From ad3e86b146fc323c3e78fa268a7413a32ee3c91b Mon Sep 17 00:00:00 2001 From: Matthieu Baumann Date: Tue, 16 Jul 2024 22:48:22 +0200 Subject: [PATCH] add format change ui, send a hips_removed event when a layer changes --- examples/al-cat-proper-motion.html | 2 +- examples/al-init-custom-options.html | 1 + src/css/aladin.css | 11 +- src/js/Aladin.js | 27 +- src/js/View.js | 5 +- src/js/gui/Box/HiPSSettingsBox.js | 392 +++++++++++++-------------- src/js/gui/Box/ServiceQueryBox.js | 7 +- src/js/gui/Widgets/ActionButton.js | 13 - src/js/gui/Widgets/Form.js | 5 +- src/js/gui/Widgets/Input.js | 23 +- src/js/gui/Widgets/Radio.js | 96 +++++++ src/js/gui/Widgets/Selector.js | 8 +- src/js/shapes/Vector.js | 4 +- 13 files changed, 313 insertions(+), 281 deletions(-) create mode 100644 src/js/gui/Widgets/Radio.js diff --git a/examples/al-cat-proper-motion.html b/examples/al-cat-proper-motion.html index bd129b26..c6a1f830 100644 --- a/examples/al-cat-proper-motion.html +++ b/examples/al-cat-proper-motion.html @@ -62,7 +62,7 @@ s.dec, s.ra + dra, s.dec + ddec, - {lineWidth: 3, color} + {color} ) } }); diff --git a/examples/al-init-custom-options.html b/examples/al-init-custom-options.html index ab7d0f68..e6aa8070 100644 --- a/examples/al-init-custom-options.html +++ b/examples/al-init-custom-options.html @@ -25,6 +25,7 @@ showZoomControl:true, showSettingsControl:true, showCooGrid: true, + fullScreen: true, } ); diff --git a/src/css/aladin.css b/src/css/aladin.css index fca00590..8e790c4e 100644 --- a/src/css/aladin.css +++ b/src/css/aladin.css @@ -901,10 +901,9 @@ canvas { .aladin-input-range { background: transparent; cursor: pointer; - width: 5em; - height: 0.1rem; - margin:0; - margin: 1rem 0; + margin: 0.8rem 0; + box-sizing: border-box; + width: 5rem; } @@ -1119,10 +1118,6 @@ canvas { width: 17rem; } -.aladin-HiPS-filter-box { - border: 1px solid white; -} - .aladin-HiPS-browser-box .aladin-input-text { width: 300px; padding: 0.5rem; diff --git a/src/js/Aladin.js b/src/js/Aladin.js index 8dc4812c..51b04137 100644 --- a/src/js/Aladin.js +++ b/src/js/Aladin.js @@ -380,8 +380,6 @@ export let Aladin = (function () { } } - this._setupUI(options); - if (options.survey) { if (Array.isArray(options.survey)) { let i = 0; @@ -496,27 +494,7 @@ export let Aladin = (function () { } }; - /*let IDs = hipsList.map((h) => { - if (h instanceof Object) { - return h.id; - } else { - return h; - } - }); - - MocServer.getHiPSesFromIDs(IDs).then((HiPSes) => { - hipsList = []; - HiPSes.forEach((h) => { - hipsList.push({ - id: h.ID, - name: h.obs_title, - regime: h.obs_regime, - - }) - }); - - fillHiPSCache(); - });*/ + fillHiPSCache(); this.view.showCatalog(options.showCatalog); @@ -572,6 +550,9 @@ export let Aladin = (function () { if (options.northPoleOrientation) { this.setViewCenter2NorthPoleAngle(options.northPoleOrientation); } + + this._setupUI(options); + }; Aladin.prototype._setupUI = function (options) { diff --git a/src/js/View.js b/src/js/View.js index 5f33b833..dcf80f77 100644 --- a/src/js/View.js +++ b/src/js/View.js @@ -1675,6 +1675,9 @@ export let View = (function () { // it exists let alreadyPresentImageLayer = this.imageLayers.get(layerName); alreadyPresentImageLayer.added = false; + + // Notify that this image layer has been replaced by the wasm part + ALEvent.HIPS_LAYER_REMOVED.dispatchedTo(this.aladinDiv, { layer: alreadyPresentImageLayer }); } imageLayer.added = true; @@ -1840,7 +1843,7 @@ export let View = (function () { this.selectLayer(this.overlayLayers[this.overlayLayers.length - 1]); } - ALEvent.HIPS_LAYER_REMOVED.dispatchedTo(this.aladinDiv, { layer }); + ALEvent.HIPS_LAYER_REMOVED.dispatchedTo(this.aladinDiv, { layer: imageLayer }); // check if there are no more surveys const noMoreLayersToWaitFor = this.promises.length === 0; diff --git a/src/js/gui/Box/HiPSSettingsBox.js b/src/js/gui/Box/HiPSSettingsBox.js index 7b93bc4e..96ee0c6b 100644 --- a/src/js/gui/Box/HiPSSettingsBox.js +++ b/src/js/gui/Box/HiPSSettingsBox.js @@ -27,7 +27,7 @@ * Author: Matthieu Baumann [CDS, matthieu.baumann@astro.unistra.fr] * *****************************************************************************/ - +import { Form } from "../Widgets/Form.js"; import { ColorCfg } from "../../ColorCfg.js"; import { Box } from "../Widgets/Box.js"; import { ALEvent } from "../../events/ALEvent.js"; @@ -35,37 +35,24 @@ import { ColorCfg } from "../../ColorCfg.js"; import luminosityIconUrl from '../../../../assets/icons/brightness.svg'; import colorIconUrl from '../../../../assets/icons/color.svg'; import pixelHistIconUrl from '../../../../assets/icons/pixel_histogram.svg'; - import { SelectorButton } from "../Widgets/Selector"; + import { RadioButton } from "../Widgets/Radio.js"; import { Layout } from "../Layout.js"; - import { Input } from "../Widgets/Input.js"; export class HiPSSettingsBox extends Box { // Constructor constructor(aladin, options) { - super({ - cssStyle: { - backgroundColor: '#333333' - }, - close: false, - ...options - }, - aladin.aladinDiv - ) - - this.aladin = aladin; - - let self = this; - this.selector = new SelectorButton({ + let self; + let selector = new RadioButton({ luminosity: { icon: { size: 'small', monochrome: true, url: luminosityIconUrl }, - tooltip: {content: 'Luminosity sliders', position: {direction: 'right'}}, - change(e) { - const content = Layout.horizontal({ + tooltip: {content: 'Luminosity sliders', position: {direction: 'bottom'}}, + action: (e) => { + const content = Layout.vertical({ layout: [self.selector, self.luminositySettingsContent] }); self.update({content}) @@ -77,9 +64,9 @@ import { ColorCfg } from "../../ColorCfg.js"; monochrome: true, url: opacityIconUrl }, - tooltip: {content: 'Opacity slider', position: {direction: 'right'}}, - change(e) { - const content = Layout.horizontal({layout: [self.selector, self.opacitySettingsContent]}); + tooltip: {content: 'Opacity slider', position: {direction: 'bottom'}}, + action: (e) => { + const content = Layout.vertical({layout: [self.selector, self.opacitySettingsContent]}); self.update({content}) } }, @@ -88,9 +75,9 @@ import { ColorCfg } from "../../ColorCfg.js"; size: 'small', url: colorIconUrl }, - tooltip: {content: 'Colormap', position: {direction: 'right'}}, - change(e) { - const content = Layout.horizontal({layout: [self.selector, self.colorSettingsContent]}); + tooltip: {content: 'Colormap', position: {direction: 'bottom'}}, + action: (e) => { + const content = Layout.vertical({layout: [self.selector, self.colorSettingsContent]}); self.update({content}) } }, @@ -100,208 +87,207 @@ import { ColorCfg } from "../../ColorCfg.js"; monochrome: true, url: pixelHistIconUrl }, - tooltip: {content: 'Pixel cutouts', position: {direction: 'right'}}, - change(e) { - const content = Layout.horizontal({layout: [self.selector, self.pixelSettingsContent]}); + tooltip: {content: 'Pixel cutouts', position: {direction: 'bottom'}}, + action: (e) => { + const content = Layout.vertical({layout: [self.selector, self.pixelSettingsContent]}); self.update({content}) } }, selected: 'opacity' }, aladin); + // Define the contents - - // content - this.minCutInput = Input.number({ - cssStyle: { - padding: '0', - width: '8ex', - 'font-family': 'monospace', - }, - tooltip: {content: 'Min cut', position: {direction: 'bottom'}}, - name: 'mincut', - change(e) { - let layer = self.options.layer; - layer.setCuts(+e.target.value, layer.getColorCfg().getCuts()[1]) - } + let opacitySettingsContent = new Form({ + subInputs: [ + { + label: 'opacity:', + name: 'opacity', + tooltip: {content: 1.0, position: {direction: 'bottom'}}, + name: 'opacitySlider', + type: 'range', + min: 0.0, + max: 1.0, + value: 1.0, + change: (e, slider) => { + const opacity = +e.target.value; + self.options.layer.setOpacity(opacity) + slider.update({value: opacity, tooltip: {content: opacity.toFixed(2), position: {direction: 'bottom'}}}) + } + } + ] }) - this.maxCutInput = Input.number({ - cssStyle: { - padding: '0', - width: '8ex', - 'font-family': 'monospace', + let luminositySettingsContent = new Form({ + subInputs: [ + { + label: 'brightness:', + tooltip: {content: 'brightness', position: {direction: 'right'}}, + name: 'brightness', + type: 'range', + min: -1, + max: 1, + ticks: [0.0], + value: 0.0, + change: (e, slider) => { + const brightness = +e.target.value; + self.options.layer.setBrightness(brightness) + slider.update({value: brightness, tooltip: {content: `${brightness.toFixed(3)}`, position: {direction: 'right'}}}) + } + }, + { + label: 'saturation:', + tooltip: {content: 'saturation', position: {direction: 'right'}}, + name: 'saturation', + type: 'range', + min: -1, + max: 1, + ticks: [0.0], + value: 0.0, + change: (e, slider) => { + const saturation = +e.target.value + self.options.layer.setSaturation(saturation) + slider.update({value: saturation, tooltip: {content: `${saturation.toFixed(3)}`, position: {direction: 'right'}}}) + } + }, + { + label: 'contrast:', + tooltip: {content: 'contrast', position: {direction: 'right'}}, + name: 'contrast', + type: 'range', + min: -1, + max: 1, + ticks: [0.0], + value: 0.0, + change: (e, slider) => { + const contrast = +e.target.value + self.options.layer.setContrast(contrast) + slider.update({value: contrast, tooltip: {content: `${contrast.toFixed(3)}`, position: {direction: 'right'}}}) + } + }, + ] + }); + let pixelSettingsContent = new Form({ + type: 'group', + subInputs: [{ + label: 'format:', + type: 'select', + name: 'fmt', + value: 'jpeg', + options: ['jpeg'], + change(e) { + if (self.options.layer.imgFormat !== e.target.value) + self.options.layer.setImageFormat(e.target.value); + }, + tooltip: {content: 'Formats availables', position: {direction: 'bottom'}} }, - tooltip: {content: 'Max cut', position: {direction: 'bottom'}}, - name: 'maxcut', - change(e) { - let layer = self.options.layer; - layer.setCuts(layer.getColorCfg().getCuts()[0], +e.target.value) - } - }) - self.stretchSelector = Input.select({ - name: 'stretch', - value: self.options.layer && self.options.layer.getColorCfg().stretch || 'linear', - options: ['sqrt', 'linear', 'asinh', 'pow2', 'log'], - change() { - let layer = self.options.layer; - layer.setColormap(layer.getColorCfg().getColormap(), {stretch: this.value}); + { + label: 'stretch:', + type: 'select', + name: 'stretch', + value: 'linear', + options: ['sqrt', 'linear', 'asinh', 'pow2', 'log'], + change(e) { + self.options.layer.setColormap(self.options.layer.getColorCfg().getColormap(), {stretch: e.target.value}); + }, + tooltip: {content: 'stretch function', position: {direction: 'bottom'}} }, - tooltip: {content: 'stretch function', position: {direction: 'right'}} + { + label: 'min cut:', + type: 'number', + cssStyle: { + width: '6rem', + }, + tooltip: {content: 'Min cut', position: {direction: 'bottom'}}, + name: 'mincut', + value: 0.0, + change: (e) => { + self.options.layer.setCuts(+e.target.value, self.options.layer.getColorCfg().getCuts()[1]) + } + }, + { + type: 'number', + label: 'max cut:', + cssStyle: { + width: '6rem', + }, + tooltip: {content: 'Max cut', position: {direction: 'bottom'}}, + name: 'maxcut', + value: 1.0, + change: (e) => { + self.options.layer.setCuts(self.options.layer.getColorCfg().getCuts()[0], +e.target.value) + } + }] + }); + + let colorSettingsContent = new Form({ + subInputs: [{ + label: 'colormap:', + type: 'select', + name: 'cmap', + value: 'native', + options: ColorCfg.COLORMAPS, + change: (e, cmapSelector) => { + self.options.layer.setColormap(e.target.value) + //cmapSelector.update({value: e.target.value}) + }, + }] }); - self._addListeners() + super({ + close: false, + ...options, + content: Layout.vertical([selector, opacitySettingsContent]), + }, + aladin.aladinDiv) + self = this; + + this.aladin = aladin; + this._addListeners() + + this.selector = selector; + this.opacitySettingsContent = opacitySettingsContent; + this.colorSettingsContent = colorSettingsContent; + this.pixelSettingsContent = pixelSettingsContent; + this.luminositySettingsContent = luminositySettingsContent; } update(options) { - let self = this; - if (options && options.layer) { - let layer = options.layer; - // Define the contents + if (options.layer) { + let hips = options.layer; + let colorCfg = hips.getColorCfg(); + let stretch = colorCfg.stretch; + let colormap = colorCfg.getColormap(); - let layerOpacity = layer.getOpacity() + let [minCut, maxCut] = colorCfg.getCuts(); + this.pixelSettingsContent.set('mincut', +minCut.toFixed(4)) + this.pixelSettingsContent.set('maxcut', +maxCut.toFixed(4)) + this.pixelSettingsContent.set('stretch', stretch) + let fmtInput = this.pixelSettingsContent.getInput('fmt') - self.opacitySettingsContent = Input.slider({ - tooltip: {content: layerOpacity, position: {direction: 'bottom'}}, - name: 'opacitySlider', - type: 'range', - min: 0.0, - max: 1.0, - value: layerOpacity, - change(e, slider) { - const opacity = +e.target.value; - layer.setOpacity(opacity) - slider.update({value: opacity, tooltip: {content: opacity.toFixed(2), position: {direction: 'bottom'}}}) - } - }) + fmtInput.innerHTML = ''; + for (const option of hips.formats) { + fmtInput.innerHTML += ""; + } + fmtInput.value = hips.imgFormat; - let brightness = layer.getColorCfg().getBrightness() - let saturation = layer.getColorCfg().getSaturation() - let contrast = layer.getColorCfg().getContrast() - self.luminositySettingsContent = Layout.vertical({ - layout: [ - Input.slider({ - tooltip: {content: 'brightness', position: {direction: 'right'}}, - name: 'brightness', - type: 'range', - min: -1, - max: 1, - ticks: [0.0], - value: brightness, - change(e, slider) { - const brightness = +e.target.value; - layer.setBrightness(brightness) - slider.update({value: brightness, tooltip: {content: `brightness: ${brightness.toFixed(3)}`, position: {direction: 'right'}}}) - } - }), - Input.slider({ - tooltip: {content: 'saturation', position: {direction: 'right'}}, - name: 'saturation', - type: 'range', - min: -1, - max: 1, - ticks: [0.0], - value: saturation, - change(e, slider) { - const saturation = +e.target.value - layer.setSaturation(saturation) - slider.update({value: saturation, tooltip: {content: `saturation: ${saturation.toFixed(3)}`, position: {direction: 'right'}}}) - } - }), - Input.slider({ - tooltip: {content: 'contrast', position: {direction: 'right'}}, - name: 'contrast', - type: 'range', - min: -1, - max: 1, - ticks: [0.0], - value: contrast, - change(e, slider) { - const contrast = +e.target.value - layer.setContrast(contrast) - slider.update({value: contrast, tooltip: {content: `contrast: ${contrast.toFixed(3)}`, position: {direction: 'right'}}}) - } - }), - ] - }); - const [minCut, maxCut] = layer.getColorCfg().getCuts(); - self.minCutInput.set(minCut); - self.maxCutInput.set(maxCut) - self.stretchSelector.update({value: layer.getColorCfg().stretch}) - - self.pixelSettingsContent = Layout.horizontal({ - layout: [ - self.stretchSelector, - self.minCutInput, - self.maxCutInput - ] - }); - - let cmap = layer.getColorCfg().getColormap(); - - this.colorSettingsContent = Input.select({ - name: 'colormap', - value: cmap, - options: ColorCfg.COLORMAPS, - change() { - let colormap = this.value; - layer.setColormap(colormap) - }, - }); - - //this.colorSettingsContent = new CmapSelector(optionsCmapSelector, this.aladin); - let content = (() => { - let selected = self.selector.options.selected; - switch (selected) { - case 'colors': - return self.colorSettingsContent; - case 'pixel': - return self.pixelSettingsContent; - case 'opacity': - return self.opacitySettingsContent; - case 'luminosity': - return self.luminositySettingsContent; - default: - return self.opacitySettingsContent; - } - })(); - options.content = Layout.horizontal({layout: [self.selector, content]}); + this.colorSettingsContent.set('cmap', colormap) + this.opacitySettingsContent.set('opacity', hips.getOpacity()) } super.update(options) } _show(options) { - this._hide(); - - if (this.selector) { + /*if (this.selector) { this.selector._show(); - } - - if (this.stretchSelector) { - this.stretchSelector._show(); - } - - if (this.colorSettingsContent) { - this.colorSettingsContent._show(); - } + }*/ super._show(options) } _hide() { - if (this.colorSettingsContent) { - this.colorSettingsContent._hide(); - } - - if (this.stretchSelector) { - this.stretchSelector._hide(); - } - - if (this.selector) { - this.selector._hide(); - } super._hide() } @@ -312,17 +298,15 @@ import { ColorCfg } from "../../ColorCfg.js"; let selectedLayer = this.options.layer; if (selectedLayer && hips.layer === selectedLayer.layer) { let colorCfg = hips.getColorCfg(); - - let cmap = colorCfg.getColormap(); - let reversed = colorCfg.getReversed(); let stretch = colorCfg.stretch; + let colormap = colorCfg.getColormap(); let [minCut, maxCut] = colorCfg.getCuts(); - this.minCutInput.set(+minCut.toFixed(2)); - this.maxCutInput.set(+maxCut.toFixed(2)); - this.stretchSelector.update({value: stretch}); - - this.opacitySettingsContent.set(hips.getOpacity()) + this.pixelSettingsContent.set('mincut', +minCut.toFixed(4)) + this.pixelSettingsContent.set('maxcut', +maxCut.toFixed(4)) + this.pixelSettingsContent.set('stretch', stretch) + this.colorSettingsContent.set('cmap', colormap) + this.opacitySettingsContent.set('opacity', hips.getOpacity()) } }); } diff --git a/src/js/gui/Box/ServiceQueryBox.js b/src/js/gui/Box/ServiceQueryBox.js index 7a819791..3bce6ad7 100644 --- a/src/js/gui/Box/ServiceQueryBox.js +++ b/src/js/gui/Box/ServiceQueryBox.js @@ -45,6 +45,7 @@ export class ServiceQueryBox extends Box { // Define the form once for all let form = new Form({ submit: { + content: 'Query', action(params) { // Construct the SODA url let url = new URL(self.service.baseUrl) @@ -65,12 +66,6 @@ export class ServiceQueryBox extends Box { url.searchParams.append('ID', params['ID']); } - /*let loadingBtn = ActionButton.create( - ActionButton.DEFAULT_BTN["loading"], - 'Waiting to get the image response...', - submitFormDiv - );*/ - let name = url.searchParams.toString(); // Tackle CORS problems Utils.loadFromUrls([url, Utils.handleCORSNotSameOrigin(url)], {timeout: 30000, dataType: 'blob'}) diff --git a/src/js/gui/Widgets/ActionButton.js b/src/js/gui/Widgets/ActionButton.js index edd4bb29..01583772 100644 --- a/src/js/gui/Widgets/ActionButton.js +++ b/src/js/gui/Widgets/ActionButton.js @@ -188,17 +188,4 @@ export class ActionButton extends DOMElement { return new ActionButton(opt, target, position); } - - static DEFAULT_BTN = { - 'loading': { - content: '⏳', - width: '28px', - height: '28px', - cssStyle: { - backgroundColor: '#bababa', - borderColor: '#484848', - }, - action(e) {} - }, - } } diff --git a/src/js/gui/Widgets/Form.js b/src/js/gui/Widgets/Form.js index 2d2db165..9939ffd7 100644 --- a/src/js/gui/Widgets/Form.js +++ b/src/js/gui/Widgets/Form.js @@ -70,9 +70,10 @@ export class Form extends DOMElement { let layout = []; if (this.options && this.options.subInputs) { - this.options.subInputs.forEach(subInput => { + /*this.options.subInputs.forEach(subInput => { layout.push(this._createInput(subInput)) - }); + });*/ + layout.push(this._createInput(this.options)) } let self = this; diff --git a/src/js/gui/Widgets/Input.js b/src/js/gui/Widgets/Input.js index d8f1277c..80c11c0c 100644 --- a/src/js/gui/Widgets/Input.js +++ b/src/js/gui/Widgets/Input.js @@ -96,7 +96,10 @@ export class Input extends DOMElement { if (this.options.change) { this.el.removeEventListener('change', this.action); - this.action = this.options.change; + this.action = (e) => { + this.options.change(e, this); + }; + this.el.addEventListener('change', this.action); } } else { @@ -168,25 +171,15 @@ export class Input extends DOMElement { let stretch = this.options.stretch || 'linear'; if (stretch === 'log') { // Refers to this StackOverflow post: https://stackoverflow.com/questions/846221/logarithmic-slider - - - if (this.options.ticks) { this.options.ticks = this.options.ticks.map((t) => logPosition(t)); } - - if (this.options.change) { let change = this.options.change; this.options.change = (e) => { const value = logSlider(e.target.value) - /*let p = 100 * (e.target.value - this.options.min) / (this.options.max - this.options.min); - if (this.options.reversed === true) { - p = 100 - p; - } - this.el.style.background = 'linear-gradient(to right, lightgreen ' + p + '%, #bababa ' + p + '%)'; - */ + change(e, this, value); }; } @@ -343,12 +336,6 @@ export class Input extends DOMElement { this.element().classList.add(this.options.classList) } - /*// Add padding for inputs except color ones - if (Utils.hasTouchScreen() && this.options.type !== "color") { - // Add a little padding - this.el.style.padding = "0.5em"; - }*/ - super._show() } diff --git a/src/js/gui/Widgets/Radio.js b/src/js/gui/Widgets/Radio.js new file mode 100644 index 00000000..5cc670dc --- /dev/null +++ b/src/js/gui/Widgets/Radio.js @@ -0,0 +1,96 @@ +// Copyright 2023 - UDS/CNRS +// The Aladin Lite program is distributed under the terms +// of the GNU General Public License version 3. +// +// This file is part of Aladin Lite. +// +// Aladin Lite is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 3 of the License. +// +// Aladin Lite is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// The GNU General Public License is available in COPYING file +// along with Aladin Lite. +// + +import { DOMElement } from "./Widget"; +import { ActionButton } from "./ActionButton"; +import { Layout } from "../Layout"; + +/****************************************************************************** + * Aladin Lite project + * + * File gui/Form.js + * + * A context menu that shows when the user right clicks, or long touch on touch device + * + * + * Author: Matthieu Baumann[CDS] + * + *****************************************************************************/ + +/* +Exemple of layout object +{ + { + label: "ID", + type: "text", + value: "the placeholder value...", + }, +*/ + +/* +options = {id: (btn option), id2: btn option, selected: id} +*/ +export class RadioButton extends DOMElement { + /** + * Create a layout + * @param {{layout: {type: String, name: String, value: Number | String, placeholder: Number | String, change: Function } | {type: String, name: String, checked: Boolean, change: Function } | { type: String, name: String, value: String, options: Array., change: Function }, cssStyle: Object}} options - Represents the structure of the Tabs + * @param {DOMElement} target - The parent element. + * @param {String} position - The position of the tabs layout relative to the target. + * For the list of possibilities, see https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentHTML + */ + constructor(options, aladin, target, position = 'beforeend') { + let layout = []; + // toggle on the selected button + const toggled = options.selected; + + for (var key in options) { + if (key !== 'selected') { + let btnOptions = options[key]; + if (key === toggled) { + btnOptions.toggled = true; + } else { + btnOptions.toggled = false; + } + + let action = btnOptions.action; + + let btn = new ActionButton({ + ...btnOptions, + action(e) { + action(e); + + for (var otherBtn of layout) { + otherBtn.update({toggled: false}); + } + + btn.update({toggled: true}); + } + }) + + layout.push(btn); + } + } + + let el = Layout.horizontal({layout}); + + super(el, options) + + this.aladin = aladin; + } +} diff --git a/src/js/gui/Widgets/Selector.js b/src/js/gui/Widgets/Selector.js index c9aa76bd..ecc1908d 100644 --- a/src/js/gui/Widgets/Selector.js +++ b/src/js/gui/Widgets/Selector.js @@ -21,6 +21,7 @@ import { DOMElement } from "./Widget"; import { FSM } from "../../FiniteStateMachine"; import { ActionButton } from "./ActionButton"; import { ContextMenu } from "./ContextMenu"; +import { Layout } from "../Layout"; /****************************************************************************** * Aladin Lite project @@ -88,6 +89,9 @@ export class SelectorButton extends DOMElement { self._show(); self.fsm.dispatch('closeCtxMenu') + }, + cssStyle: { + padding: "0", } }) } @@ -96,9 +100,6 @@ export class SelectorButton extends DOMElement { position: { nextTo: this.el, direction: 'bottom', - }, - cssStyle: { - padding: '0', } }) }; @@ -137,6 +138,7 @@ export class SelectorButton extends DOMElement { const selectedId = this.options.selected; let {target, position} = this.remove(); + this.el = new ActionButton({ ...this.options[selectedId], action: (e) => { diff --git a/src/js/shapes/Vector.js b/src/js/shapes/Vector.js index a223624f..4cb68834 100644 --- a/src/js/shapes/Vector.js +++ b/src/js/shapes/Vector.js @@ -116,7 +116,7 @@ export let Vector = (function() { } let baseColor = this.color || (this.overlay && this.overlay.color) || '#ff0000'; - let lineWidth = this.lineWidth || this.overlay.lineWidth || 3; + let lineWidth = this.lineWidth || (this.overlay && this.overlay.lineWidth) || 2; // too small if(!noSmallCheck) { @@ -144,7 +144,7 @@ export let Vector = (function() { if (this.arrow) { // draw the arrow var angle, x, y, xh, yh; - var arrowRad = this.lineWidth * 3; + var arrowRad = lineWidth * 3; angle = Math.atan2(v2[1] - v1[1], v2[0] - v1[0]) xh = v2[0];