@tradecanvas/chart 2.0.0 → 2.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{LayoutSession-DUkQpWp6.cjs → LayoutSession-CwpR5ScW.cjs} +2 -2
- package/dist/{LayoutSession-DUkQpWp6.cjs.map → LayoutSession-CwpR5ScW.cjs.map} +1 -1
- package/dist/{LayoutSession-QZPqjaRt.js → LayoutSession-fJQLPSQ8.js} +2 -2
- package/dist/{LayoutSession-QZPqjaRt.js.map → LayoutSession-fJQLPSQ8.js.map} +1 -1
- package/dist/{WidgetCommandPalette-CdqVGBYg.cjs → WidgetCommandPalette-CASDu1gu.cjs} +2 -2
- package/dist/{WidgetCommandPalette-CdqVGBYg.cjs.map → WidgetCommandPalette-CASDu1gu.cjs.map} +1 -1
- package/dist/{WidgetCommandPalette-BRVQZRQe.js → WidgetCommandPalette-DpP-lG1A.js} +2 -2
- package/dist/{WidgetCommandPalette-BRVQZRQe.js.map → WidgetCommandPalette-DpP-lG1A.js.map} +1 -1
- package/dist/{WidgetDrawingSettings-CFksb9-G.cjs → WidgetDrawingSettings-BpX4Cdys.cjs} +2 -2
- package/dist/{WidgetDrawingSettings-CFksb9-G.cjs.map → WidgetDrawingSettings-BpX4Cdys.cjs.map} +1 -1
- package/dist/{WidgetDrawingSettings-DBBFUUg7.js → WidgetDrawingSettings-Cwds8CdJ.js} +2 -2
- package/dist/{WidgetDrawingSettings-DBBFUUg7.js.map → WidgetDrawingSettings-Cwds8CdJ.js.map} +1 -1
- package/dist/{WidgetIndicatorPicker-DiB_819o.cjs → WidgetIndicatorPicker-CTK-2QvV.cjs} +2 -2
- package/dist/{WidgetIndicatorPicker-DiB_819o.cjs.map → WidgetIndicatorPicker-CTK-2QvV.cjs.map} +1 -1
- package/dist/{WidgetIndicatorPicker-CcIYSWPd.js → WidgetIndicatorPicker-DsFCT2xZ.js} +2 -2
- package/dist/{WidgetIndicatorPicker-CcIYSWPd.js.map → WidgetIndicatorPicker-DsFCT2xZ.js.map} +1 -1
- package/dist/{WidgetIndicatorSettings-DcRbZpJJ.js → WidgetIndicatorSettings-D3W40Nud.js} +3 -3
- package/dist/{WidgetIndicatorSettings-DcRbZpJJ.js.map → WidgetIndicatorSettings-D3W40Nud.js.map} +1 -1
- package/dist/{WidgetIndicatorSettings-GnMFEumO.cjs → WidgetIndicatorSettings-DRt-E8Y_.cjs} +2 -2
- package/dist/{WidgetIndicatorSettings-GnMFEumO.cjs.map → WidgetIndicatorSettings-DRt-E8Y_.cjs.map} +1 -1
- package/dist/{WidgetSettings-Ds8LIQJL.js → WidgetSettings-BsjEg-Dj.js} +2 -2
- package/dist/{WidgetSettings-Ds8LIQJL.js.map → WidgetSettings-BsjEg-Dj.js.map} +1 -1
- package/dist/{WidgetSettings-BnXD_NEb.cjs → WidgetSettings-D0fnEBqC.cjs} +2 -2
- package/dist/{WidgetSettings-BnXD_NEb.cjs.map → WidgetSettings-D0fnEBqC.cjs.map} +1 -1
- package/dist/{WidgetSymbolSearch-CB27E553.cjs → WidgetSymbolSearch-CTfNYC_m.cjs} +2 -2
- package/dist/{WidgetSymbolSearch-CB27E553.cjs.map → WidgetSymbolSearch-CTfNYC_m.cjs.map} +1 -1
- package/dist/{WidgetSymbolSearch-W9ZoSxTR.js → WidgetSymbolSearch-Cxxq4SrK.js} +2 -2
- package/dist/{WidgetSymbolSearch-W9ZoSxTR.js.map → WidgetSymbolSearch-Cxxq4SrK.js.map} +1 -1
- package/dist/{i18n-BudCuwZD.js → i18n-COYPJUtF.js} +33 -33
- package/dist/i18n-COYPJUtF.js.map +1 -0
- package/dist/i18n-Q4mxL2mj.cjs +2 -0
- package/dist/i18n-Q4mxL2mj.cjs.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/{indicatorInputWords-BfiSCSB-.js → indicatorInputWords-DFmWy8Ah.js} +2 -2
- package/dist/{indicatorInputWords-BfiSCSB-.js.map → indicatorInputWords-DFmWy8Ah.js.map} +1 -1
- package/dist/{indicatorInputWords-CdAL8a73.cjs → indicatorInputWords-hVKLYuVB.cjs} +2 -2
- package/dist/{indicatorInputWords-CdAL8a73.cjs.map → indicatorInputWords-hVKLYuVB.cjs.map} +1 -1
- package/dist/widget/WidgetDrawingEdit.d.ts.map +1 -1
- package/dist/widget/WidgetInlineText.d.ts +2 -0
- package/dist/widget/WidgetInlineText.d.ts.map +1 -1
- package/dist/widget/WidgetSelectionToolbar.d.ts +7 -1
- package/dist/widget/WidgetSelectionToolbar.d.ts.map +1 -1
- package/dist/widget/index.cjs +3 -3
- package/dist/widget/index.cjs.map +1 -1
- package/dist/widget/index.js +520 -519
- package/dist/widget/index.js.map +1 -1
- package/dist/widget/locales/index.cjs +1 -1
- package/dist/widget/locales/index.js +1 -1
- package/package.json +3 -3
- package/dist/i18n-BudCuwZD.js.map +0 -1
- package/dist/i18n-D8MhJwUv.cjs +0 -2
- package/dist/i18n-D8MhJwUv.cjs.map +0 -1
package/dist/{WidgetIndicatorSettings-DcRbZpJJ.js.map → WidgetIndicatorSettings-D3W40Nud.js.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WidgetIndicatorSettings-DcRbZpJJ.js","names":[],"sources":["../src/widget/WidgetIndicatorSettings.ts"],"sourcesContent":["import type { IndicatorInputSpec, IndicatorIntervals, IndicatorPlot, IndicatorPlotStyle, IndicatorStyleConfig } from '@tradecanvas/commons';\nimport { PRICE_SOURCES, parseIndicatorSource } from '@tradecanvas/commons';\nimport { describeInputs, isSeriesPalette, type IndicatorInputInfo } from '@tradecanvas/core';\nimport { FOCUSABLE, keepTabInside } from './focusTrap.js';\nimport { createIcon } from './icons.js';\nimport { EN_MESSAGES, type Translator } from './i18n.js';\nimport { WidgetContextMenu } from './WidgetContextMenu.js';\nimport { colorInput, numberInput, settingsRow, toggleSwitch } from './settingsControls.js';\nimport { renderStyleTab, renderVisibilityTab } from './indicatorSettingsTabs.js';\nimport { choiceLabel, inputLabel } from './indicatorInputWords.js';\n\nexport type IndicatorParamValue = number | string | boolean;\n\nexport interface IndicatorSettingsTarget {\n instanceId: string;\n /** The kind of indicator (`'rsi'`): what \"Save as default\" is for. */\n indicatorId: string;\n name: string;\n /** Default config — defines the editable keys and their types. */\n defaults: Record<string, unknown>;\n /** Current values, overlaid on defaults. */\n params: Record<string, unknown>;\n /** How parameters are edited (the descriptor's `inputs`). */\n inputs?: Readonly<Record<string, IndicatorInputSpec>>;\n /** Other indicators' lines a source can be: value `ind:<instanceId>:<key>`. */\n lineSources?: readonly { value: string; label: string }[];\n /** What it draws; with `colors`, the lines of the Style tab. */\n plots?: readonly IndicatorPlot[];\n colors?: readonly string[];\n lineWidth?: number;\n /** Each line's own dash and visibility, by its key. */\n plotStyles?: Readonly<Record<string, IndicatorPlotStyle>>;\n /** Its levels and its indicator's defaults; with them, the levels under the lines. */\n levels?: readonly number[];\n defaultLevels?: readonly number[];\n /** Whether `levels` are the instance's own; not: its indicator's defaults, which Cancel leaves it on. */\n ownLevels?: boolean;\n /** The price scale an overlay is on; set only for price-pane overlays. */\n scale?: 'right' | 'left';\n /** The intervals it shows on; unset or null: all of them. */\n intervals?: IndicatorIntervals | null;\n}\n\n/** What \"Save as default\" keeps: new indicators of the kind start with it. */\nexport interface IndicatorDefault {\n params: Record<string, IndicatorParamValue>;\n style: IndicatorStyleConfig;\n}\n\nexport interface IndicatorSettingsCallbacks {\n onApply: (instanceId: string, params: Record<string, IndicatorParamValue>) => void;\n onStyle?: (instanceId: string, style: IndicatorStyleConfig) => void;\n /** `null` restores the indicator's default levels. */\n onLevels?: (instanceId: string, levels: number[] | null) => void;\n /** Move an overlay to the left price scale or back to the price scale. */\n onScale?: (instanceId: string, scale: 'right' | 'left') => void;\n /** The intervals it shows on (`null`: all); with it, the Visibility tab. */\n onIntervals?: (instanceId: string, intervals: IndicatorIntervals | null) => void;\n /** \"Save as default\" in the Defaults menu. */\n onSaveDefault?: (indicatorId: string, saved: IndicatorDefault) => void;\n onClose: () => void;\n}\n\ntype Tab = 'inputs' | 'style' | 'visibility';\n\n/** What the dialog changes besides the inputs: as it was on opening, and as it is. */\ninterface Look {\n colors: string[];\n lineWidth: number;\n plots: Record<string, IndicatorPlotStyle>;\n levels: number[];\n scale?: 'right' | 'left';\n intervals: IndicatorIntervals | null;\n}\n\n/** Ids for the dialog's labelling (several widgets may share a page). */\nlet nextDialogId = 1;\n\nconst DEFAULT_LINE_WIDTH = 1.5;\n/** A line as it is with no style of its own: shown, and dashed as its indicator declares it. */\nfunction plainPlot(plots: readonly IndicatorPlot[] | undefined, key: string): IndicatorPlotStyle {\n return { visible: true, lineStyle: plots?.find((p) => p.key === key)?.lineStyle ?? 'solid' };\n}\n\nconst same = (a: unknown, b: unknown) => JSON.stringify(a) === JSON.stringify(b);\n/** A copy of plain values (numbers, strings, flags, lists of them). */\nconst copy = <T>(value: T): T => JSON.parse(JSON.stringify(value)) as T;\n\nfunction titleCase(key: string): string {\n // camelCase / snake_case → \"Title Case\"\n return key\n .replace(/_/g, ' ')\n .replace(/([a-z0-9])([A-Z])/g, '$1 $2')\n .replace(/^./, (c) => c.toUpperCase());\n}\n\n/** Each line put back as `to` has it; one `to` has no style for goes back to plain. */\nfunction plotsBack(\n from: Record<string, IndicatorPlotStyle>,\n to: Record<string, IndicatorPlotStyle>,\n plots: readonly IndicatorPlot[] | undefined,\n): Record<string, IndicatorPlotStyle> {\n return Object.fromEntries([...new Set([...Object.keys(from), ...Object.keys(to)])].map((key) => [key, { ...plainPlot(plots, key), ...to[key] }]));\n}\n\n/**\n * Per-indicator settings: its inputs; its style (each line shown or not, its\n * colours and dash, the line width, the scale, the levels); and the\n * intervals it shows on. Every edit shows at once; Cancel (or Escape) puts\n * it all back as it was, Ok keeps it. The Defaults menu resets it, or keeps\n * it for the next ones of its kind.\n */\nexport class WidgetIndicatorSettings {\n private backdrop: HTMLDivElement;\n private modal: HTMLDivElement;\n private bodyEl: HTMLDivElement;\n private titleEl: HTMLHeadingElement;\n private tabsEl: HTMLDivElement;\n private defaultsBtn: HTMLButtonElement;\n private menu: WidgetContextMenu | null = null;\n private callbacks: IndicatorSettingsCallbacks;\n private target: IndicatorSettingsTarget | null = null;\n private draft: Record<string, IndicatorParamValue> = {};\n private initial: { params: Record<string, IndicatorParamValue>; look: Look } | null = null;\n private look: Look = { colors: [], lineWidth: DEFAULT_LINE_WIDTH, plots: {}, levels: [], intervals: null };\n private tab: Tab = 'inputs';\n private tabButtons: HTMLButtonElement[] = [];\n private returnFocus: HTMLElement | null = null;\n private readonly uid = nextDialogId++;\n private readonly t: Translator;\n\n constructor(host: HTMLElement, callbacks: IndicatorSettingsCallbacks, t?: Translator) {\n this.callbacks = callbacks;\n this.t = t ?? ((key) => EN_MESSAGES[key]);\n\n this.backdrop = document.createElement('div');\n this.backdrop.className = 'tcw-modal-backdrop';\n this.backdrop.hidden = true;\n // A press that starts in the dialog (a slider dragged, text selected) and ends on the backdrop doesn't cancel it.\n let pressedBackdrop = false;\n this.backdrop.addEventListener('pointerdown', (e) => {\n pressedBackdrop = e.target === this.backdrop;\n });\n this.backdrop.addEventListener('click', (e) => {\n if (e.target === this.backdrop && pressedBackdrop) this.finish(false);\n pressedBackdrop = false;\n });\n\n this.modal = document.createElement('div');\n this.modal.className = 'tcw-modal tcw-modal-narrow tcw-indi';\n this.modal.setAttribute('role', 'dialog');\n this.modal.setAttribute('aria-modal', 'true');\n this.modal.setAttribute('aria-labelledby', `tcw-indi-title-${this.uid}`);\n // Focusable, so keys still reach it after a click on its blank parts.\n this.modal.tabIndex = -1;\n this.modal.addEventListener('keydown', (e) => this.onKeyDown(e));\n\n const header = document.createElement('div');\n header.className = 'tcw-modal-header';\n this.titleEl = document.createElement('h3');\n this.titleEl.id = `tcw-indi-title-${this.uid}`;\n const closeBtn = document.createElement('button');\n closeBtn.type = 'button';\n closeBtn.className = 'tcw-modal-close';\n closeBtn.setAttribute('aria-label', this.t('indicatorSettings.close'));\n closeBtn.innerHTML = createIcon('x', 16);\n closeBtn.addEventListener('click', () => this.finish(false));\n header.append(this.titleEl, closeBtn);\n\n this.tabsEl = document.createElement('div');\n this.tabsEl.className = 'tcw-modal-tabs';\n this.tabsEl.setAttribute('role', 'tablist');\n this.tabsEl.addEventListener('keydown', (e) => this.onTabKey(e));\n\n this.bodyEl = document.createElement('div');\n this.bodyEl.className = 'tcw-modal-body';\n this.bodyEl.id = `tcw-indi-panel-${this.uid}`;\n this.bodyEl.setAttribute('role', 'tabpanel');\n\n this.defaultsBtn = this.footerButton('defaults', 'tcw-footer-menu', () => this.openDefaults());\n this.modal.append(header, this.tabsEl, this.bodyEl, this.buildFooter());\n this.backdrop.appendChild(this.modal);\n host.appendChild(this.backdrop);\n }\n\n /** The Defaults menu on the left; Cancel and OK on the right. */\n private buildFooter(): HTMLDivElement {\n this.defaultsBtn.setAttribute('aria-haspopup', 'menu');\n this.defaultsBtn.setAttribute('aria-expanded', 'false');\n this.defaultsBtn.innerHTML = `<span></span>${createIcon('chevronDown', 12)}`;\n this.defaultsBtn.querySelector('span')!.textContent = this.t('indicatorSettings.defaults');\n const end = document.createElement('div');\n end.className = 'tcw-footer-end';\n end.append(\n this.footerButton('cancel', 'tcw-footer-cancel', () => this.finish(false), this.t('common.cancel')),\n this.footerButton('ok', 'tcw-done-btn', () => this.finish(true), this.t('drawingSettings.ok')),\n );\n const footer = document.createElement('div');\n footer.className = 'tcw-modal-footer';\n footer.append(this.defaultsBtn, end);\n return footer;\n }\n\n open(target: IndicatorSettingsTarget): void {\n if (this.target) this.finish(true);\n if (this.backdrop.hidden) this.returnFocus = document.activeElement instanceof HTMLElement ? document.activeElement : null;\n this.target = target;\n this.titleEl.textContent = this.t('indicatorSettings.title').replace('{name}', target.name);\n this.draft = {};\n this.look = {\n colors: [...(target.colors ?? [])],\n lineWidth: target.lineWidth ?? DEFAULT_LINE_WIDTH,\n plots: Object.fromEntries(Object.entries(target.plotStyles ?? {}).map(([key, plot]) => [key, { ...plot }])),\n levels: [...(target.levels ?? [])],\n scale: target.scale,\n intervals: target.intervals ?? null,\n };\n this.initial = { params: this.currentParams(), look: copy(this.look) };\n this.tab = 'inputs';\n this.renderTabs();\n this.renderBody();\n this.backdrop.hidden = false;\n // Start on the first input, else the tabs, else the dialog itself.\n const firstTab = this.tabsEl.hidden ? null : this.tabButtons[0] ?? null;\n (this.bodyEl.querySelector<HTMLElement>(FOCUSABLE) ?? firstTab ?? this.modal).focus();\n }\n\n /** Close, keeping what was changed (as Ok does). */\n close(): void {\n this.finish(true);\n }\n\n isOpen(): boolean {\n return !this.backdrop.hidden;\n }\n\n destroy(): void {\n this.menu?.destroy();\n this.backdrop.remove();\n }\n\n /** Close with Ok (`keep`: a value still being typed applies) or Cancel (everything goes back). */\n private finish(keep: boolean): void {\n if (!this.target) return;\n if (keep) {\n const active = document.activeElement;\n if ((active instanceof HTMLInputElement || active instanceof HTMLSelectElement) && this.modal.contains(active)) {\n active.dispatchEvent(new Event('change'));\n }\n } else {\n this.revert();\n }\n // No target: a field's change on its way out (its blur) no longer applies.\n this.target = null;\n this.menu?.close();\n this.backdrop.hidden = true;\n this.callbacks.onClose();\n // Back to where the dialog was opened from (a legend row, the object tree).\n if (this.returnFocus?.isConnected) this.returnFocus.focus();\n this.returnFocus = null;\n }\n\n /** Everything as it was on opening: only what changed is sent back. */\n private revert(): void {\n const target = this.target;\n const initial = this.initial;\n if (!target || !initial) return;\n const id = target.instanceId;\n if (!same(this.currentParams(), initial.params)) this.callbacks.onApply(id, { ...initial.params });\n const was = initial.look;\n const style: IndicatorStyleConfig = {};\n if (!same(this.look.colors, was.colors)) style.colors = [...was.colors];\n if (this.look.lineWidth !== was.lineWidth) style.lineWidths = [was.lineWidth];\n if (!same(this.look.plots, was.plots)) style.plots = plotsBack(this.look.plots, was.plots, target.plots);\n if (Object.keys(style).length > 0) this.callbacks.onStyle?.(id, style);\n if (!same(this.look.levels, was.levels)) this.callbacks.onLevels?.(id, target.ownLevels ? [...was.levels] : null);\n if (this.look.scale !== was.scale && was.scale) this.callbacks.onScale?.(id, was.scale);\n if (!same(this.look.intervals, was.intervals)) this.callbacks.onIntervals?.(id, was.intervals);\n }\n\n private currentParams(): Record<string, IndicatorParamValue> {\n const target = this.target;\n if (!target) return {};\n return { ...target.defaults, ...target.params, ...this.draft } as Record<string, IndicatorParamValue>;\n }\n\n // --- Tabs ---\n\n private tabs(): Tab[] {\n const target = this.target;\n if (!target) return [];\n const lines = !!(target.plots?.length && target.colors && this.callbacks.onStyle);\n const levels = !!(target.levels && this.callbacks.onLevels);\n return [\n 'inputs',\n ...(lines || levels ? ['style' as const] : []),\n ...(this.callbacks.onIntervals ? ['visibility' as const] : []),\n ];\n }\n\n /** The tab buttons for this indicator; built on open, then only marked (focus stays put). */\n private renderTabs(): void {\n const tabs = this.tabs();\n this.tabsEl.hidden = tabs.length < 2;\n this.tabButtons = tabs.map((tab) => {\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.className = 'tcw-modal-tab';\n btn.id = `tcw-indi-tab-${this.uid}-${tab}`;\n btn.setAttribute('role', 'tab');\n btn.setAttribute('aria-controls', this.bodyEl.id);\n btn.dataset.tab = tab;\n btn.textContent = this.t(`indicatorSettings.tab.${tab}`);\n btn.addEventListener('click', () => this.selectTab(tab));\n return btn;\n });\n this.tabsEl.replaceChildren(...this.tabButtons);\n this.markTabs();\n }\n\n private markTabs(): void {\n for (const btn of this.tabButtons) {\n const selected = btn.dataset.tab === this.tab;\n btn.classList.toggle('tcw-active', selected);\n btn.setAttribute('aria-selected', String(selected));\n btn.tabIndex = selected ? 0 : -1;\n if (selected) this.bodyEl.setAttribute('aria-labelledby', btn.id);\n }\n }\n\n private selectTab(tab: Tab, focus = false): void {\n this.tab = tab;\n this.markTabs();\n this.renderBody();\n if (focus) this.tabButtons.find((b) => b.dataset.tab === tab)?.focus();\n }\n\n /** Arrow keys (the other way right to left), Home and End move between tabs. */\n private onTabKey(e: KeyboardEvent): void {\n const tabs = this.tabButtons.map((b) => b.dataset.tab as Tab);\n const at = tabs.indexOf(this.tab);\n const rtl = this.modal.closest('[dir]')?.getAttribute('dir') === 'rtl';\n const [forward, back] = rtl ? ['ArrowLeft', 'ArrowRight'] : ['ArrowRight', 'ArrowLeft'];\n const next = e.key === forward ? (at + 1) % tabs.length\n : e.key === back ? (at - 1 + tabs.length) % tabs.length\n : e.key === 'Home' ? 0\n : e.key === 'End' ? tabs.length - 1\n : -1;\n if (next < 0 || tabs.length === 0) return;\n e.preventDefault();\n this.selectTab(tabs[next], true);\n }\n\n /** Escape cancels; Tab stays inside the dialog. */\n private onKeyDown(e: KeyboardEvent): void {\n if (e.key === 'Escape') {\n e.preventDefault();\n e.stopPropagation();\n this.finish(false);\n return;\n }\n keepTabInside(e, this.modal);\n }\n\n private renderBody(): void {\n this.bodyEl.replaceChildren();\n const target = this.target;\n if (!target) return;\n if (this.tab === 'style') this.renderStyle(target);\n else if (this.tab === 'visibility') renderVisibilityTab(this.bodyEl, this.look.intervals, (next) => this.setIntervals(next), this.t);\n else this.renderInputs();\n }\n\n // --- Inputs ---\n\n private currentValue(key: string): IndicatorParamValue {\n if (key in this.draft) return this.draft[key];\n const cur = this.target?.params[key];\n const def = this.target?.defaults[key];\n return (cur ?? def) as IndicatorParamValue;\n }\n\n private setValue(key: string, value: IndicatorParamValue): void {\n if (!this.target) return;\n this.draft[key] = value;\n this.callbacks.onApply(this.target.instanceId, { ...this.draft });\n }\n\n private renderInputs(): void {\n const section = document.createElement('div');\n section.className = 'tcw-settings-section';\n const inputs = describeInputs({ defaultConfig: this.target!.defaults, inputs: this.target!.inputs });\n if (inputs.length === 0) section.appendChild(this.note(this.t('indicatorSettings.noInputs')));\n for (const input of inputs) section.appendChild(this.fieldRow(input));\n this.bodyEl.appendChild(section);\n }\n\n /** One input's row: its control follows the kind the indicator declares, or the one its default implies. */\n private fieldRow(input: IndicatorInputInfo): HTMLLabelElement {\n const key = input.name;\n const value = this.currentValue(key);\n let control: HTMLElement;\n switch (input.kind) {\n case 'source': control = this.sourceControl(key, String(value ?? 'close')); break;\n case 'choice': control = this.choiceControl(key, input.options ?? [], value); break;\n case 'boolean': control = toggleSwitch(Boolean(value), (v) => this.setValue(key, v)); break;\n case 'int':\n case 'number':\n control = numberInput(Number(value), { min: input.min, max: input.max, step: input.step, whole: input.kind === 'int' }, (v) => this.setValue(key, v));\n break;\n case 'color': control = colorInput(String(value), (v) => this.setValue(key, v)); break;\n default: control = this.textControl(key, String(value ?? ''));\n }\n return settingsRow(input.label ?? inputLabel(key, this.t) ?? titleCase(key), control);\n }\n\n private sourceControl(key: string, value: string): HTMLSelectElement {\n const select = document.createElement('select');\n select.className = 'tcw-indi-input tcw-indi-select';\n const prices = document.createElement('optgroup');\n prices.label = this.t('indicatorSettings.source.prices');\n for (const source of PRICE_SOURCES) prices.appendChild(this.option(source, source));\n select.appendChild(prices);\n const lines = this.target?.lineSources ?? [];\n if (lines.length) {\n const group = document.createElement('optgroup');\n group.label = this.t('indicatorSettings.source.indicators');\n for (const line of lines) group.appendChild(this.option(line.value, line.label));\n select.appendChild(group);\n }\n select.value = value;\n if (select.value !== value) select.value = 'close'; // a line that is gone\n select.addEventListener('change', () => this.setValue(key, select.value));\n return select;\n }\n\n private choiceControl(key: string, options: readonly (string | number)[], value: IndicatorParamValue): HTMLSelectElement {\n const select = document.createElement('select');\n select.className = 'tcw-indi-input tcw-indi-select';\n for (const opt of options) select.appendChild(this.option(String(opt), choiceLabel(key, opt, this.t) ?? String(opt)));\n select.value = String(value);\n select.addEventListener('change', () => {\n const picked = options.find((o) => String(o) === select.value);\n if (picked !== undefined) this.setValue(key, picked);\n });\n return select;\n }\n\n private textControl(key: string, value: string): HTMLInputElement {\n const input = document.createElement('input');\n input.type = 'text';\n input.className = 'tcw-indi-input';\n input.value = value;\n input.addEventListener('change', () => this.setValue(key, input.value));\n return input;\n }\n\n // --- Style and visibility ---\n\n private renderStyle(target: IndicatorSettingsTarget): void {\n const id = target.instanceId;\n // Once closed, nothing: a field's change on its way out (its blur after a Cancel) no longer applies.\n const open = () => this.target === target;\n const onStyle = (style: IndicatorStyleConfig) => open() && this.callbacks.onStyle?.(id, style);\n const onLevels = (levels: number[]) => open() && this.callbacks.onLevels?.(id, levels);\n const onScale = (scale: 'right' | 'left') => open() && this.callbacks.onScale?.(id, scale);\n renderStyleTab(this.bodyEl, {\n plots: target.colors && this.callbacks.onStyle ? target.plots ?? [] : [],\n colors: this.look.colors,\n plotStyles: this.look.plots,\n lineWidth: this.look.lineWidth,\n scale: target.scale && this.callbacks.onScale ? this.look.scale : undefined,\n levels: target.levels && this.callbacks.onLevels ? this.look.levels : undefined,\n }, {\n color: (index, value) => {\n if (!open()) return;\n const next = [...this.look.colors];\n while (next.length <= index) next.push(next[0] ?? value);\n next[index] = value;\n this.look.colors = next;\n onStyle({ colors: [...next] });\n },\n plot: (key, patch) => {\n if (!open()) return;\n this.look.plots = { ...this.look.plots, [key]: { ...this.look.plots[key], ...patch } };\n onStyle({ plots: { [key]: patch } });\n },\n lineWidth: (width) => {\n if (!open()) return;\n this.look.lineWidth = width;\n onStyle({ lineWidths: [width] });\n },\n scale: (scale) => {\n if (!open()) return;\n this.look.scale = scale;\n onScale(scale);\n },\n levels: (levels) => {\n if (!open()) return;\n this.look.levels = levels;\n onLevels([...levels]);\n },\n }, this.t, this.uid);\n }\n\n private setIntervals(intervals: IndicatorIntervals | null): void {\n if (!this.target) return;\n this.look.intervals = intervals;\n this.callbacks.onIntervals?.(this.target.instanceId, intervals);\n }\n\n // --- Defaults ---\n\n private openDefaults(): void {\n if (this.menu?.isOpen()) {\n this.menu.close();\n return;\n }\n this.menu ??= new WidgetContextMenu(this.modal, this.t('indicatorSettings.defaults'));\n const at = this.defaultsBtn.getBoundingClientRect();\n const box = this.modal.getBoundingClientRect();\n const entries = [\n { id: 'reset', label: this.t('indicatorSettings.resetSettings'), icon: 'reset' },\n ...(this.callbacks.onSaveDefault ? [{ id: 'save', label: this.t('indicatorSettings.saveAsDefault'), icon: 'save' }] : []),\n ];\n // From the button's top: in the footer there is no room below, so it opens above the button.\n this.menu.open(entries, at.left - box.left, at.top - box.top, (pick) => {\n if (pick === 'reset') this.resetAll();\n else this.saveDefault();\n }, this.defaultsBtn);\n }\n\n /** The inputs, the lines' own styles, the levels and the intervals back to the indicator's own. */\n private resetAll(): void {\n const target = this.target;\n if (!target) return;\n const id = target.instanceId;\n const params = { ...target.defaults } as Record<string, IndicatorParamValue>;\n this.draft = { ...params };\n this.callbacks.onApply(id, { ...params });\n if (Object.keys(this.look.plots).length > 0) {\n this.callbacks.onStyle?.(id, { plots: plotsBack(this.look.plots, {}, target.plots) });\n this.look.plots = {};\n }\n if (target.levels && this.callbacks.onLevels) {\n this.callbacks.onLevels(id, null);\n this.look.levels = [...(target.defaultLevels ?? [])];\n }\n if (this.look.intervals) this.setIntervals(null);\n this.renderBody();\n }\n\n /**\n * Keep what was chosen for the next ones of the kind: the inputs that differ\n * from the indicator's own (not a line of another indicator, which the next\n * one won't have), and of the look only what someone picked. Colours the\n * chart gave stay its to give, so a second one still gets a colour of its own.\n */\n private saveDefault(): void {\n const target = this.target;\n if (!target) return;\n const params = Object.fromEntries(Object.entries(this.currentParams()).filter(([key, value]) =>\n !same(value, target.defaults[key]) && !(typeof value === 'string' && parseIndicatorSource(value)))) as Record<string, IndicatorParamValue>;\n const style: IndicatorStyleConfig = {};\n if (!isSeriesPalette(this.look.colors)) style.colors = [...this.look.colors];\n if (this.look.lineWidth !== DEFAULT_LINE_WIDTH) style.lineWidths = [this.look.lineWidth];\n if (Object.keys(this.look.plots).length > 0) style.plots = copy(this.look.plots);\n this.callbacks.onSaveDefault?.(target.indicatorId, { params, style });\n }\n\n // --- Pieces ---\n\n private footerButton(act: string, className: string, onClick: () => void, text?: string): HTMLButtonElement {\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.className = className;\n btn.dataset.act = act;\n if (text) btn.textContent = text;\n btn.addEventListener('click', onClick);\n return btn;\n }\n\n private note(text: string): HTMLDivElement {\n const el = document.createElement('div');\n el.className = 'tcw-settings-label';\n el.textContent = text;\n return el;\n }\n\n private option(value: string, label: string): HTMLOptionElement {\n const opt = document.createElement('option');\n opt.value = value;\n opt.textContent = label;\n return opt;\n }\n}\n"],"mappings":";;;;;;;;AA4EA,IAAI,IAAe,GAEb,IAAqB;AAE3B,SAAS,EAAU,GAA6C,GAAiC;AAC/F,QAAO;EAAE,SAAS;EAAM,WAAW,GAAO,MAAM,MAAM,EAAE,QAAQ,EAAI,EAAE,aAAa;EAAS;;AAG9F,IAAM,KAAQ,GAAY,MAAe,KAAK,UAAU,EAAE,KAAK,KAAK,UAAU,EAAE,EAE1E,KAAW,MAAgB,KAAK,MAAM,KAAK,UAAU,EAAM,CAAC;AAElE,SAAS,EAAU,GAAqB;AAEtC,QAAO,EACJ,QAAQ,MAAM,IAAI,CAClB,QAAQ,sBAAsB,QAAQ,CACtC,QAAQ,OAAO,MAAM,EAAE,aAAa,CAAC;;AAI1C,SAAS,EACP,GACA,GACA,GACoC;AACpC,QAAO,OAAO,YAAY,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,OAAO,KAAK,EAAK,EAAE,GAAG,OAAO,KAAK,EAAG,CAAC,CAAC,CAAC,CAAC,KAAK,MAAQ,CAAC,GAAK;EAAE,GAAG,EAAU,GAAO,EAAI;EAAE,GAAG,EAAG;EAAM,CAAC,CAAC,CAAC;;AAUnJ,IAAa,IAAb,MAAqC;CACnC;CACA;CACA;CACA;CACA;CACA;CACA,OAAyC;CACzC;CACA,SAAiD;CACjD,QAAqD,EAAE;CACvD,UAAsF;CACtF,OAAqB;EAAE,QAAQ,EAAE;EAAE,WAAW;EAAoB,OAAO,EAAE;EAAE,QAAQ,EAAE;EAAE,WAAW;EAAM;CAC1G,MAAmB;CACnB,aAA0C,EAAE;CAC5C,cAA0C;CAC1C,MAAuB;CACvB;CAEA,YAAY,GAAmB,GAAuC,GAAgB;AAMpF,EALA,KAAK,YAAY,GACjB,KAAK,IAAI,OAAO,MAAQ,EAAY,KAEpC,KAAK,WAAW,SAAS,cAAc,MAAM,EAC7C,KAAK,SAAS,YAAY,sBAC1B,KAAK,SAAS,SAAS;EAEvB,IAAI,IAAkB;AAgBtB,EAfA,KAAK,SAAS,iBAAiB,gBAAgB,MAAM;AACnD,OAAkB,EAAE,WAAW,KAAK;IACpC,EACF,KAAK,SAAS,iBAAiB,UAAU,MAAM;AAE7C,GADI,EAAE,WAAW,KAAK,YAAY,KAAiB,KAAK,OAAO,GAAM,EACrE,IAAkB;IAClB,EAEF,KAAK,QAAQ,SAAS,cAAc,MAAM,EAC1C,KAAK,MAAM,YAAY,uCACvB,KAAK,MAAM,aAAa,QAAQ,SAAS,EACzC,KAAK,MAAM,aAAa,cAAc,OAAO,EAC7C,KAAK,MAAM,aAAa,mBAAmB,kBAAkB,KAAK,MAAM,EAExE,KAAK,MAAM,WAAW,IACtB,KAAK,MAAM,iBAAiB,YAAY,MAAM,KAAK,UAAU,EAAE,CAAC;EAEhE,IAAM,IAAS,SAAS,cAAc,MAAM;AAG5C,EAFA,EAAO,YAAY,oBACnB,KAAK,UAAU,SAAS,cAAc,KAAK,EAC3C,KAAK,QAAQ,KAAK,kBAAkB,KAAK;EACzC,IAAM,IAAW,SAAS,cAAc,SAAS;AAqBjD,EApBA,EAAS,OAAO,UAChB,EAAS,YAAY,mBACrB,EAAS,aAAa,cAAc,KAAK,EAAE,0BAA0B,CAAC,EACtE,EAAS,YAAY,EAAW,KAAK,GAAG,EACxC,EAAS,iBAAiB,eAAe,KAAK,OAAO,GAAM,CAAC,EAC5D,EAAO,OAAO,KAAK,SAAS,EAAS,EAErC,KAAK,SAAS,SAAS,cAAc,MAAM,EAC3C,KAAK,OAAO,YAAY,kBACxB,KAAK,OAAO,aAAa,QAAQ,UAAU,EAC3C,KAAK,OAAO,iBAAiB,YAAY,MAAM,KAAK,SAAS,EAAE,CAAC,EAEhE,KAAK,SAAS,SAAS,cAAc,MAAM,EAC3C,KAAK,OAAO,YAAY,kBACxB,KAAK,OAAO,KAAK,kBAAkB,KAAK,OACxC,KAAK,OAAO,aAAa,QAAQ,WAAW,EAE5C,KAAK,cAAc,KAAK,aAAa,YAAY,yBAAyB,KAAK,cAAc,CAAC,EAC9F,KAAK,MAAM,OAAO,GAAQ,KAAK,QAAQ,KAAK,QAAQ,KAAK,aAAa,CAAC,EACvE,KAAK,SAAS,YAAY,KAAK,MAAM,EACrC,EAAK,YAAY,KAAK,SAAS;;CAIjC,cAAsC;AAIpC,EAHA,KAAK,YAAY,aAAa,iBAAiB,OAAO,EACtD,KAAK,YAAY,aAAa,iBAAiB,QAAQ,EACvD,KAAK,YAAY,YAAY,gBAAgB,EAAW,eAAe,GAAG,IAC1E,KAAK,YAAY,cAAc,OAAO,CAAE,cAAc,KAAK,EAAE,6BAA6B;EAC1F,IAAM,IAAM,SAAS,cAAc,MAAM;AAEzC,EADA,EAAI,YAAY,kBAChB,EAAI,OACF,KAAK,aAAa,UAAU,2BAA2B,KAAK,OAAO,GAAM,EAAE,KAAK,EAAE,gBAAgB,CAAC,EACnG,KAAK,aAAa,MAAM,sBAAsB,KAAK,OAAO,GAAK,EAAE,KAAK,EAAE,qBAAqB,CAAC,CAC/F;EACD,IAAM,IAAS,SAAS,cAAc,MAAM;AAG5C,SAFA,EAAO,YAAY,oBACnB,EAAO,OAAO,KAAK,aAAa,EAAI,EAC7B;;CAGT,KAAK,GAAuC;AAkB1C,EAjBI,KAAK,UAAQ,KAAK,OAAO,GAAK,EAC9B,KAAK,SAAS,WAAQ,KAAK,cAAc,SAAS,yBAAyB,cAAc,SAAS,gBAAgB,OACtH,KAAK,SAAS,GACd,KAAK,QAAQ,cAAc,KAAK,EAAE,0BAA0B,CAAC,QAAQ,UAAU,EAAO,KAAK,EAC3F,KAAK,QAAQ,EAAE,EACf,KAAK,OAAO;GACV,QAAQ,CAAC,GAAI,EAAO,UAAU,EAAE,CAAE;GAClC,WAAW,EAAO,aAAa;GAC/B,OAAO,OAAO,YAAY,OAAO,QAAQ,EAAO,cAAc,EAAE,CAAC,CAAC,KAAK,CAAC,GAAK,OAAU,CAAC,GAAK,EAAE,GAAG,GAAM,CAAC,CAAC,CAAC;GAC3G,QAAQ,CAAC,GAAI,EAAO,UAAU,EAAE,CAAE;GAClC,OAAO,EAAO;GACd,WAAW,EAAO,aAAa;GAChC,EACD,KAAK,UAAU;GAAE,QAAQ,KAAK,eAAe;GAAE,MAAM,EAAK,KAAK,KAAK;GAAE,EACtE,KAAK,MAAM,UACX,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,SAAS,SAAS;EAEvB,IAAM,IAAW,KAAK,OAAO,SAAS,OAAO,KAAK,WAAW,MAAM;AACnE,GAAC,KAAK,OAAO,cAAA,qIAAqC,IAAI,KAAY,KAAK,OAAO,OAAO;;CAIvF,QAAc;AACZ,OAAK,OAAO,GAAK;;CAGnB,SAAkB;AAChB,SAAO,CAAC,KAAK,SAAS;;CAGxB,UAAgB;AAEd,EADA,KAAK,MAAM,SAAS,EACpB,KAAK,SAAS,QAAQ;;CAIxB,OAAe,GAAqB;AAC7B,WAAK,QACV;OAAI,GAAM;IACR,IAAM,IAAS,SAAS;AACxB,KAAK,aAAkB,oBAAoB,aAAkB,sBAAsB,KAAK,MAAM,SAAS,EAAO,IAC5G,EAAO,cAAc,IAAI,MAAM,SAAS,CAAC;SAG3C,MAAK,QAAQ;AASf,GANA,KAAK,SAAS,MACd,KAAK,MAAM,OAAO,EAClB,KAAK,SAAS,SAAS,IACvB,KAAK,UAAU,SAAS,EAEpB,KAAK,aAAa,eAAa,KAAK,YAAY,OAAO,EAC3D,KAAK,cAAc;;;CAIrB,SAAuB;EACrB,IAAM,IAAS,KAAK,QACd,IAAU,KAAK;AACrB,MAAI,CAAC,KAAU,CAAC,EAAS;EACzB,IAAM,IAAK,EAAO;AAClB,EAAK,EAAK,KAAK,eAAe,EAAE,EAAQ,OAAO,IAAE,KAAK,UAAU,QAAQ,GAAI,EAAE,GAAG,EAAQ,QAAQ,CAAC;EAClG,IAAM,IAAM,EAAQ,MACd,IAA8B,EAAE;AAOtC,EANK,EAAK,KAAK,KAAK,QAAQ,EAAI,OAAO,KAAE,EAAM,SAAS,CAAC,GAAG,EAAI,OAAO,GACnE,KAAK,KAAK,cAAc,EAAI,cAAW,EAAM,aAAa,CAAC,EAAI,UAAU,GACxE,EAAK,KAAK,KAAK,OAAO,EAAI,MAAM,KAAE,EAAM,QAAQ,EAAU,KAAK,KAAK,OAAO,EAAI,OAAO,EAAO,MAAM,GACpG,OAAO,KAAK,EAAM,CAAC,SAAS,KAAG,KAAK,UAAU,UAAU,GAAI,EAAM,EACjE,EAAK,KAAK,KAAK,QAAQ,EAAI,OAAO,IAAE,KAAK,UAAU,WAAW,GAAI,EAAO,YAAY,CAAC,GAAG,EAAI,OAAO,GAAG,KAAK,EAC7G,KAAK,KAAK,UAAU,EAAI,SAAS,EAAI,SAAO,KAAK,UAAU,UAAU,GAAI,EAAI,MAAM,EAClF,EAAK,KAAK,KAAK,WAAW,EAAI,UAAU,IAAE,KAAK,UAAU,cAAc,GAAI,EAAI,UAAU;;CAGhG,gBAA6D;EAC3D,IAAM,IAAS,KAAK;AAEpB,SADK,IACE;GAAE,GAAG,EAAO;GAAU,GAAG,EAAO;GAAQ,GAAG,KAAK;GAAO,GAD1C,EAAE;;CAMxB,OAAsB;EACpB,IAAM,IAAS,KAAK;AACpB,MAAI,CAAC,EAAQ,QAAO,EAAE;EACtB,IAAM,IAAQ,CAAC,EAAE,EAAO,OAAO,UAAU,EAAO,UAAU,KAAK,UAAU,UACnE,IAAS,CAAC,EAAE,EAAO,UAAU,KAAK,UAAU;AAClD,SAAO;GACL;GACA,GAAI,KAAS,IAAS,CAAC,QAAiB,GAAG,EAAE;GAC7C,GAAI,KAAK,UAAU,cAAc,CAAC,aAAsB,GAAG,EAAE;GAC9D;;CAIH,aAA2B;EACzB,IAAM,IAAO,KAAK,MAAM;AAexB,EAdA,KAAK,OAAO,SAAS,EAAK,SAAS,GACnC,KAAK,aAAa,EAAK,KAAK,MAAQ;GAClC,IAAM,IAAM,SAAS,cAAc,SAAS;AAS5C,UARA,EAAI,OAAO,UACX,EAAI,YAAY,iBAChB,EAAI,KAAK,gBAAgB,KAAK,IAAI,GAAG,KACrC,EAAI,aAAa,QAAQ,MAAM,EAC/B,EAAI,aAAa,iBAAiB,KAAK,OAAO,GAAG,EACjD,EAAI,QAAQ,MAAM,GAClB,EAAI,cAAc,KAAK,EAAE,yBAAyB,IAAM,EACxD,EAAI,iBAAiB,eAAe,KAAK,UAAU,EAAI,CAAC,EACjD;IACP,EACF,KAAK,OAAO,gBAAgB,GAAG,KAAK,WAAW,EAC/C,KAAK,UAAU;;CAGjB,WAAyB;AACvB,OAAK,IAAM,KAAO,KAAK,YAAY;GACjC,IAAM,IAAW,EAAI,QAAQ,QAAQ,KAAK;AAI1C,GAHA,EAAI,UAAU,OAAO,cAAc,EAAS,EAC5C,EAAI,aAAa,iBAAiB,OAAO,EAAS,CAAC,EACnD,EAAI,WAAW,IAAW,IAAI,IAC1B,KAAU,KAAK,OAAO,aAAa,mBAAmB,EAAI,GAAG;;;CAIrE,UAAkB,GAAU,IAAQ,IAAa;AAI/C,EAHA,KAAK,MAAM,GACX,KAAK,UAAU,EACf,KAAK,YAAY,EACb,KAAO,KAAK,WAAW,MAAM,MAAM,EAAE,QAAQ,QAAQ,EAAI,EAAE,OAAO;;CAIxE,SAAiB,GAAwB;EACvC,IAAM,IAAO,KAAK,WAAW,KAAK,MAAM,EAAE,QAAQ,IAAW,EACvD,IAAK,EAAK,QAAQ,KAAK,IAAI,EAE3B,CAAC,GAAS,KADJ,KAAK,MAAM,QAAQ,QAAQ,EAAE,aAAa,MAAM,KAAK,QACnC,CAAC,aAAa,aAAa,GAAG,CAAC,cAAc,YAAY,EACjF,IAAO,EAAE,QAAQ,KAAW,IAAK,KAAK,EAAK,SAC7C,EAAE,QAAQ,KAAQ,IAAK,IAAI,EAAK,UAAU,EAAK,SAC/C,EAAE,QAAQ,SAAS,IACnB,EAAE,QAAQ,QAAQ,EAAK,SAAS,IAChC;AACA,MAAO,KAAK,EAAK,WAAW,MAChC,EAAE,gBAAgB,EAClB,KAAK,UAAU,EAAK,IAAO,GAAK;;CAIlC,UAAkB,GAAwB;AACxC,MAAI,EAAE,QAAQ,UAAU;AAGtB,GAFA,EAAE,gBAAgB,EAClB,EAAE,iBAAiB,EACnB,KAAK,OAAO,GAAM;AAClB;;AAEF,IAAc,GAAG,KAAK,MAAM;;CAG9B,aAA2B;AACzB,OAAK,OAAO,iBAAiB;EAC7B,IAAM,IAAS,KAAK;AACf,QACD,KAAK,QAAQ,UAAS,KAAK,YAAY,EAAO,GACzC,KAAK,QAAQ,eAAc,EAAoB,KAAK,QAAQ,KAAK,KAAK,YAAY,MAAS,KAAK,aAAa,EAAK,EAAE,KAAK,EAAE,GAC/H,KAAK,cAAc;;CAK1B,aAAqB,GAAkC;AACrD,MAAI,KAAO,KAAK,MAAO,QAAO,KAAK,MAAM;EACzC,IAAM,IAAM,KAAK,QAAQ,OAAO,IAC1B,IAAM,KAAK,QAAQ,SAAS;AAClC,SAAQ,KAAO;;CAGjB,SAAiB,GAAa,GAAkC;AACzD,OAAK,WACV,KAAK,MAAM,KAAO,GAClB,KAAK,UAAU,QAAQ,KAAK,OAAO,YAAY,EAAE,GAAG,KAAK,OAAO,CAAC;;CAGnE,eAA6B;EAC3B,IAAM,IAAU,SAAS,cAAc,MAAM;AAC7C,IAAQ,YAAY;EACpB,IAAM,IAAS,EAAe;GAAE,eAAe,KAAK,OAAQ;GAAU,QAAQ,KAAK,OAAQ;GAAQ,CAAC;AACpG,EAAI,EAAO,WAAW,KAAG,EAAQ,YAAY,KAAK,KAAK,KAAK,EAAE,6BAA6B,CAAC,CAAC;AAC7F,OAAK,IAAM,KAAS,EAAQ,GAAQ,YAAY,KAAK,SAAS,EAAM,CAAC;AACrE,OAAK,OAAO,YAAY,EAAQ;;CAIlC,SAAiB,GAA6C;EAC5D,IAAM,IAAM,EAAM,MACZ,IAAQ,KAAK,aAAa,EAAI,EAChC;AACJ,UAAQ,EAAM,MAAd;GACE,KAAK;AAAU,QAAU,KAAK,cAAc,GAAK,OAAO,KAAS,QAAQ,CAAC;AAAE;GAC5E,KAAK;AAAU,QAAU,KAAK,cAAc,GAAK,EAAM,WAAW,EAAE,EAAE,EAAM;AAAE;GAC9E,KAAK;AAAW,QAAU,EAAa,EAAQ,IAAS,MAAM,KAAK,SAAS,GAAK,EAAE,CAAC;AAAE;GACtF,KAAK;GACL,KAAK;AACH,QAAU,EAAY,OAAO,EAAM,EAAE;KAAE,KAAK,EAAM;KAAK,KAAK,EAAM;KAAK,MAAM,EAAM;KAAM,OAAO,EAAM,SAAS;KAAO,GAAG,MAAM,KAAK,SAAS,GAAK,EAAE,CAAC;AACrJ;GACF,KAAK;AAAS,QAAU,EAAW,OAAO,EAAM,GAAG,MAAM,KAAK,SAAS,GAAK,EAAE,CAAC;AAAE;GACjF,QAAS,KAAU,KAAK,YAAY,GAAK,OAAO,KAAS,GAAG,CAAC;;AAE/D,SAAO,EAAY,EAAM,SAAS,EAAW,GAAK,KAAK,EAAE,IAAI,EAAU,EAAI,EAAE,EAAQ;;CAGvF,cAAsB,GAAa,GAAkC;EACnE,IAAM,IAAS,SAAS,cAAc,SAAS;AAC/C,IAAO,YAAY;EACnB,IAAM,IAAS,SAAS,cAAc,WAAW;AACjD,IAAO,QAAQ,KAAK,EAAE,kCAAkC;AACxD,OAAK,IAAM,KAAU,EAAe,GAAO,YAAY,KAAK,OAAO,GAAQ,EAAO,CAAC;AACnF,IAAO,YAAY,EAAO;EAC1B,IAAM,IAAQ,KAAK,QAAQ,eAAe,EAAE;AAC5C,MAAI,EAAM,QAAQ;GAChB,IAAM,IAAQ,SAAS,cAAc,WAAW;AAChD,KAAM,QAAQ,KAAK,EAAE,sCAAsC;AAC3D,QAAK,IAAM,KAAQ,EAAO,GAAM,YAAY,KAAK,OAAO,EAAK,OAAO,EAAK,MAAM,CAAC;AAChF,KAAO,YAAY,EAAM;;AAK3B,SAHA,EAAO,QAAQ,GACX,EAAO,UAAU,MAAO,EAAO,QAAQ,UAC3C,EAAO,iBAAiB,gBAAgB,KAAK,SAAS,GAAK,EAAO,MAAM,CAAC,EAClE;;CAGT,cAAsB,GAAa,GAAuC,GAA+C;EACvH,IAAM,IAAS,SAAS,cAAc,SAAS;AAC/C,IAAO,YAAY;AACnB,OAAK,IAAM,KAAO,EAAS,GAAO,YAAY,KAAK,OAAO,OAAO,EAAI,EAAE,EAAY,GAAK,GAAK,KAAK,EAAE,IAAI,OAAO,EAAI,CAAC,CAAC;AAMrH,SALA,EAAO,QAAQ,OAAO,EAAM,EAC5B,EAAO,iBAAiB,gBAAgB;GACtC,IAAM,IAAS,EAAQ,MAAM,MAAM,OAAO,EAAE,KAAK,EAAO,MAAM;AAC9D,GAAI,MAAW,KAAA,KAAW,KAAK,SAAS,GAAK,EAAO;IACpD,EACK;;CAGT,YAAoB,GAAa,GAAiC;EAChE,IAAM,IAAQ,SAAS,cAAc,QAAQ;AAK7C,SAJA,EAAM,OAAO,QACb,EAAM,YAAY,kBAClB,EAAM,QAAQ,GACd,EAAM,iBAAiB,gBAAgB,KAAK,SAAS,GAAK,EAAM,MAAM,CAAC,EAChE;;CAKT,YAAoB,GAAuC;EACzD,IAAM,IAAK,EAAO,YAEZ,UAAa,KAAK,WAAW,GAC7B,KAAW,MAAgC,GAAM,IAAI,KAAK,UAAU,UAAU,GAAI,EAAM,EACxF,KAAY,MAAqB,GAAM,IAAI,KAAK,UAAU,WAAW,GAAI,EAAO,EAChF,KAAW,MAA4B,GAAM,IAAI,KAAK,UAAU,UAAU,GAAI,EAAM;AAC1F,IAAe,KAAK,QAAQ;GAC1B,OAAO,EAAO,UAAU,KAAK,UAAU,UAAU,EAAO,SAAS,EAAE,GAAG,EAAE;GACxE,QAAQ,KAAK,KAAK;GAClB,YAAY,KAAK,KAAK;GACtB,WAAW,KAAK,KAAK;GACrB,OAAO,EAAO,SAAS,KAAK,UAAU,UAAU,KAAK,KAAK,QAAQ,KAAA;GAClE,QAAQ,EAAO,UAAU,KAAK,UAAU,WAAW,KAAK,KAAK,SAAS,KAAA;GACvE,EAAE;GACD,QAAQ,GAAO,MAAU;AACvB,QAAI,CAAC,GAAM,CAAE;IACb,IAAM,IAAO,CAAC,GAAG,KAAK,KAAK,OAAO;AAClC,WAAO,EAAK,UAAU,GAAO,GAAK,KAAK,EAAK,MAAM,EAAM;AAGxD,IAFA,EAAK,KAAS,GACd,KAAK,KAAK,SAAS,GACnB,EAAQ,EAAE,QAAQ,CAAC,GAAG,EAAK,EAAE,CAAC;;GAEhC,OAAO,GAAK,MAAU;AACf,OAAM,KACX,KAAK,KAAK,QAAQ;KAAE,GAAG,KAAK,KAAK;MAAQ,IAAM;MAAE,GAAG,KAAK,KAAK,MAAM;MAAM,GAAG;MAAO;KAAE,EACtF,EAAQ,EAAE,OAAO,GAAG,IAAM,GAAO,EAAE,CAAC;;GAEtC,YAAY,MAAU;AACf,OAAM,KACX,KAAK,KAAK,YAAY,GACtB,EAAQ,EAAE,YAAY,CAAC,EAAM,EAAE,CAAC;;GAElC,QAAQ,MAAU;AACX,OAAM,KACX,KAAK,KAAK,QAAQ,GAClB,EAAQ,EAAM;;GAEhB,SAAS,MAAW;AACb,OAAM,KACX,KAAK,KAAK,SAAS,GACnB,EAAS,CAAC,GAAG,EAAO,CAAC;;GAExB,EAAE,KAAK,GAAG,KAAK,IAAI;;CAGtB,aAAqB,GAA4C;AAC1D,OAAK,WACV,KAAK,KAAK,YAAY,GACtB,KAAK,UAAU,cAAc,KAAK,OAAO,YAAY,EAAU;;CAKjE,eAA6B;AAC3B,MAAI,KAAK,MAAM,QAAQ,EAAE;AACvB,QAAK,KAAK,OAAO;AACjB;;AAEF,OAAK,SAAS,IAAI,EAAkB,KAAK,OAAO,KAAK,EAAE,6BAA6B,CAAC;EACrF,IAAM,IAAK,KAAK,YAAY,uBAAuB,EAC7C,IAAM,KAAK,MAAM,uBAAuB,EACxC,IAAU,CACd;GAAE,IAAI;GAAS,OAAO,KAAK,EAAE,kCAAkC;GAAE,MAAM;GAAS,EAChF,GAAI,KAAK,UAAU,gBAAgB,CAAC;GAAE,IAAI;GAAQ,OAAO,KAAK,EAAE,kCAAkC;GAAE,MAAM;GAAQ,CAAC,GAAG,EAAE,CACzH;AAED,OAAK,KAAK,KAAK,GAAS,EAAG,OAAO,EAAI,MAAM,EAAG,MAAM,EAAI,MAAM,MAAS;AACtE,GAAI,MAAS,UAAS,KAAK,UAAU,GAChC,KAAK,aAAa;KACtB,KAAK,YAAY;;CAItB,WAAyB;EACvB,IAAM,IAAS,KAAK;AACpB,MAAI,CAAC,EAAQ;EACb,IAAM,IAAK,EAAO,YACZ,IAAS,EAAE,GAAG,EAAO,UAAU;AAYrC,EAXA,KAAK,QAAQ,EAAE,GAAG,GAAQ,EAC1B,KAAK,UAAU,QAAQ,GAAI,EAAE,GAAG,GAAQ,CAAC,EACrC,OAAO,KAAK,KAAK,KAAK,MAAM,CAAC,SAAS,MACxC,KAAK,UAAU,UAAU,GAAI,EAAE,OAAO,EAAU,KAAK,KAAK,OAAO,EAAE,EAAE,EAAO,MAAM,EAAE,CAAC,EACrF,KAAK,KAAK,QAAQ,EAAE,GAElB,EAAO,UAAU,KAAK,UAAU,aAClC,KAAK,UAAU,SAAS,GAAI,KAAK,EACjC,KAAK,KAAK,SAAS,CAAC,GAAI,EAAO,iBAAiB,EAAE,CAAE,GAElD,KAAK,KAAK,aAAW,KAAK,aAAa,KAAK,EAChD,KAAK,YAAY;;CASnB,cAA4B;EAC1B,IAAM,IAAS,KAAK;AACpB,MAAI,CAAC,EAAQ;EACb,IAAM,IAAS,OAAO,YAAY,OAAO,QAAQ,KAAK,eAAe,CAAC,CAAC,QAAQ,CAAC,GAAK,OACnF,CAAC,EAAK,GAAO,EAAO,SAAS,GAAK,IAAI,EAAE,OAAO,KAAU,YAAY,EAAqB,EAAM,EAAE,CAAC,EAC/F,IAA8B,EAAE;AAItC,EAHK,EAAgB,KAAK,KAAK,OAAO,KAAE,EAAM,SAAS,CAAC,GAAG,KAAK,KAAK,OAAO,GACxE,KAAK,KAAK,cAAc,MAAoB,EAAM,aAAa,CAAC,KAAK,KAAK,UAAU,GACpF,OAAO,KAAK,KAAK,KAAK,MAAM,CAAC,SAAS,MAAG,EAAM,QAAQ,EAAK,KAAK,KAAK,MAAM,GAChF,KAAK,UAAU,gBAAgB,EAAO,aAAa;GAAE;GAAQ;GAAO,CAAC;;CAKvE,aAAqB,GAAa,GAAmB,GAAqB,GAAkC;EAC1G,IAAM,IAAM,SAAS,cAAc,SAAS;AAM5C,SALA,EAAI,OAAO,UACX,EAAI,YAAY,GAChB,EAAI,QAAQ,MAAM,GACd,MAAM,EAAI,cAAc,IAC5B,EAAI,iBAAiB,SAAS,EAAQ,EAC/B;;CAGT,KAAa,GAA8B;EACzC,IAAM,IAAK,SAAS,cAAc,MAAM;AAGxC,SAFA,EAAG,YAAY,sBACf,EAAG,cAAc,GACV;;CAGT,OAAe,GAAe,GAAkC;EAC9D,IAAM,IAAM,SAAS,cAAc,SAAS;AAG5C,SAFA,EAAI,QAAQ,GACZ,EAAI,cAAc,GACX"}
|
|
1
|
+
{"version":3,"file":"WidgetIndicatorSettings-D3W40Nud.js","names":[],"sources":["../src/widget/WidgetIndicatorSettings.ts"],"sourcesContent":["import type { IndicatorInputSpec, IndicatorIntervals, IndicatorPlot, IndicatorPlotStyle, IndicatorStyleConfig } from '@tradecanvas/commons';\nimport { PRICE_SOURCES, parseIndicatorSource } from '@tradecanvas/commons';\nimport { describeInputs, isSeriesPalette, type IndicatorInputInfo } from '@tradecanvas/core';\nimport { FOCUSABLE, keepTabInside } from './focusTrap.js';\nimport { createIcon } from './icons.js';\nimport { EN_MESSAGES, type Translator } from './i18n.js';\nimport { WidgetContextMenu } from './WidgetContextMenu.js';\nimport { colorInput, numberInput, settingsRow, toggleSwitch } from './settingsControls.js';\nimport { renderStyleTab, renderVisibilityTab } from './indicatorSettingsTabs.js';\nimport { choiceLabel, inputLabel } from './indicatorInputWords.js';\n\nexport type IndicatorParamValue = number | string | boolean;\n\nexport interface IndicatorSettingsTarget {\n instanceId: string;\n /** The kind of indicator (`'rsi'`): what \"Save as default\" is for. */\n indicatorId: string;\n name: string;\n /** Default config — defines the editable keys and their types. */\n defaults: Record<string, unknown>;\n /** Current values, overlaid on defaults. */\n params: Record<string, unknown>;\n /** How parameters are edited (the descriptor's `inputs`). */\n inputs?: Readonly<Record<string, IndicatorInputSpec>>;\n /** Other indicators' lines a source can be: value `ind:<instanceId>:<key>`. */\n lineSources?: readonly { value: string; label: string }[];\n /** What it draws; with `colors`, the lines of the Style tab. */\n plots?: readonly IndicatorPlot[];\n colors?: readonly string[];\n lineWidth?: number;\n /** Each line's own dash and visibility, by its key. */\n plotStyles?: Readonly<Record<string, IndicatorPlotStyle>>;\n /** Its levels and its indicator's defaults; with them, the levels under the lines. */\n levels?: readonly number[];\n defaultLevels?: readonly number[];\n /** Whether `levels` are the instance's own; not: its indicator's defaults, which Cancel leaves it on. */\n ownLevels?: boolean;\n /** The price scale an overlay is on; set only for price-pane overlays. */\n scale?: 'right' | 'left';\n /** The intervals it shows on; unset or null: all of them. */\n intervals?: IndicatorIntervals | null;\n}\n\n/** What \"Save as default\" keeps: new indicators of the kind start with it. */\nexport interface IndicatorDefault {\n params: Record<string, IndicatorParamValue>;\n style: IndicatorStyleConfig;\n}\n\nexport interface IndicatorSettingsCallbacks {\n onApply: (instanceId: string, params: Record<string, IndicatorParamValue>) => void;\n onStyle?: (instanceId: string, style: IndicatorStyleConfig) => void;\n /** `null` restores the indicator's default levels. */\n onLevels?: (instanceId: string, levels: number[] | null) => void;\n /** Move an overlay to the left price scale or back to the price scale. */\n onScale?: (instanceId: string, scale: 'right' | 'left') => void;\n /** The intervals it shows on (`null`: all); with it, the Visibility tab. */\n onIntervals?: (instanceId: string, intervals: IndicatorIntervals | null) => void;\n /** \"Save as default\" in the Defaults menu. */\n onSaveDefault?: (indicatorId: string, saved: IndicatorDefault) => void;\n onClose: () => void;\n}\n\ntype Tab = 'inputs' | 'style' | 'visibility';\n\n/** What the dialog changes besides the inputs: as it was on opening, and as it is. */\ninterface Look {\n colors: string[];\n lineWidth: number;\n plots: Record<string, IndicatorPlotStyle>;\n levels: number[];\n scale?: 'right' | 'left';\n intervals: IndicatorIntervals | null;\n}\n\n/** Ids for the dialog's labelling (several widgets may share a page). */\nlet nextDialogId = 1;\n\nconst DEFAULT_LINE_WIDTH = 1.5;\n/** A line as it is with no style of its own: shown, and dashed as its indicator declares it. */\nfunction plainPlot(plots: readonly IndicatorPlot[] | undefined, key: string): IndicatorPlotStyle {\n return { visible: true, lineStyle: plots?.find((p) => p.key === key)?.lineStyle ?? 'solid' };\n}\n\nconst same = (a: unknown, b: unknown) => JSON.stringify(a) === JSON.stringify(b);\n/** A copy of plain values (numbers, strings, flags, lists of them). */\nconst copy = <T>(value: T): T => JSON.parse(JSON.stringify(value)) as T;\n\nfunction titleCase(key: string): string {\n // camelCase / snake_case → \"Title Case\"\n return key\n .replace(/_/g, ' ')\n .replace(/([a-z0-9])([A-Z])/g, '$1 $2')\n .replace(/^./, (c) => c.toUpperCase());\n}\n\n/** Each line put back as `to` has it; one `to` has no style for goes back to plain. */\nfunction plotsBack(\n from: Record<string, IndicatorPlotStyle>,\n to: Record<string, IndicatorPlotStyle>,\n plots: readonly IndicatorPlot[] | undefined,\n): Record<string, IndicatorPlotStyle> {\n return Object.fromEntries([...new Set([...Object.keys(from), ...Object.keys(to)])].map((key) => [key, { ...plainPlot(plots, key), ...to[key] }]));\n}\n\n/**\n * Per-indicator settings: its inputs; its style (each line shown or not, its\n * colours and dash, the line width, the scale, the levels); and the\n * intervals it shows on. Every edit shows at once; Cancel (or Escape) puts\n * it all back as it was, Ok keeps it. The Defaults menu resets it, or keeps\n * it for the next ones of its kind.\n */\nexport class WidgetIndicatorSettings {\n private backdrop: HTMLDivElement;\n private modal: HTMLDivElement;\n private bodyEl: HTMLDivElement;\n private titleEl: HTMLHeadingElement;\n private tabsEl: HTMLDivElement;\n private defaultsBtn: HTMLButtonElement;\n private menu: WidgetContextMenu | null = null;\n private callbacks: IndicatorSettingsCallbacks;\n private target: IndicatorSettingsTarget | null = null;\n private draft: Record<string, IndicatorParamValue> = {};\n private initial: { params: Record<string, IndicatorParamValue>; look: Look } | null = null;\n private look: Look = { colors: [], lineWidth: DEFAULT_LINE_WIDTH, plots: {}, levels: [], intervals: null };\n private tab: Tab = 'inputs';\n private tabButtons: HTMLButtonElement[] = [];\n private returnFocus: HTMLElement | null = null;\n private readonly uid = nextDialogId++;\n private readonly t: Translator;\n\n constructor(host: HTMLElement, callbacks: IndicatorSettingsCallbacks, t?: Translator) {\n this.callbacks = callbacks;\n this.t = t ?? ((key) => EN_MESSAGES[key]);\n\n this.backdrop = document.createElement('div');\n this.backdrop.className = 'tcw-modal-backdrop';\n this.backdrop.hidden = true;\n // A press that starts in the dialog (a slider dragged, text selected) and ends on the backdrop doesn't cancel it.\n let pressedBackdrop = false;\n this.backdrop.addEventListener('pointerdown', (e) => {\n pressedBackdrop = e.target === this.backdrop;\n });\n this.backdrop.addEventListener('click', (e) => {\n if (e.target === this.backdrop && pressedBackdrop) this.finish(false);\n pressedBackdrop = false;\n });\n\n this.modal = document.createElement('div');\n this.modal.className = 'tcw-modal tcw-modal-narrow tcw-indi';\n this.modal.setAttribute('role', 'dialog');\n this.modal.setAttribute('aria-modal', 'true');\n this.modal.setAttribute('aria-labelledby', `tcw-indi-title-${this.uid}`);\n // Focusable, so keys still reach it after a click on its blank parts.\n this.modal.tabIndex = -1;\n this.modal.addEventListener('keydown', (e) => this.onKeyDown(e));\n\n const header = document.createElement('div');\n header.className = 'tcw-modal-header';\n this.titleEl = document.createElement('h3');\n this.titleEl.id = `tcw-indi-title-${this.uid}`;\n const closeBtn = document.createElement('button');\n closeBtn.type = 'button';\n closeBtn.className = 'tcw-modal-close';\n closeBtn.setAttribute('aria-label', this.t('indicatorSettings.close'));\n closeBtn.innerHTML = createIcon('x', 16);\n closeBtn.addEventListener('click', () => this.finish(false));\n header.append(this.titleEl, closeBtn);\n\n this.tabsEl = document.createElement('div');\n this.tabsEl.className = 'tcw-modal-tabs';\n this.tabsEl.setAttribute('role', 'tablist');\n this.tabsEl.addEventListener('keydown', (e) => this.onTabKey(e));\n\n this.bodyEl = document.createElement('div');\n this.bodyEl.className = 'tcw-modal-body';\n this.bodyEl.id = `tcw-indi-panel-${this.uid}`;\n this.bodyEl.setAttribute('role', 'tabpanel');\n\n this.defaultsBtn = this.footerButton('defaults', 'tcw-footer-menu', () => this.openDefaults());\n this.modal.append(header, this.tabsEl, this.bodyEl, this.buildFooter());\n this.backdrop.appendChild(this.modal);\n host.appendChild(this.backdrop);\n }\n\n /** The Defaults menu on the left; Cancel and OK on the right. */\n private buildFooter(): HTMLDivElement {\n this.defaultsBtn.setAttribute('aria-haspopup', 'menu');\n this.defaultsBtn.setAttribute('aria-expanded', 'false');\n this.defaultsBtn.innerHTML = `<span></span>${createIcon('chevronDown', 12)}`;\n this.defaultsBtn.querySelector('span')!.textContent = this.t('indicatorSettings.defaults');\n const end = document.createElement('div');\n end.className = 'tcw-footer-end';\n end.append(\n this.footerButton('cancel', 'tcw-footer-cancel', () => this.finish(false), this.t('common.cancel')),\n this.footerButton('ok', 'tcw-done-btn', () => this.finish(true), this.t('drawingSettings.ok')),\n );\n const footer = document.createElement('div');\n footer.className = 'tcw-modal-footer';\n footer.append(this.defaultsBtn, end);\n return footer;\n }\n\n open(target: IndicatorSettingsTarget): void {\n if (this.target) this.finish(true);\n if (this.backdrop.hidden) this.returnFocus = document.activeElement instanceof HTMLElement ? document.activeElement : null;\n this.target = target;\n this.titleEl.textContent = this.t('indicatorSettings.title').replace('{name}', target.name);\n this.draft = {};\n this.look = {\n colors: [...(target.colors ?? [])],\n lineWidth: target.lineWidth ?? DEFAULT_LINE_WIDTH,\n plots: Object.fromEntries(Object.entries(target.plotStyles ?? {}).map(([key, plot]) => [key, { ...plot }])),\n levels: [...(target.levels ?? [])],\n scale: target.scale,\n intervals: target.intervals ?? null,\n };\n this.initial = { params: this.currentParams(), look: copy(this.look) };\n this.tab = 'inputs';\n this.renderTabs();\n this.renderBody();\n this.backdrop.hidden = false;\n // Start on the first input, else the tabs, else the dialog itself.\n const firstTab = this.tabsEl.hidden ? null : this.tabButtons[0] ?? null;\n (this.bodyEl.querySelector<HTMLElement>(FOCUSABLE) ?? firstTab ?? this.modal).focus();\n }\n\n /** Close, keeping what was changed (as Ok does). */\n close(): void {\n this.finish(true);\n }\n\n isOpen(): boolean {\n return !this.backdrop.hidden;\n }\n\n destroy(): void {\n this.menu?.destroy();\n this.backdrop.remove();\n }\n\n /** Close with Ok (`keep`: a value still being typed applies) or Cancel (everything goes back). */\n private finish(keep: boolean): void {\n if (!this.target) return;\n if (keep) {\n const active = document.activeElement;\n if ((active instanceof HTMLInputElement || active instanceof HTMLSelectElement) && this.modal.contains(active)) {\n active.dispatchEvent(new Event('change'));\n }\n } else {\n this.revert();\n }\n // No target: a field's change on its way out (its blur) no longer applies.\n this.target = null;\n this.menu?.close();\n this.backdrop.hidden = true;\n this.callbacks.onClose();\n // Back to where the dialog was opened from (a legend row, the object tree).\n if (this.returnFocus?.isConnected) this.returnFocus.focus();\n this.returnFocus = null;\n }\n\n /** Everything as it was on opening: only what changed is sent back. */\n private revert(): void {\n const target = this.target;\n const initial = this.initial;\n if (!target || !initial) return;\n const id = target.instanceId;\n if (!same(this.currentParams(), initial.params)) this.callbacks.onApply(id, { ...initial.params });\n const was = initial.look;\n const style: IndicatorStyleConfig = {};\n if (!same(this.look.colors, was.colors)) style.colors = [...was.colors];\n if (this.look.lineWidth !== was.lineWidth) style.lineWidths = [was.lineWidth];\n if (!same(this.look.plots, was.plots)) style.plots = plotsBack(this.look.plots, was.plots, target.plots);\n if (Object.keys(style).length > 0) this.callbacks.onStyle?.(id, style);\n if (!same(this.look.levels, was.levels)) this.callbacks.onLevels?.(id, target.ownLevels ? [...was.levels] : null);\n if (this.look.scale !== was.scale && was.scale) this.callbacks.onScale?.(id, was.scale);\n if (!same(this.look.intervals, was.intervals)) this.callbacks.onIntervals?.(id, was.intervals);\n }\n\n private currentParams(): Record<string, IndicatorParamValue> {\n const target = this.target;\n if (!target) return {};\n return { ...target.defaults, ...target.params, ...this.draft } as Record<string, IndicatorParamValue>;\n }\n\n // --- Tabs ---\n\n private tabs(): Tab[] {\n const target = this.target;\n if (!target) return [];\n const lines = !!(target.plots?.length && target.colors && this.callbacks.onStyle);\n const levels = !!(target.levels && this.callbacks.onLevels);\n return [\n 'inputs',\n ...(lines || levels ? ['style' as const] : []),\n ...(this.callbacks.onIntervals ? ['visibility' as const] : []),\n ];\n }\n\n /** The tab buttons for this indicator; built on open, then only marked (focus stays put). */\n private renderTabs(): void {\n const tabs = this.tabs();\n this.tabsEl.hidden = tabs.length < 2;\n this.tabButtons = tabs.map((tab) => {\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.className = 'tcw-modal-tab';\n btn.id = `tcw-indi-tab-${this.uid}-${tab}`;\n btn.setAttribute('role', 'tab');\n btn.setAttribute('aria-controls', this.bodyEl.id);\n btn.dataset.tab = tab;\n btn.textContent = this.t(`indicatorSettings.tab.${tab}`);\n btn.addEventListener('click', () => this.selectTab(tab));\n return btn;\n });\n this.tabsEl.replaceChildren(...this.tabButtons);\n this.markTabs();\n }\n\n private markTabs(): void {\n for (const btn of this.tabButtons) {\n const selected = btn.dataset.tab === this.tab;\n btn.classList.toggle('tcw-active', selected);\n btn.setAttribute('aria-selected', String(selected));\n btn.tabIndex = selected ? 0 : -1;\n if (selected) this.bodyEl.setAttribute('aria-labelledby', btn.id);\n }\n }\n\n private selectTab(tab: Tab, focus = false): void {\n this.tab = tab;\n this.markTabs();\n this.renderBody();\n if (focus) this.tabButtons.find((b) => b.dataset.tab === tab)?.focus();\n }\n\n /** Arrow keys (the other way right to left), Home and End move between tabs. */\n private onTabKey(e: KeyboardEvent): void {\n const tabs = this.tabButtons.map((b) => b.dataset.tab as Tab);\n const at = tabs.indexOf(this.tab);\n const rtl = this.modal.closest('[dir]')?.getAttribute('dir') === 'rtl';\n const [forward, back] = rtl ? ['ArrowLeft', 'ArrowRight'] : ['ArrowRight', 'ArrowLeft'];\n const next = e.key === forward ? (at + 1) % tabs.length\n : e.key === back ? (at - 1 + tabs.length) % tabs.length\n : e.key === 'Home' ? 0\n : e.key === 'End' ? tabs.length - 1\n : -1;\n if (next < 0 || tabs.length === 0) return;\n e.preventDefault();\n this.selectTab(tabs[next], true);\n }\n\n /** Escape cancels; Tab stays inside the dialog. */\n private onKeyDown(e: KeyboardEvent): void {\n if (e.key === 'Escape') {\n e.preventDefault();\n e.stopPropagation();\n this.finish(false);\n return;\n }\n keepTabInside(e, this.modal);\n }\n\n private renderBody(): void {\n this.bodyEl.replaceChildren();\n const target = this.target;\n if (!target) return;\n if (this.tab === 'style') this.renderStyle(target);\n else if (this.tab === 'visibility') renderVisibilityTab(this.bodyEl, this.look.intervals, (next) => this.setIntervals(next), this.t);\n else this.renderInputs();\n }\n\n // --- Inputs ---\n\n private currentValue(key: string): IndicatorParamValue {\n if (key in this.draft) return this.draft[key];\n const cur = this.target?.params[key];\n const def = this.target?.defaults[key];\n return (cur ?? def) as IndicatorParamValue;\n }\n\n private setValue(key: string, value: IndicatorParamValue): void {\n if (!this.target) return;\n this.draft[key] = value;\n this.callbacks.onApply(this.target.instanceId, { ...this.draft });\n }\n\n private renderInputs(): void {\n const section = document.createElement('div');\n section.className = 'tcw-settings-section';\n const inputs = describeInputs({ defaultConfig: this.target!.defaults, inputs: this.target!.inputs });\n if (inputs.length === 0) section.appendChild(this.note(this.t('indicatorSettings.noInputs')));\n for (const input of inputs) section.appendChild(this.fieldRow(input));\n this.bodyEl.appendChild(section);\n }\n\n /** One input's row: its control follows the kind the indicator declares, or the one its default implies. */\n private fieldRow(input: IndicatorInputInfo): HTMLLabelElement {\n const key = input.name;\n const value = this.currentValue(key);\n let control: HTMLElement;\n switch (input.kind) {\n case 'source': control = this.sourceControl(key, String(value ?? 'close')); break;\n case 'choice': control = this.choiceControl(key, input.options ?? [], value); break;\n case 'boolean': control = toggleSwitch(Boolean(value), (v) => this.setValue(key, v)); break;\n case 'int':\n case 'number':\n control = numberInput(Number(value), { min: input.min, max: input.max, step: input.step, whole: input.kind === 'int' }, (v) => this.setValue(key, v));\n break;\n case 'color': control = colorInput(String(value), (v) => this.setValue(key, v)); break;\n default: control = this.textControl(key, String(value ?? ''));\n }\n return settingsRow(input.label ?? inputLabel(key, this.t) ?? titleCase(key), control);\n }\n\n private sourceControl(key: string, value: string): HTMLSelectElement {\n const select = document.createElement('select');\n select.className = 'tcw-indi-input tcw-indi-select';\n const prices = document.createElement('optgroup');\n prices.label = this.t('indicatorSettings.source.prices');\n for (const source of PRICE_SOURCES) prices.appendChild(this.option(source, source));\n select.appendChild(prices);\n const lines = this.target?.lineSources ?? [];\n if (lines.length) {\n const group = document.createElement('optgroup');\n group.label = this.t('indicatorSettings.source.indicators');\n for (const line of lines) group.appendChild(this.option(line.value, line.label));\n select.appendChild(group);\n }\n select.value = value;\n if (select.value !== value) select.value = 'close'; // a line that is gone\n select.addEventListener('change', () => this.setValue(key, select.value));\n return select;\n }\n\n private choiceControl(key: string, options: readonly (string | number)[], value: IndicatorParamValue): HTMLSelectElement {\n const select = document.createElement('select');\n select.className = 'tcw-indi-input tcw-indi-select';\n for (const opt of options) select.appendChild(this.option(String(opt), choiceLabel(key, opt, this.t) ?? String(opt)));\n select.value = String(value);\n select.addEventListener('change', () => {\n const picked = options.find((o) => String(o) === select.value);\n if (picked !== undefined) this.setValue(key, picked);\n });\n return select;\n }\n\n private textControl(key: string, value: string): HTMLInputElement {\n const input = document.createElement('input');\n input.type = 'text';\n input.className = 'tcw-indi-input';\n input.value = value;\n input.addEventListener('change', () => this.setValue(key, input.value));\n return input;\n }\n\n // --- Style and visibility ---\n\n private renderStyle(target: IndicatorSettingsTarget): void {\n const id = target.instanceId;\n // Once closed, nothing: a field's change on its way out (its blur after a Cancel) no longer applies.\n const open = () => this.target === target;\n const onStyle = (style: IndicatorStyleConfig) => open() && this.callbacks.onStyle?.(id, style);\n const onLevels = (levels: number[]) => open() && this.callbacks.onLevels?.(id, levels);\n const onScale = (scale: 'right' | 'left') => open() && this.callbacks.onScale?.(id, scale);\n renderStyleTab(this.bodyEl, {\n plots: target.colors && this.callbacks.onStyle ? target.plots ?? [] : [],\n colors: this.look.colors,\n plotStyles: this.look.plots,\n lineWidth: this.look.lineWidth,\n scale: target.scale && this.callbacks.onScale ? this.look.scale : undefined,\n levels: target.levels && this.callbacks.onLevels ? this.look.levels : undefined,\n }, {\n color: (index, value) => {\n if (!open()) return;\n const next = [...this.look.colors];\n while (next.length <= index) next.push(next[0] ?? value);\n next[index] = value;\n this.look.colors = next;\n onStyle({ colors: [...next] });\n },\n plot: (key, patch) => {\n if (!open()) return;\n this.look.plots = { ...this.look.plots, [key]: { ...this.look.plots[key], ...patch } };\n onStyle({ plots: { [key]: patch } });\n },\n lineWidth: (width) => {\n if (!open()) return;\n this.look.lineWidth = width;\n onStyle({ lineWidths: [width] });\n },\n scale: (scale) => {\n if (!open()) return;\n this.look.scale = scale;\n onScale(scale);\n },\n levels: (levels) => {\n if (!open()) return;\n this.look.levels = levels;\n onLevels([...levels]);\n },\n }, this.t, this.uid);\n }\n\n private setIntervals(intervals: IndicatorIntervals | null): void {\n if (!this.target) return;\n this.look.intervals = intervals;\n this.callbacks.onIntervals?.(this.target.instanceId, intervals);\n }\n\n // --- Defaults ---\n\n private openDefaults(): void {\n if (this.menu?.isOpen()) {\n this.menu.close();\n return;\n }\n this.menu ??= new WidgetContextMenu(this.modal, this.t('indicatorSettings.defaults'));\n const at = this.defaultsBtn.getBoundingClientRect();\n const box = this.modal.getBoundingClientRect();\n const entries = [\n { id: 'reset', label: this.t('indicatorSettings.resetSettings'), icon: 'reset' },\n ...(this.callbacks.onSaveDefault ? [{ id: 'save', label: this.t('indicatorSettings.saveAsDefault'), icon: 'save' }] : []),\n ];\n // From the button's top: in the footer there is no room below, so it opens above the button.\n this.menu.open(entries, at.left - box.left, at.top - box.top, (pick) => {\n if (pick === 'reset') this.resetAll();\n else this.saveDefault();\n }, this.defaultsBtn);\n }\n\n /** The inputs, the lines' own styles, the levels and the intervals back to the indicator's own. */\n private resetAll(): void {\n const target = this.target;\n if (!target) return;\n const id = target.instanceId;\n const params = { ...target.defaults } as Record<string, IndicatorParamValue>;\n this.draft = { ...params };\n this.callbacks.onApply(id, { ...params });\n if (Object.keys(this.look.plots).length > 0) {\n this.callbacks.onStyle?.(id, { plots: plotsBack(this.look.plots, {}, target.plots) });\n this.look.plots = {};\n }\n if (target.levels && this.callbacks.onLevels) {\n this.callbacks.onLevels(id, null);\n this.look.levels = [...(target.defaultLevels ?? [])];\n }\n if (this.look.intervals) this.setIntervals(null);\n this.renderBody();\n }\n\n /**\n * Keep what was chosen for the next ones of the kind: the inputs that differ\n * from the indicator's own (not a line of another indicator, which the next\n * one won't have), and of the look only what someone picked. Colours the\n * chart gave stay its to give, so a second one still gets a colour of its own.\n */\n private saveDefault(): void {\n const target = this.target;\n if (!target) return;\n const params = Object.fromEntries(Object.entries(this.currentParams()).filter(([key, value]) =>\n !same(value, target.defaults[key]) && !(typeof value === 'string' && parseIndicatorSource(value)))) as Record<string, IndicatorParamValue>;\n const style: IndicatorStyleConfig = {};\n if (!isSeriesPalette(this.look.colors)) style.colors = [...this.look.colors];\n if (this.look.lineWidth !== DEFAULT_LINE_WIDTH) style.lineWidths = [this.look.lineWidth];\n if (Object.keys(this.look.plots).length > 0) style.plots = copy(this.look.plots);\n this.callbacks.onSaveDefault?.(target.indicatorId, { params, style });\n }\n\n // --- Pieces ---\n\n private footerButton(act: string, className: string, onClick: () => void, text?: string): HTMLButtonElement {\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.className = className;\n btn.dataset.act = act;\n if (text) btn.textContent = text;\n btn.addEventListener('click', onClick);\n return btn;\n }\n\n private note(text: string): HTMLDivElement {\n const el = document.createElement('div');\n el.className = 'tcw-settings-label';\n el.textContent = text;\n return el;\n }\n\n private option(value: string, label: string): HTMLOptionElement {\n const opt = document.createElement('option');\n opt.value = value;\n opt.textContent = label;\n return opt;\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require(`./focusTrap-DrhhmG0V.cjs`),t=require(`./settingsControls-Tn8_vJJv.cjs`),n=require(`./i18n-D8MhJwUv.cjs`),r=require(`./indicatorInputWords-CdAL8a73.cjs`),i=require(`./indicatorSettingsTabs-B0liE3ST.cjs`);let a=require(`@tradecanvas/commons`),o=require(`@tradecanvas/core`);var s=1,c=1.5;function l(e,t){return{visible:!0,lineStyle:e?.find(e=>e.key===t)?.lineStyle??`solid`}}var u=(e,t)=>JSON.stringify(e)===JSON.stringify(t),d=e=>JSON.parse(JSON.stringify(e));function f(e){return e.replace(/_/g,` `).replace(/([a-z0-9])([A-Z])/g,`$1 $2`).replace(/^./,e=>e.toUpperCase())}function p(e,t,n){return Object.fromEntries([...new Set([...Object.keys(e),...Object.keys(t)])].map(e=>[e,{...l(n,e),...t[e]}]))}var m=class{backdrop;modal;bodyEl;titleEl;tabsEl;defaultsBtn;menu=null;callbacks;target=null;draft={};initial=null;look={colors:[],lineWidth:c,plots:{},levels:[],intervals:null};tab=`inputs`;tabButtons=[];returnFocus=null;uid=s++;t;constructor(t,r,i){this.callbacks=r,this.t=i??(e=>n.c[e]),this.backdrop=document.createElement(`div`),this.backdrop.className=`tcw-modal-backdrop`,this.backdrop.hidden=!0;let a=!1;this.backdrop.addEventListener(`pointerdown`,e=>{a=e.target===this.backdrop}),this.backdrop.addEventListener(`click`,e=>{e.target===this.backdrop&&a&&this.finish(!1),a=!1}),this.modal=document.createElement(`div`),this.modal.className=`tcw-modal tcw-modal-narrow tcw-indi`,this.modal.setAttribute(`role`,`dialog`),this.modal.setAttribute(`aria-modal`,`true`),this.modal.setAttribute(`aria-labelledby`,`tcw-indi-title-${this.uid}`),this.modal.tabIndex=-1,this.modal.addEventListener(`keydown`,e=>this.onKeyDown(e));let o=document.createElement(`div`);o.className=`tcw-modal-header`,this.titleEl=document.createElement(`h3`),this.titleEl.id=`tcw-indi-title-${this.uid}`;let s=document.createElement(`button`);s.type=`button`,s.className=`tcw-modal-close`,s.setAttribute(`aria-label`,this.t(`indicatorSettings.close`)),s.innerHTML=e.i(`x`,16),s.addEventListener(`click`,()=>this.finish(!1)),o.append(this.titleEl,s),this.tabsEl=document.createElement(`div`),this.tabsEl.className=`tcw-modal-tabs`,this.tabsEl.setAttribute(`role`,`tablist`),this.tabsEl.addEventListener(`keydown`,e=>this.onTabKey(e)),this.bodyEl=document.createElement(`div`),this.bodyEl.className=`tcw-modal-body`,this.bodyEl.id=`tcw-indi-panel-${this.uid}`,this.bodyEl.setAttribute(`role`,`tabpanel`),this.defaultsBtn=this.footerButton(`defaults`,`tcw-footer-menu`,()=>this.openDefaults()),this.modal.append(o,this.tabsEl,this.bodyEl,this.buildFooter()),this.backdrop.appendChild(this.modal),t.appendChild(this.backdrop)}buildFooter(){this.defaultsBtn.setAttribute(`aria-haspopup`,`menu`),this.defaultsBtn.setAttribute(`aria-expanded`,`false`),this.defaultsBtn.innerHTML=`<span></span>${e.i(`chevronDown`,12)}`,this.defaultsBtn.querySelector(`span`).textContent=this.t(`indicatorSettings.defaults`);let t=document.createElement(`div`);t.className=`tcw-footer-end`,t.append(this.footerButton(`cancel`,`tcw-footer-cancel`,()=>this.finish(!1),this.t(`common.cancel`)),this.footerButton(`ok`,`tcw-done-btn`,()=>this.finish(!0),this.t(`drawingSettings.ok`)));let n=document.createElement(`div`);return n.className=`tcw-modal-footer`,n.append(this.defaultsBtn,t),n}open(e){this.target&&this.finish(!0),this.backdrop.hidden&&(this.returnFocus=document.activeElement instanceof HTMLElement?document.activeElement:null),this.target=e,this.titleEl.textContent=this.t(`indicatorSettings.title`).replace(`{name}`,e.name),this.draft={},this.look={colors:[...e.colors??[]],lineWidth:e.lineWidth??c,plots:Object.fromEntries(Object.entries(e.plotStyles??{}).map(([e,t])=>[e,{...t}])),levels:[...e.levels??[]],scale:e.scale,intervals:e.intervals??null},this.initial={params:this.currentParams(),look:d(this.look)},this.tab=`inputs`,this.renderTabs(),this.renderBody(),this.backdrop.hidden=!1;let t=this.tabsEl.hidden?null:this.tabButtons[0]??null;(this.bodyEl.querySelector(`button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])`)??t??this.modal).focus()}close(){this.finish(!0)}isOpen(){return!this.backdrop.hidden}destroy(){this.menu?.destroy(),this.backdrop.remove()}finish(e){if(this.target){if(e){let e=document.activeElement;(e instanceof HTMLInputElement||e instanceof HTMLSelectElement)&&this.modal.contains(e)&&e.dispatchEvent(new Event(`change`))}else this.revert();this.target=null,this.menu?.close(),this.backdrop.hidden=!0,this.callbacks.onClose(),this.returnFocus?.isConnected&&this.returnFocus.focus(),this.returnFocus=null}}revert(){let e=this.target,t=this.initial;if(!e||!t)return;let n=e.instanceId;u(this.currentParams(),t.params)||this.callbacks.onApply(n,{...t.params});let r=t.look,i={};u(this.look.colors,r.colors)||(i.colors=[...r.colors]),this.look.lineWidth!==r.lineWidth&&(i.lineWidths=[r.lineWidth]),u(this.look.plots,r.plots)||(i.plots=p(this.look.plots,r.plots,e.plots)),Object.keys(i).length>0&&this.callbacks.onStyle?.(n,i),u(this.look.levels,r.levels)||this.callbacks.onLevels?.(n,e.ownLevels?[...r.levels]:null),this.look.scale!==r.scale&&r.scale&&this.callbacks.onScale?.(n,r.scale),u(this.look.intervals,r.intervals)||this.callbacks.onIntervals?.(n,r.intervals)}currentParams(){let e=this.target;return e?{...e.defaults,...e.params,...this.draft}:{}}tabs(){let e=this.target;if(!e)return[];let t=!!(e.plots?.length&&e.colors&&this.callbacks.onStyle),n=!!(e.levels&&this.callbacks.onLevels);return[`inputs`,...t||n?[`style`]:[],...this.callbacks.onIntervals?[`visibility`]:[]]}renderTabs(){let e=this.tabs();this.tabsEl.hidden=e.length<2,this.tabButtons=e.map(e=>{let t=document.createElement(`button`);return t.type=`button`,t.className=`tcw-modal-tab`,t.id=`tcw-indi-tab-${this.uid}-${e}`,t.setAttribute(`role`,`tab`),t.setAttribute(`aria-controls`,this.bodyEl.id),t.dataset.tab=e,t.textContent=this.t(`indicatorSettings.tab.${e}`),t.addEventListener(`click`,()=>this.selectTab(e)),t}),this.tabsEl.replaceChildren(...this.tabButtons),this.markTabs()}markTabs(){for(let e of this.tabButtons){let t=e.dataset.tab===this.tab;e.classList.toggle(`tcw-active`,t),e.setAttribute(`aria-selected`,String(t)),e.tabIndex=t?0:-1,t&&this.bodyEl.setAttribute(`aria-labelledby`,e.id)}}selectTab(e,t=!1){this.tab=e,this.markTabs(),this.renderBody(),t&&this.tabButtons.find(t=>t.dataset.tab===e)?.focus()}onTabKey(e){let t=this.tabButtons.map(e=>e.dataset.tab),n=t.indexOf(this.tab),[r,i]=this.modal.closest(`[dir]`)?.getAttribute(`dir`)===`rtl`?[`ArrowLeft`,`ArrowRight`]:[`ArrowRight`,`ArrowLeft`],a=e.key===r?(n+1)%t.length:e.key===i?(n-1+t.length)%t.length:e.key===`Home`?0:e.key===`End`?t.length-1:-1;a<0||t.length===0||(e.preventDefault(),this.selectTab(t[a],!0))}onKeyDown(t){if(t.key===`Escape`){t.preventDefault(),t.stopPropagation(),this.finish(!1);return}e.n(t,this.modal)}renderBody(){this.bodyEl.replaceChildren();let e=this.target;e&&(this.tab===`style`?this.renderStyle(e):this.tab===`visibility`?i.n(this.bodyEl,this.look.intervals,e=>this.setIntervals(e),this.t):this.renderInputs())}currentValue(e){if(e in this.draft)return this.draft[e];let t=this.target?.params[e],n=this.target?.defaults[e];return t??n}setValue(e,t){this.target&&(this.draft[e]=t,this.callbacks.onApply(this.target.instanceId,{...this.draft}))}renderInputs(){let e=document.createElement(`div`);e.className=`tcw-settings-section`;let t=(0,o.describeInputs)({defaultConfig:this.target.defaults,inputs:this.target.inputs});t.length===0&&e.appendChild(this.note(this.t(`indicatorSettings.noInputs`)));for(let n of t)e.appendChild(this.fieldRow(n));this.bodyEl.appendChild(e)}fieldRow(e){let n=e.name,i=this.currentValue(n),a;switch(e.kind){case`source`:a=this.sourceControl(n,String(i??`close`));break;case`choice`:a=this.choiceControl(n,e.options??[],i);break;case`boolean`:a=t.u(!!i,e=>this.setValue(n,e));break;case`int`:case`number`:a=t.i(Number(i),{min:e.min,max:e.max,step:e.step,whole:e.kind===`int`},e=>this.setValue(n,e));break;case`color`:a=t.r(String(i),e=>this.setValue(n,e));break;default:a=this.textControl(n,String(i??``))}return t.s(e.label??r.n(n,this.t)??f(n),a)}sourceControl(e,t){let n=document.createElement(`select`);n.className=`tcw-indi-input tcw-indi-select`;let r=document.createElement(`optgroup`);r.label=this.t(`indicatorSettings.source.prices`);for(let e of a.PRICE_SOURCES)r.appendChild(this.option(e,e));n.appendChild(r);let i=this.target?.lineSources??[];if(i.length){let e=document.createElement(`optgroup`);e.label=this.t(`indicatorSettings.source.indicators`);for(let t of i)e.appendChild(this.option(t.value,t.label));n.appendChild(e)}return n.value=t,n.value!==t&&(n.value=`close`),n.addEventListener(`change`,()=>this.setValue(e,n.value)),n}choiceControl(e,t,n){let i=document.createElement(`select`);i.className=`tcw-indi-input tcw-indi-select`;for(let n of t)i.appendChild(this.option(String(n),r.t(e,n,this.t)??String(n)));return i.value=String(n),i.addEventListener(`change`,()=>{let n=t.find(e=>String(e)===i.value);n!==void 0&&this.setValue(e,n)}),i}textControl(e,t){let n=document.createElement(`input`);return n.type=`text`,n.className=`tcw-indi-input`,n.value=t,n.addEventListener(`change`,()=>this.setValue(e,n.value)),n}renderStyle(e){let t=e.instanceId,n=()=>this.target===e,r=e=>n()&&this.callbacks.onStyle?.(t,e),a=e=>n()&&this.callbacks.onLevels?.(t,e),o=e=>n()&&this.callbacks.onScale?.(t,e);i.t(this.bodyEl,{plots:e.colors&&this.callbacks.onStyle?e.plots??[]:[],colors:this.look.colors,plotStyles:this.look.plots,lineWidth:this.look.lineWidth,scale:e.scale&&this.callbacks.onScale?this.look.scale:void 0,levels:e.levels&&this.callbacks.onLevels?this.look.levels:void 0},{color:(e,t)=>{if(!n())return;let i=[...this.look.colors];for(;i.length<=e;)i.push(i[0]??t);i[e]=t,this.look.colors=i,r({colors:[...i]})},plot:(e,t)=>{n()&&(this.look.plots={...this.look.plots,[e]:{...this.look.plots[e],...t}},r({plots:{[e]:t}}))},lineWidth:e=>{n()&&(this.look.lineWidth=e,r({lineWidths:[e]}))},scale:e=>{n()&&(this.look.scale=e,o(e))},levels:e=>{n()&&(this.look.levels=e,a([...e]))}},this.t,this.uid)}setIntervals(e){this.target&&(this.look.intervals=e,this.callbacks.onIntervals?.(this.target.instanceId,e))}openDefaults(){if(this.menu?.isOpen()){this.menu.close();return}this.menu??=new t.f(this.modal,this.t(`indicatorSettings.defaults`));let e=this.defaultsBtn.getBoundingClientRect(),n=this.modal.getBoundingClientRect(),r=[{id:`reset`,label:this.t(`indicatorSettings.resetSettings`),icon:`reset`},...this.callbacks.onSaveDefault?[{id:`save`,label:this.t(`indicatorSettings.saveAsDefault`),icon:`save`}]:[]];this.menu.open(r,e.left-n.left,e.top-n.top,e=>{e===`reset`?this.resetAll():this.saveDefault()},this.defaultsBtn)}resetAll(){let e=this.target;if(!e)return;let t=e.instanceId,n={...e.defaults};this.draft={...n},this.callbacks.onApply(t,{...n}),Object.keys(this.look.plots).length>0&&(this.callbacks.onStyle?.(t,{plots:p(this.look.plots,{},e.plots)}),this.look.plots={}),e.levels&&this.callbacks.onLevels&&(this.callbacks.onLevels(t,null),this.look.levels=[...e.defaultLevels??[]]),this.look.intervals&&this.setIntervals(null),this.renderBody()}saveDefault(){let e=this.target;if(!e)return;let t=Object.fromEntries(Object.entries(this.currentParams()).filter(([t,n])=>!u(n,e.defaults[t])&&!(typeof n==`string`&&(0,a.parseIndicatorSource)(n)))),n={};(0,o.isSeriesPalette)(this.look.colors)||(n.colors=[...this.look.colors]),this.look.lineWidth!==c&&(n.lineWidths=[this.look.lineWidth]),Object.keys(this.look.plots).length>0&&(n.plots=d(this.look.plots)),this.callbacks.onSaveDefault?.(e.indicatorId,{params:t,style:n})}footerButton(e,t,n,r){let i=document.createElement(`button`);return i.type=`button`,i.className=t,i.dataset.act=e,r&&(i.textContent=r),i.addEventListener(`click`,n),i}note(e){let t=document.createElement(`div`);return t.className=`tcw-settings-label`,t.textContent=e,t}option(e,t){let n=document.createElement(`option`);return n.value=e,n.textContent=t,n}};exports.WidgetIndicatorSettings=m;
|
|
2
|
-
//# sourceMappingURL=WidgetIndicatorSettings-
|
|
1
|
+
const e=require(`./focusTrap-DrhhmG0V.cjs`),t=require(`./settingsControls-Tn8_vJJv.cjs`),n=require(`./i18n-Q4mxL2mj.cjs`),r=require(`./indicatorInputWords-hVKLYuVB.cjs`),i=require(`./indicatorSettingsTabs-B0liE3ST.cjs`);let a=require(`@tradecanvas/commons`),o=require(`@tradecanvas/core`);var s=1,c=1.5;function l(e,t){return{visible:!0,lineStyle:e?.find(e=>e.key===t)?.lineStyle??`solid`}}var u=(e,t)=>JSON.stringify(e)===JSON.stringify(t),d=e=>JSON.parse(JSON.stringify(e));function f(e){return e.replace(/_/g,` `).replace(/([a-z0-9])([A-Z])/g,`$1 $2`).replace(/^./,e=>e.toUpperCase())}function p(e,t,n){return Object.fromEntries([...new Set([...Object.keys(e),...Object.keys(t)])].map(e=>[e,{...l(n,e),...t[e]}]))}var m=class{backdrop;modal;bodyEl;titleEl;tabsEl;defaultsBtn;menu=null;callbacks;target=null;draft={};initial=null;look={colors:[],lineWidth:c,plots:{},levels:[],intervals:null};tab=`inputs`;tabButtons=[];returnFocus=null;uid=s++;t;constructor(t,r,i){this.callbacks=r,this.t=i??(e=>n.c[e]),this.backdrop=document.createElement(`div`),this.backdrop.className=`tcw-modal-backdrop`,this.backdrop.hidden=!0;let a=!1;this.backdrop.addEventListener(`pointerdown`,e=>{a=e.target===this.backdrop}),this.backdrop.addEventListener(`click`,e=>{e.target===this.backdrop&&a&&this.finish(!1),a=!1}),this.modal=document.createElement(`div`),this.modal.className=`tcw-modal tcw-modal-narrow tcw-indi`,this.modal.setAttribute(`role`,`dialog`),this.modal.setAttribute(`aria-modal`,`true`),this.modal.setAttribute(`aria-labelledby`,`tcw-indi-title-${this.uid}`),this.modal.tabIndex=-1,this.modal.addEventListener(`keydown`,e=>this.onKeyDown(e));let o=document.createElement(`div`);o.className=`tcw-modal-header`,this.titleEl=document.createElement(`h3`),this.titleEl.id=`tcw-indi-title-${this.uid}`;let s=document.createElement(`button`);s.type=`button`,s.className=`tcw-modal-close`,s.setAttribute(`aria-label`,this.t(`indicatorSettings.close`)),s.innerHTML=e.i(`x`,16),s.addEventListener(`click`,()=>this.finish(!1)),o.append(this.titleEl,s),this.tabsEl=document.createElement(`div`),this.tabsEl.className=`tcw-modal-tabs`,this.tabsEl.setAttribute(`role`,`tablist`),this.tabsEl.addEventListener(`keydown`,e=>this.onTabKey(e)),this.bodyEl=document.createElement(`div`),this.bodyEl.className=`tcw-modal-body`,this.bodyEl.id=`tcw-indi-panel-${this.uid}`,this.bodyEl.setAttribute(`role`,`tabpanel`),this.defaultsBtn=this.footerButton(`defaults`,`tcw-footer-menu`,()=>this.openDefaults()),this.modal.append(o,this.tabsEl,this.bodyEl,this.buildFooter()),this.backdrop.appendChild(this.modal),t.appendChild(this.backdrop)}buildFooter(){this.defaultsBtn.setAttribute(`aria-haspopup`,`menu`),this.defaultsBtn.setAttribute(`aria-expanded`,`false`),this.defaultsBtn.innerHTML=`<span></span>${e.i(`chevronDown`,12)}`,this.defaultsBtn.querySelector(`span`).textContent=this.t(`indicatorSettings.defaults`);let t=document.createElement(`div`);t.className=`tcw-footer-end`,t.append(this.footerButton(`cancel`,`tcw-footer-cancel`,()=>this.finish(!1),this.t(`common.cancel`)),this.footerButton(`ok`,`tcw-done-btn`,()=>this.finish(!0),this.t(`drawingSettings.ok`)));let n=document.createElement(`div`);return n.className=`tcw-modal-footer`,n.append(this.defaultsBtn,t),n}open(e){this.target&&this.finish(!0),this.backdrop.hidden&&(this.returnFocus=document.activeElement instanceof HTMLElement?document.activeElement:null),this.target=e,this.titleEl.textContent=this.t(`indicatorSettings.title`).replace(`{name}`,e.name),this.draft={},this.look={colors:[...e.colors??[]],lineWidth:e.lineWidth??c,plots:Object.fromEntries(Object.entries(e.plotStyles??{}).map(([e,t])=>[e,{...t}])),levels:[...e.levels??[]],scale:e.scale,intervals:e.intervals??null},this.initial={params:this.currentParams(),look:d(this.look)},this.tab=`inputs`,this.renderTabs(),this.renderBody(),this.backdrop.hidden=!1;let t=this.tabsEl.hidden?null:this.tabButtons[0]??null;(this.bodyEl.querySelector(`button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])`)??t??this.modal).focus()}close(){this.finish(!0)}isOpen(){return!this.backdrop.hidden}destroy(){this.menu?.destroy(),this.backdrop.remove()}finish(e){if(this.target){if(e){let e=document.activeElement;(e instanceof HTMLInputElement||e instanceof HTMLSelectElement)&&this.modal.contains(e)&&e.dispatchEvent(new Event(`change`))}else this.revert();this.target=null,this.menu?.close(),this.backdrop.hidden=!0,this.callbacks.onClose(),this.returnFocus?.isConnected&&this.returnFocus.focus(),this.returnFocus=null}}revert(){let e=this.target,t=this.initial;if(!e||!t)return;let n=e.instanceId;u(this.currentParams(),t.params)||this.callbacks.onApply(n,{...t.params});let r=t.look,i={};u(this.look.colors,r.colors)||(i.colors=[...r.colors]),this.look.lineWidth!==r.lineWidth&&(i.lineWidths=[r.lineWidth]),u(this.look.plots,r.plots)||(i.plots=p(this.look.plots,r.plots,e.plots)),Object.keys(i).length>0&&this.callbacks.onStyle?.(n,i),u(this.look.levels,r.levels)||this.callbacks.onLevels?.(n,e.ownLevels?[...r.levels]:null),this.look.scale!==r.scale&&r.scale&&this.callbacks.onScale?.(n,r.scale),u(this.look.intervals,r.intervals)||this.callbacks.onIntervals?.(n,r.intervals)}currentParams(){let e=this.target;return e?{...e.defaults,...e.params,...this.draft}:{}}tabs(){let e=this.target;if(!e)return[];let t=!!(e.plots?.length&&e.colors&&this.callbacks.onStyle),n=!!(e.levels&&this.callbacks.onLevels);return[`inputs`,...t||n?[`style`]:[],...this.callbacks.onIntervals?[`visibility`]:[]]}renderTabs(){let e=this.tabs();this.tabsEl.hidden=e.length<2,this.tabButtons=e.map(e=>{let t=document.createElement(`button`);return t.type=`button`,t.className=`tcw-modal-tab`,t.id=`tcw-indi-tab-${this.uid}-${e}`,t.setAttribute(`role`,`tab`),t.setAttribute(`aria-controls`,this.bodyEl.id),t.dataset.tab=e,t.textContent=this.t(`indicatorSettings.tab.${e}`),t.addEventListener(`click`,()=>this.selectTab(e)),t}),this.tabsEl.replaceChildren(...this.tabButtons),this.markTabs()}markTabs(){for(let e of this.tabButtons){let t=e.dataset.tab===this.tab;e.classList.toggle(`tcw-active`,t),e.setAttribute(`aria-selected`,String(t)),e.tabIndex=t?0:-1,t&&this.bodyEl.setAttribute(`aria-labelledby`,e.id)}}selectTab(e,t=!1){this.tab=e,this.markTabs(),this.renderBody(),t&&this.tabButtons.find(t=>t.dataset.tab===e)?.focus()}onTabKey(e){let t=this.tabButtons.map(e=>e.dataset.tab),n=t.indexOf(this.tab),[r,i]=this.modal.closest(`[dir]`)?.getAttribute(`dir`)===`rtl`?[`ArrowLeft`,`ArrowRight`]:[`ArrowRight`,`ArrowLeft`],a=e.key===r?(n+1)%t.length:e.key===i?(n-1+t.length)%t.length:e.key===`Home`?0:e.key===`End`?t.length-1:-1;a<0||t.length===0||(e.preventDefault(),this.selectTab(t[a],!0))}onKeyDown(t){if(t.key===`Escape`){t.preventDefault(),t.stopPropagation(),this.finish(!1);return}e.n(t,this.modal)}renderBody(){this.bodyEl.replaceChildren();let e=this.target;e&&(this.tab===`style`?this.renderStyle(e):this.tab===`visibility`?i.n(this.bodyEl,this.look.intervals,e=>this.setIntervals(e),this.t):this.renderInputs())}currentValue(e){if(e in this.draft)return this.draft[e];let t=this.target?.params[e],n=this.target?.defaults[e];return t??n}setValue(e,t){this.target&&(this.draft[e]=t,this.callbacks.onApply(this.target.instanceId,{...this.draft}))}renderInputs(){let e=document.createElement(`div`);e.className=`tcw-settings-section`;let t=(0,o.describeInputs)({defaultConfig:this.target.defaults,inputs:this.target.inputs});t.length===0&&e.appendChild(this.note(this.t(`indicatorSettings.noInputs`)));for(let n of t)e.appendChild(this.fieldRow(n));this.bodyEl.appendChild(e)}fieldRow(e){let n=e.name,i=this.currentValue(n),a;switch(e.kind){case`source`:a=this.sourceControl(n,String(i??`close`));break;case`choice`:a=this.choiceControl(n,e.options??[],i);break;case`boolean`:a=t.u(!!i,e=>this.setValue(n,e));break;case`int`:case`number`:a=t.i(Number(i),{min:e.min,max:e.max,step:e.step,whole:e.kind===`int`},e=>this.setValue(n,e));break;case`color`:a=t.r(String(i),e=>this.setValue(n,e));break;default:a=this.textControl(n,String(i??``))}return t.s(e.label??r.n(n,this.t)??f(n),a)}sourceControl(e,t){let n=document.createElement(`select`);n.className=`tcw-indi-input tcw-indi-select`;let r=document.createElement(`optgroup`);r.label=this.t(`indicatorSettings.source.prices`);for(let e of a.PRICE_SOURCES)r.appendChild(this.option(e,e));n.appendChild(r);let i=this.target?.lineSources??[];if(i.length){let e=document.createElement(`optgroup`);e.label=this.t(`indicatorSettings.source.indicators`);for(let t of i)e.appendChild(this.option(t.value,t.label));n.appendChild(e)}return n.value=t,n.value!==t&&(n.value=`close`),n.addEventListener(`change`,()=>this.setValue(e,n.value)),n}choiceControl(e,t,n){let i=document.createElement(`select`);i.className=`tcw-indi-input tcw-indi-select`;for(let n of t)i.appendChild(this.option(String(n),r.t(e,n,this.t)??String(n)));return i.value=String(n),i.addEventListener(`change`,()=>{let n=t.find(e=>String(e)===i.value);n!==void 0&&this.setValue(e,n)}),i}textControl(e,t){let n=document.createElement(`input`);return n.type=`text`,n.className=`tcw-indi-input`,n.value=t,n.addEventListener(`change`,()=>this.setValue(e,n.value)),n}renderStyle(e){let t=e.instanceId,n=()=>this.target===e,r=e=>n()&&this.callbacks.onStyle?.(t,e),a=e=>n()&&this.callbacks.onLevels?.(t,e),o=e=>n()&&this.callbacks.onScale?.(t,e);i.t(this.bodyEl,{plots:e.colors&&this.callbacks.onStyle?e.plots??[]:[],colors:this.look.colors,plotStyles:this.look.plots,lineWidth:this.look.lineWidth,scale:e.scale&&this.callbacks.onScale?this.look.scale:void 0,levels:e.levels&&this.callbacks.onLevels?this.look.levels:void 0},{color:(e,t)=>{if(!n())return;let i=[...this.look.colors];for(;i.length<=e;)i.push(i[0]??t);i[e]=t,this.look.colors=i,r({colors:[...i]})},plot:(e,t)=>{n()&&(this.look.plots={...this.look.plots,[e]:{...this.look.plots[e],...t}},r({plots:{[e]:t}}))},lineWidth:e=>{n()&&(this.look.lineWidth=e,r({lineWidths:[e]}))},scale:e=>{n()&&(this.look.scale=e,o(e))},levels:e=>{n()&&(this.look.levels=e,a([...e]))}},this.t,this.uid)}setIntervals(e){this.target&&(this.look.intervals=e,this.callbacks.onIntervals?.(this.target.instanceId,e))}openDefaults(){if(this.menu?.isOpen()){this.menu.close();return}this.menu??=new t.f(this.modal,this.t(`indicatorSettings.defaults`));let e=this.defaultsBtn.getBoundingClientRect(),n=this.modal.getBoundingClientRect(),r=[{id:`reset`,label:this.t(`indicatorSettings.resetSettings`),icon:`reset`},...this.callbacks.onSaveDefault?[{id:`save`,label:this.t(`indicatorSettings.saveAsDefault`),icon:`save`}]:[]];this.menu.open(r,e.left-n.left,e.top-n.top,e=>{e===`reset`?this.resetAll():this.saveDefault()},this.defaultsBtn)}resetAll(){let e=this.target;if(!e)return;let t=e.instanceId,n={...e.defaults};this.draft={...n},this.callbacks.onApply(t,{...n}),Object.keys(this.look.plots).length>0&&(this.callbacks.onStyle?.(t,{plots:p(this.look.plots,{},e.plots)}),this.look.plots={}),e.levels&&this.callbacks.onLevels&&(this.callbacks.onLevels(t,null),this.look.levels=[...e.defaultLevels??[]]),this.look.intervals&&this.setIntervals(null),this.renderBody()}saveDefault(){let e=this.target;if(!e)return;let t=Object.fromEntries(Object.entries(this.currentParams()).filter(([t,n])=>!u(n,e.defaults[t])&&!(typeof n==`string`&&(0,a.parseIndicatorSource)(n)))),n={};(0,o.isSeriesPalette)(this.look.colors)||(n.colors=[...this.look.colors]),this.look.lineWidth!==c&&(n.lineWidths=[this.look.lineWidth]),Object.keys(this.look.plots).length>0&&(n.plots=d(this.look.plots)),this.callbacks.onSaveDefault?.(e.indicatorId,{params:t,style:n})}footerButton(e,t,n,r){let i=document.createElement(`button`);return i.type=`button`,i.className=t,i.dataset.act=e,r&&(i.textContent=r),i.addEventListener(`click`,n),i}note(e){let t=document.createElement(`div`);return t.className=`tcw-settings-label`,t.textContent=e,t}option(e,t){let n=document.createElement(`option`);return n.value=e,n.textContent=t,n}};exports.WidgetIndicatorSettings=m;
|
|
2
|
+
//# sourceMappingURL=WidgetIndicatorSettings-DRt-E8Y_.cjs.map
|
package/dist/{WidgetIndicatorSettings-GnMFEumO.cjs.map → WidgetIndicatorSettings-DRt-E8Y_.cjs.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WidgetIndicatorSettings-GnMFEumO.cjs","names":[],"sources":["../src/widget/WidgetIndicatorSettings.ts"],"sourcesContent":["import type { IndicatorInputSpec, IndicatorIntervals, IndicatorPlot, IndicatorPlotStyle, IndicatorStyleConfig } from '@tradecanvas/commons';\nimport { PRICE_SOURCES, parseIndicatorSource } from '@tradecanvas/commons';\nimport { describeInputs, isSeriesPalette, type IndicatorInputInfo } from '@tradecanvas/core';\nimport { FOCUSABLE, keepTabInside } from './focusTrap.js';\nimport { createIcon } from './icons.js';\nimport { EN_MESSAGES, type Translator } from './i18n.js';\nimport { WidgetContextMenu } from './WidgetContextMenu.js';\nimport { colorInput, numberInput, settingsRow, toggleSwitch } from './settingsControls.js';\nimport { renderStyleTab, renderVisibilityTab } from './indicatorSettingsTabs.js';\nimport { choiceLabel, inputLabel } from './indicatorInputWords.js';\n\nexport type IndicatorParamValue = number | string | boolean;\n\nexport interface IndicatorSettingsTarget {\n instanceId: string;\n /** The kind of indicator (`'rsi'`): what \"Save as default\" is for. */\n indicatorId: string;\n name: string;\n /** Default config — defines the editable keys and their types. */\n defaults: Record<string, unknown>;\n /** Current values, overlaid on defaults. */\n params: Record<string, unknown>;\n /** How parameters are edited (the descriptor's `inputs`). */\n inputs?: Readonly<Record<string, IndicatorInputSpec>>;\n /** Other indicators' lines a source can be: value `ind:<instanceId>:<key>`. */\n lineSources?: readonly { value: string; label: string }[];\n /** What it draws; with `colors`, the lines of the Style tab. */\n plots?: readonly IndicatorPlot[];\n colors?: readonly string[];\n lineWidth?: number;\n /** Each line's own dash and visibility, by its key. */\n plotStyles?: Readonly<Record<string, IndicatorPlotStyle>>;\n /** Its levels and its indicator's defaults; with them, the levels under the lines. */\n levels?: readonly number[];\n defaultLevels?: readonly number[];\n /** Whether `levels` are the instance's own; not: its indicator's defaults, which Cancel leaves it on. */\n ownLevels?: boolean;\n /** The price scale an overlay is on; set only for price-pane overlays. */\n scale?: 'right' | 'left';\n /** The intervals it shows on; unset or null: all of them. */\n intervals?: IndicatorIntervals | null;\n}\n\n/** What \"Save as default\" keeps: new indicators of the kind start with it. */\nexport interface IndicatorDefault {\n params: Record<string, IndicatorParamValue>;\n style: IndicatorStyleConfig;\n}\n\nexport interface IndicatorSettingsCallbacks {\n onApply: (instanceId: string, params: Record<string, IndicatorParamValue>) => void;\n onStyle?: (instanceId: string, style: IndicatorStyleConfig) => void;\n /** `null` restores the indicator's default levels. */\n onLevels?: (instanceId: string, levels: number[] | null) => void;\n /** Move an overlay to the left price scale or back to the price scale. */\n onScale?: (instanceId: string, scale: 'right' | 'left') => void;\n /** The intervals it shows on (`null`: all); with it, the Visibility tab. */\n onIntervals?: (instanceId: string, intervals: IndicatorIntervals | null) => void;\n /** \"Save as default\" in the Defaults menu. */\n onSaveDefault?: (indicatorId: string, saved: IndicatorDefault) => void;\n onClose: () => void;\n}\n\ntype Tab = 'inputs' | 'style' | 'visibility';\n\n/** What the dialog changes besides the inputs: as it was on opening, and as it is. */\ninterface Look {\n colors: string[];\n lineWidth: number;\n plots: Record<string, IndicatorPlotStyle>;\n levels: number[];\n scale?: 'right' | 'left';\n intervals: IndicatorIntervals | null;\n}\n\n/** Ids for the dialog's labelling (several widgets may share a page). */\nlet nextDialogId = 1;\n\nconst DEFAULT_LINE_WIDTH = 1.5;\n/** A line as it is with no style of its own: shown, and dashed as its indicator declares it. */\nfunction plainPlot(plots: readonly IndicatorPlot[] | undefined, key: string): IndicatorPlotStyle {\n return { visible: true, lineStyle: plots?.find((p) => p.key === key)?.lineStyle ?? 'solid' };\n}\n\nconst same = (a: unknown, b: unknown) => JSON.stringify(a) === JSON.stringify(b);\n/** A copy of plain values (numbers, strings, flags, lists of them). */\nconst copy = <T>(value: T): T => JSON.parse(JSON.stringify(value)) as T;\n\nfunction titleCase(key: string): string {\n // camelCase / snake_case → \"Title Case\"\n return key\n .replace(/_/g, ' ')\n .replace(/([a-z0-9])([A-Z])/g, '$1 $2')\n .replace(/^./, (c) => c.toUpperCase());\n}\n\n/** Each line put back as `to` has it; one `to` has no style for goes back to plain. */\nfunction plotsBack(\n from: Record<string, IndicatorPlotStyle>,\n to: Record<string, IndicatorPlotStyle>,\n plots: readonly IndicatorPlot[] | undefined,\n): Record<string, IndicatorPlotStyle> {\n return Object.fromEntries([...new Set([...Object.keys(from), ...Object.keys(to)])].map((key) => [key, { ...plainPlot(plots, key), ...to[key] }]));\n}\n\n/**\n * Per-indicator settings: its inputs; its style (each line shown or not, its\n * colours and dash, the line width, the scale, the levels); and the\n * intervals it shows on. Every edit shows at once; Cancel (or Escape) puts\n * it all back as it was, Ok keeps it. The Defaults menu resets it, or keeps\n * it for the next ones of its kind.\n */\nexport class WidgetIndicatorSettings {\n private backdrop: HTMLDivElement;\n private modal: HTMLDivElement;\n private bodyEl: HTMLDivElement;\n private titleEl: HTMLHeadingElement;\n private tabsEl: HTMLDivElement;\n private defaultsBtn: HTMLButtonElement;\n private menu: WidgetContextMenu | null = null;\n private callbacks: IndicatorSettingsCallbacks;\n private target: IndicatorSettingsTarget | null = null;\n private draft: Record<string, IndicatorParamValue> = {};\n private initial: { params: Record<string, IndicatorParamValue>; look: Look } | null = null;\n private look: Look = { colors: [], lineWidth: DEFAULT_LINE_WIDTH, plots: {}, levels: [], intervals: null };\n private tab: Tab = 'inputs';\n private tabButtons: HTMLButtonElement[] = [];\n private returnFocus: HTMLElement | null = null;\n private readonly uid = nextDialogId++;\n private readonly t: Translator;\n\n constructor(host: HTMLElement, callbacks: IndicatorSettingsCallbacks, t?: Translator) {\n this.callbacks = callbacks;\n this.t = t ?? ((key) => EN_MESSAGES[key]);\n\n this.backdrop = document.createElement('div');\n this.backdrop.className = 'tcw-modal-backdrop';\n this.backdrop.hidden = true;\n // A press that starts in the dialog (a slider dragged, text selected) and ends on the backdrop doesn't cancel it.\n let pressedBackdrop = false;\n this.backdrop.addEventListener('pointerdown', (e) => {\n pressedBackdrop = e.target === this.backdrop;\n });\n this.backdrop.addEventListener('click', (e) => {\n if (e.target === this.backdrop && pressedBackdrop) this.finish(false);\n pressedBackdrop = false;\n });\n\n this.modal = document.createElement('div');\n this.modal.className = 'tcw-modal tcw-modal-narrow tcw-indi';\n this.modal.setAttribute('role', 'dialog');\n this.modal.setAttribute('aria-modal', 'true');\n this.modal.setAttribute('aria-labelledby', `tcw-indi-title-${this.uid}`);\n // Focusable, so keys still reach it after a click on its blank parts.\n this.modal.tabIndex = -1;\n this.modal.addEventListener('keydown', (e) => this.onKeyDown(e));\n\n const header = document.createElement('div');\n header.className = 'tcw-modal-header';\n this.titleEl = document.createElement('h3');\n this.titleEl.id = `tcw-indi-title-${this.uid}`;\n const closeBtn = document.createElement('button');\n closeBtn.type = 'button';\n closeBtn.className = 'tcw-modal-close';\n closeBtn.setAttribute('aria-label', this.t('indicatorSettings.close'));\n closeBtn.innerHTML = createIcon('x', 16);\n closeBtn.addEventListener('click', () => this.finish(false));\n header.append(this.titleEl, closeBtn);\n\n this.tabsEl = document.createElement('div');\n this.tabsEl.className = 'tcw-modal-tabs';\n this.tabsEl.setAttribute('role', 'tablist');\n this.tabsEl.addEventListener('keydown', (e) => this.onTabKey(e));\n\n this.bodyEl = document.createElement('div');\n this.bodyEl.className = 'tcw-modal-body';\n this.bodyEl.id = `tcw-indi-panel-${this.uid}`;\n this.bodyEl.setAttribute('role', 'tabpanel');\n\n this.defaultsBtn = this.footerButton('defaults', 'tcw-footer-menu', () => this.openDefaults());\n this.modal.append(header, this.tabsEl, this.bodyEl, this.buildFooter());\n this.backdrop.appendChild(this.modal);\n host.appendChild(this.backdrop);\n }\n\n /** The Defaults menu on the left; Cancel and OK on the right. */\n private buildFooter(): HTMLDivElement {\n this.defaultsBtn.setAttribute('aria-haspopup', 'menu');\n this.defaultsBtn.setAttribute('aria-expanded', 'false');\n this.defaultsBtn.innerHTML = `<span></span>${createIcon('chevronDown', 12)}`;\n this.defaultsBtn.querySelector('span')!.textContent = this.t('indicatorSettings.defaults');\n const end = document.createElement('div');\n end.className = 'tcw-footer-end';\n end.append(\n this.footerButton('cancel', 'tcw-footer-cancel', () => this.finish(false), this.t('common.cancel')),\n this.footerButton('ok', 'tcw-done-btn', () => this.finish(true), this.t('drawingSettings.ok')),\n );\n const footer = document.createElement('div');\n footer.className = 'tcw-modal-footer';\n footer.append(this.defaultsBtn, end);\n return footer;\n }\n\n open(target: IndicatorSettingsTarget): void {\n if (this.target) this.finish(true);\n if (this.backdrop.hidden) this.returnFocus = document.activeElement instanceof HTMLElement ? document.activeElement : null;\n this.target = target;\n this.titleEl.textContent = this.t('indicatorSettings.title').replace('{name}', target.name);\n this.draft = {};\n this.look = {\n colors: [...(target.colors ?? [])],\n lineWidth: target.lineWidth ?? DEFAULT_LINE_WIDTH,\n plots: Object.fromEntries(Object.entries(target.plotStyles ?? {}).map(([key, plot]) => [key, { ...plot }])),\n levels: [...(target.levels ?? [])],\n scale: target.scale,\n intervals: target.intervals ?? null,\n };\n this.initial = { params: this.currentParams(), look: copy(this.look) };\n this.tab = 'inputs';\n this.renderTabs();\n this.renderBody();\n this.backdrop.hidden = false;\n // Start on the first input, else the tabs, else the dialog itself.\n const firstTab = this.tabsEl.hidden ? null : this.tabButtons[0] ?? null;\n (this.bodyEl.querySelector<HTMLElement>(FOCUSABLE) ?? firstTab ?? this.modal).focus();\n }\n\n /** Close, keeping what was changed (as Ok does). */\n close(): void {\n this.finish(true);\n }\n\n isOpen(): boolean {\n return !this.backdrop.hidden;\n }\n\n destroy(): void {\n this.menu?.destroy();\n this.backdrop.remove();\n }\n\n /** Close with Ok (`keep`: a value still being typed applies) or Cancel (everything goes back). */\n private finish(keep: boolean): void {\n if (!this.target) return;\n if (keep) {\n const active = document.activeElement;\n if ((active instanceof HTMLInputElement || active instanceof HTMLSelectElement) && this.modal.contains(active)) {\n active.dispatchEvent(new Event('change'));\n }\n } else {\n this.revert();\n }\n // No target: a field's change on its way out (its blur) no longer applies.\n this.target = null;\n this.menu?.close();\n this.backdrop.hidden = true;\n this.callbacks.onClose();\n // Back to where the dialog was opened from (a legend row, the object tree).\n if (this.returnFocus?.isConnected) this.returnFocus.focus();\n this.returnFocus = null;\n }\n\n /** Everything as it was on opening: only what changed is sent back. */\n private revert(): void {\n const target = this.target;\n const initial = this.initial;\n if (!target || !initial) return;\n const id = target.instanceId;\n if (!same(this.currentParams(), initial.params)) this.callbacks.onApply(id, { ...initial.params });\n const was = initial.look;\n const style: IndicatorStyleConfig = {};\n if (!same(this.look.colors, was.colors)) style.colors = [...was.colors];\n if (this.look.lineWidth !== was.lineWidth) style.lineWidths = [was.lineWidth];\n if (!same(this.look.plots, was.plots)) style.plots = plotsBack(this.look.plots, was.plots, target.plots);\n if (Object.keys(style).length > 0) this.callbacks.onStyle?.(id, style);\n if (!same(this.look.levels, was.levels)) this.callbacks.onLevels?.(id, target.ownLevels ? [...was.levels] : null);\n if (this.look.scale !== was.scale && was.scale) this.callbacks.onScale?.(id, was.scale);\n if (!same(this.look.intervals, was.intervals)) this.callbacks.onIntervals?.(id, was.intervals);\n }\n\n private currentParams(): Record<string, IndicatorParamValue> {\n const target = this.target;\n if (!target) return {};\n return { ...target.defaults, ...target.params, ...this.draft } as Record<string, IndicatorParamValue>;\n }\n\n // --- Tabs ---\n\n private tabs(): Tab[] {\n const target = this.target;\n if (!target) return [];\n const lines = !!(target.plots?.length && target.colors && this.callbacks.onStyle);\n const levels = !!(target.levels && this.callbacks.onLevels);\n return [\n 'inputs',\n ...(lines || levels ? ['style' as const] : []),\n ...(this.callbacks.onIntervals ? ['visibility' as const] : []),\n ];\n }\n\n /** The tab buttons for this indicator; built on open, then only marked (focus stays put). */\n private renderTabs(): void {\n const tabs = this.tabs();\n this.tabsEl.hidden = tabs.length < 2;\n this.tabButtons = tabs.map((tab) => {\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.className = 'tcw-modal-tab';\n btn.id = `tcw-indi-tab-${this.uid}-${tab}`;\n btn.setAttribute('role', 'tab');\n btn.setAttribute('aria-controls', this.bodyEl.id);\n btn.dataset.tab = tab;\n btn.textContent = this.t(`indicatorSettings.tab.${tab}`);\n btn.addEventListener('click', () => this.selectTab(tab));\n return btn;\n });\n this.tabsEl.replaceChildren(...this.tabButtons);\n this.markTabs();\n }\n\n private markTabs(): void {\n for (const btn of this.tabButtons) {\n const selected = btn.dataset.tab === this.tab;\n btn.classList.toggle('tcw-active', selected);\n btn.setAttribute('aria-selected', String(selected));\n btn.tabIndex = selected ? 0 : -1;\n if (selected) this.bodyEl.setAttribute('aria-labelledby', btn.id);\n }\n }\n\n private selectTab(tab: Tab, focus = false): void {\n this.tab = tab;\n this.markTabs();\n this.renderBody();\n if (focus) this.tabButtons.find((b) => b.dataset.tab === tab)?.focus();\n }\n\n /** Arrow keys (the other way right to left), Home and End move between tabs. */\n private onTabKey(e: KeyboardEvent): void {\n const tabs = this.tabButtons.map((b) => b.dataset.tab as Tab);\n const at = tabs.indexOf(this.tab);\n const rtl = this.modal.closest('[dir]')?.getAttribute('dir') === 'rtl';\n const [forward, back] = rtl ? ['ArrowLeft', 'ArrowRight'] : ['ArrowRight', 'ArrowLeft'];\n const next = e.key === forward ? (at + 1) % tabs.length\n : e.key === back ? (at - 1 + tabs.length) % tabs.length\n : e.key === 'Home' ? 0\n : e.key === 'End' ? tabs.length - 1\n : -1;\n if (next < 0 || tabs.length === 0) return;\n e.preventDefault();\n this.selectTab(tabs[next], true);\n }\n\n /** Escape cancels; Tab stays inside the dialog. */\n private onKeyDown(e: KeyboardEvent): void {\n if (e.key === 'Escape') {\n e.preventDefault();\n e.stopPropagation();\n this.finish(false);\n return;\n }\n keepTabInside(e, this.modal);\n }\n\n private renderBody(): void {\n this.bodyEl.replaceChildren();\n const target = this.target;\n if (!target) return;\n if (this.tab === 'style') this.renderStyle(target);\n else if (this.tab === 'visibility') renderVisibilityTab(this.bodyEl, this.look.intervals, (next) => this.setIntervals(next), this.t);\n else this.renderInputs();\n }\n\n // --- Inputs ---\n\n private currentValue(key: string): IndicatorParamValue {\n if (key in this.draft) return this.draft[key];\n const cur = this.target?.params[key];\n const def = this.target?.defaults[key];\n return (cur ?? def) as IndicatorParamValue;\n }\n\n private setValue(key: string, value: IndicatorParamValue): void {\n if (!this.target) return;\n this.draft[key] = value;\n this.callbacks.onApply(this.target.instanceId, { ...this.draft });\n }\n\n private renderInputs(): void {\n const section = document.createElement('div');\n section.className = 'tcw-settings-section';\n const inputs = describeInputs({ defaultConfig: this.target!.defaults, inputs: this.target!.inputs });\n if (inputs.length === 0) section.appendChild(this.note(this.t('indicatorSettings.noInputs')));\n for (const input of inputs) section.appendChild(this.fieldRow(input));\n this.bodyEl.appendChild(section);\n }\n\n /** One input's row: its control follows the kind the indicator declares, or the one its default implies. */\n private fieldRow(input: IndicatorInputInfo): HTMLLabelElement {\n const key = input.name;\n const value = this.currentValue(key);\n let control: HTMLElement;\n switch (input.kind) {\n case 'source': control = this.sourceControl(key, String(value ?? 'close')); break;\n case 'choice': control = this.choiceControl(key, input.options ?? [], value); break;\n case 'boolean': control = toggleSwitch(Boolean(value), (v) => this.setValue(key, v)); break;\n case 'int':\n case 'number':\n control = numberInput(Number(value), { min: input.min, max: input.max, step: input.step, whole: input.kind === 'int' }, (v) => this.setValue(key, v));\n break;\n case 'color': control = colorInput(String(value), (v) => this.setValue(key, v)); break;\n default: control = this.textControl(key, String(value ?? ''));\n }\n return settingsRow(input.label ?? inputLabel(key, this.t) ?? titleCase(key), control);\n }\n\n private sourceControl(key: string, value: string): HTMLSelectElement {\n const select = document.createElement('select');\n select.className = 'tcw-indi-input tcw-indi-select';\n const prices = document.createElement('optgroup');\n prices.label = this.t('indicatorSettings.source.prices');\n for (const source of PRICE_SOURCES) prices.appendChild(this.option(source, source));\n select.appendChild(prices);\n const lines = this.target?.lineSources ?? [];\n if (lines.length) {\n const group = document.createElement('optgroup');\n group.label = this.t('indicatorSettings.source.indicators');\n for (const line of lines) group.appendChild(this.option(line.value, line.label));\n select.appendChild(group);\n }\n select.value = value;\n if (select.value !== value) select.value = 'close'; // a line that is gone\n select.addEventListener('change', () => this.setValue(key, select.value));\n return select;\n }\n\n private choiceControl(key: string, options: readonly (string | number)[], value: IndicatorParamValue): HTMLSelectElement {\n const select = document.createElement('select');\n select.className = 'tcw-indi-input tcw-indi-select';\n for (const opt of options) select.appendChild(this.option(String(opt), choiceLabel(key, opt, this.t) ?? String(opt)));\n select.value = String(value);\n select.addEventListener('change', () => {\n const picked = options.find((o) => String(o) === select.value);\n if (picked !== undefined) this.setValue(key, picked);\n });\n return select;\n }\n\n private textControl(key: string, value: string): HTMLInputElement {\n const input = document.createElement('input');\n input.type = 'text';\n input.className = 'tcw-indi-input';\n input.value = value;\n input.addEventListener('change', () => this.setValue(key, input.value));\n return input;\n }\n\n // --- Style and visibility ---\n\n private renderStyle(target: IndicatorSettingsTarget): void {\n const id = target.instanceId;\n // Once closed, nothing: a field's change on its way out (its blur after a Cancel) no longer applies.\n const open = () => this.target === target;\n const onStyle = (style: IndicatorStyleConfig) => open() && this.callbacks.onStyle?.(id, style);\n const onLevels = (levels: number[]) => open() && this.callbacks.onLevels?.(id, levels);\n const onScale = (scale: 'right' | 'left') => open() && this.callbacks.onScale?.(id, scale);\n renderStyleTab(this.bodyEl, {\n plots: target.colors && this.callbacks.onStyle ? target.plots ?? [] : [],\n colors: this.look.colors,\n plotStyles: this.look.plots,\n lineWidth: this.look.lineWidth,\n scale: target.scale && this.callbacks.onScale ? this.look.scale : undefined,\n levels: target.levels && this.callbacks.onLevels ? this.look.levels : undefined,\n }, {\n color: (index, value) => {\n if (!open()) return;\n const next = [...this.look.colors];\n while (next.length <= index) next.push(next[0] ?? value);\n next[index] = value;\n this.look.colors = next;\n onStyle({ colors: [...next] });\n },\n plot: (key, patch) => {\n if (!open()) return;\n this.look.plots = { ...this.look.plots, [key]: { ...this.look.plots[key], ...patch } };\n onStyle({ plots: { [key]: patch } });\n },\n lineWidth: (width) => {\n if (!open()) return;\n this.look.lineWidth = width;\n onStyle({ lineWidths: [width] });\n },\n scale: (scale) => {\n if (!open()) return;\n this.look.scale = scale;\n onScale(scale);\n },\n levels: (levels) => {\n if (!open()) return;\n this.look.levels = levels;\n onLevels([...levels]);\n },\n }, this.t, this.uid);\n }\n\n private setIntervals(intervals: IndicatorIntervals | null): void {\n if (!this.target) return;\n this.look.intervals = intervals;\n this.callbacks.onIntervals?.(this.target.instanceId, intervals);\n }\n\n // --- Defaults ---\n\n private openDefaults(): void {\n if (this.menu?.isOpen()) {\n this.menu.close();\n return;\n }\n this.menu ??= new WidgetContextMenu(this.modal, this.t('indicatorSettings.defaults'));\n const at = this.defaultsBtn.getBoundingClientRect();\n const box = this.modal.getBoundingClientRect();\n const entries = [\n { id: 'reset', label: this.t('indicatorSettings.resetSettings'), icon: 'reset' },\n ...(this.callbacks.onSaveDefault ? [{ id: 'save', label: this.t('indicatorSettings.saveAsDefault'), icon: 'save' }] : []),\n ];\n // From the button's top: in the footer there is no room below, so it opens above the button.\n this.menu.open(entries, at.left - box.left, at.top - box.top, (pick) => {\n if (pick === 'reset') this.resetAll();\n else this.saveDefault();\n }, this.defaultsBtn);\n }\n\n /** The inputs, the lines' own styles, the levels and the intervals back to the indicator's own. */\n private resetAll(): void {\n const target = this.target;\n if (!target) return;\n const id = target.instanceId;\n const params = { ...target.defaults } as Record<string, IndicatorParamValue>;\n this.draft = { ...params };\n this.callbacks.onApply(id, { ...params });\n if (Object.keys(this.look.plots).length > 0) {\n this.callbacks.onStyle?.(id, { plots: plotsBack(this.look.plots, {}, target.plots) });\n this.look.plots = {};\n }\n if (target.levels && this.callbacks.onLevels) {\n this.callbacks.onLevels(id, null);\n this.look.levels = [...(target.defaultLevels ?? [])];\n }\n if (this.look.intervals) this.setIntervals(null);\n this.renderBody();\n }\n\n /**\n * Keep what was chosen for the next ones of the kind: the inputs that differ\n * from the indicator's own (not a line of another indicator, which the next\n * one won't have), and of the look only what someone picked. Colours the\n * chart gave stay its to give, so a second one still gets a colour of its own.\n */\n private saveDefault(): void {\n const target = this.target;\n if (!target) return;\n const params = Object.fromEntries(Object.entries(this.currentParams()).filter(([key, value]) =>\n !same(value, target.defaults[key]) && !(typeof value === 'string' && parseIndicatorSource(value)))) as Record<string, IndicatorParamValue>;\n const style: IndicatorStyleConfig = {};\n if (!isSeriesPalette(this.look.colors)) style.colors = [...this.look.colors];\n if (this.look.lineWidth !== DEFAULT_LINE_WIDTH) style.lineWidths = [this.look.lineWidth];\n if (Object.keys(this.look.plots).length > 0) style.plots = copy(this.look.plots);\n this.callbacks.onSaveDefault?.(target.indicatorId, { params, style });\n }\n\n // --- Pieces ---\n\n private footerButton(act: string, className: string, onClick: () => void, text?: string): HTMLButtonElement {\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.className = className;\n btn.dataset.act = act;\n if (text) btn.textContent = text;\n btn.addEventListener('click', onClick);\n return btn;\n }\n\n private note(text: string): HTMLDivElement {\n const el = document.createElement('div');\n el.className = 'tcw-settings-label';\n el.textContent = text;\n return el;\n }\n\n private option(value: string, label: string): HTMLOptionElement {\n const opt = document.createElement('option');\n opt.value = value;\n opt.textContent = label;\n return opt;\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"WidgetIndicatorSettings-DRt-E8Y_.cjs","names":[],"sources":["../src/widget/WidgetIndicatorSettings.ts"],"sourcesContent":["import type { IndicatorInputSpec, IndicatorIntervals, IndicatorPlot, IndicatorPlotStyle, IndicatorStyleConfig } from '@tradecanvas/commons';\nimport { PRICE_SOURCES, parseIndicatorSource } from '@tradecanvas/commons';\nimport { describeInputs, isSeriesPalette, type IndicatorInputInfo } from '@tradecanvas/core';\nimport { FOCUSABLE, keepTabInside } from './focusTrap.js';\nimport { createIcon } from './icons.js';\nimport { EN_MESSAGES, type Translator } from './i18n.js';\nimport { WidgetContextMenu } from './WidgetContextMenu.js';\nimport { colorInput, numberInput, settingsRow, toggleSwitch } from './settingsControls.js';\nimport { renderStyleTab, renderVisibilityTab } from './indicatorSettingsTabs.js';\nimport { choiceLabel, inputLabel } from './indicatorInputWords.js';\n\nexport type IndicatorParamValue = number | string | boolean;\n\nexport interface IndicatorSettingsTarget {\n instanceId: string;\n /** The kind of indicator (`'rsi'`): what \"Save as default\" is for. */\n indicatorId: string;\n name: string;\n /** Default config — defines the editable keys and their types. */\n defaults: Record<string, unknown>;\n /** Current values, overlaid on defaults. */\n params: Record<string, unknown>;\n /** How parameters are edited (the descriptor's `inputs`). */\n inputs?: Readonly<Record<string, IndicatorInputSpec>>;\n /** Other indicators' lines a source can be: value `ind:<instanceId>:<key>`. */\n lineSources?: readonly { value: string; label: string }[];\n /** What it draws; with `colors`, the lines of the Style tab. */\n plots?: readonly IndicatorPlot[];\n colors?: readonly string[];\n lineWidth?: number;\n /** Each line's own dash and visibility, by its key. */\n plotStyles?: Readonly<Record<string, IndicatorPlotStyle>>;\n /** Its levels and its indicator's defaults; with them, the levels under the lines. */\n levels?: readonly number[];\n defaultLevels?: readonly number[];\n /** Whether `levels` are the instance's own; not: its indicator's defaults, which Cancel leaves it on. */\n ownLevels?: boolean;\n /** The price scale an overlay is on; set only for price-pane overlays. */\n scale?: 'right' | 'left';\n /** The intervals it shows on; unset or null: all of them. */\n intervals?: IndicatorIntervals | null;\n}\n\n/** What \"Save as default\" keeps: new indicators of the kind start with it. */\nexport interface IndicatorDefault {\n params: Record<string, IndicatorParamValue>;\n style: IndicatorStyleConfig;\n}\n\nexport interface IndicatorSettingsCallbacks {\n onApply: (instanceId: string, params: Record<string, IndicatorParamValue>) => void;\n onStyle?: (instanceId: string, style: IndicatorStyleConfig) => void;\n /** `null` restores the indicator's default levels. */\n onLevels?: (instanceId: string, levels: number[] | null) => void;\n /** Move an overlay to the left price scale or back to the price scale. */\n onScale?: (instanceId: string, scale: 'right' | 'left') => void;\n /** The intervals it shows on (`null`: all); with it, the Visibility tab. */\n onIntervals?: (instanceId: string, intervals: IndicatorIntervals | null) => void;\n /** \"Save as default\" in the Defaults menu. */\n onSaveDefault?: (indicatorId: string, saved: IndicatorDefault) => void;\n onClose: () => void;\n}\n\ntype Tab = 'inputs' | 'style' | 'visibility';\n\n/** What the dialog changes besides the inputs: as it was on opening, and as it is. */\ninterface Look {\n colors: string[];\n lineWidth: number;\n plots: Record<string, IndicatorPlotStyle>;\n levels: number[];\n scale?: 'right' | 'left';\n intervals: IndicatorIntervals | null;\n}\n\n/** Ids for the dialog's labelling (several widgets may share a page). */\nlet nextDialogId = 1;\n\nconst DEFAULT_LINE_WIDTH = 1.5;\n/** A line as it is with no style of its own: shown, and dashed as its indicator declares it. */\nfunction plainPlot(plots: readonly IndicatorPlot[] | undefined, key: string): IndicatorPlotStyle {\n return { visible: true, lineStyle: plots?.find((p) => p.key === key)?.lineStyle ?? 'solid' };\n}\n\nconst same = (a: unknown, b: unknown) => JSON.stringify(a) === JSON.stringify(b);\n/** A copy of plain values (numbers, strings, flags, lists of them). */\nconst copy = <T>(value: T): T => JSON.parse(JSON.stringify(value)) as T;\n\nfunction titleCase(key: string): string {\n // camelCase / snake_case → \"Title Case\"\n return key\n .replace(/_/g, ' ')\n .replace(/([a-z0-9])([A-Z])/g, '$1 $2')\n .replace(/^./, (c) => c.toUpperCase());\n}\n\n/** Each line put back as `to` has it; one `to` has no style for goes back to plain. */\nfunction plotsBack(\n from: Record<string, IndicatorPlotStyle>,\n to: Record<string, IndicatorPlotStyle>,\n plots: readonly IndicatorPlot[] | undefined,\n): Record<string, IndicatorPlotStyle> {\n return Object.fromEntries([...new Set([...Object.keys(from), ...Object.keys(to)])].map((key) => [key, { ...plainPlot(plots, key), ...to[key] }]));\n}\n\n/**\n * Per-indicator settings: its inputs; its style (each line shown or not, its\n * colours and dash, the line width, the scale, the levels); and the\n * intervals it shows on. Every edit shows at once; Cancel (or Escape) puts\n * it all back as it was, Ok keeps it. The Defaults menu resets it, or keeps\n * it for the next ones of its kind.\n */\nexport class WidgetIndicatorSettings {\n private backdrop: HTMLDivElement;\n private modal: HTMLDivElement;\n private bodyEl: HTMLDivElement;\n private titleEl: HTMLHeadingElement;\n private tabsEl: HTMLDivElement;\n private defaultsBtn: HTMLButtonElement;\n private menu: WidgetContextMenu | null = null;\n private callbacks: IndicatorSettingsCallbacks;\n private target: IndicatorSettingsTarget | null = null;\n private draft: Record<string, IndicatorParamValue> = {};\n private initial: { params: Record<string, IndicatorParamValue>; look: Look } | null = null;\n private look: Look = { colors: [], lineWidth: DEFAULT_LINE_WIDTH, plots: {}, levels: [], intervals: null };\n private tab: Tab = 'inputs';\n private tabButtons: HTMLButtonElement[] = [];\n private returnFocus: HTMLElement | null = null;\n private readonly uid = nextDialogId++;\n private readonly t: Translator;\n\n constructor(host: HTMLElement, callbacks: IndicatorSettingsCallbacks, t?: Translator) {\n this.callbacks = callbacks;\n this.t = t ?? ((key) => EN_MESSAGES[key]);\n\n this.backdrop = document.createElement('div');\n this.backdrop.className = 'tcw-modal-backdrop';\n this.backdrop.hidden = true;\n // A press that starts in the dialog (a slider dragged, text selected) and ends on the backdrop doesn't cancel it.\n let pressedBackdrop = false;\n this.backdrop.addEventListener('pointerdown', (e) => {\n pressedBackdrop = e.target === this.backdrop;\n });\n this.backdrop.addEventListener('click', (e) => {\n if (e.target === this.backdrop && pressedBackdrop) this.finish(false);\n pressedBackdrop = false;\n });\n\n this.modal = document.createElement('div');\n this.modal.className = 'tcw-modal tcw-modal-narrow tcw-indi';\n this.modal.setAttribute('role', 'dialog');\n this.modal.setAttribute('aria-modal', 'true');\n this.modal.setAttribute('aria-labelledby', `tcw-indi-title-${this.uid}`);\n // Focusable, so keys still reach it after a click on its blank parts.\n this.modal.tabIndex = -1;\n this.modal.addEventListener('keydown', (e) => this.onKeyDown(e));\n\n const header = document.createElement('div');\n header.className = 'tcw-modal-header';\n this.titleEl = document.createElement('h3');\n this.titleEl.id = `tcw-indi-title-${this.uid}`;\n const closeBtn = document.createElement('button');\n closeBtn.type = 'button';\n closeBtn.className = 'tcw-modal-close';\n closeBtn.setAttribute('aria-label', this.t('indicatorSettings.close'));\n closeBtn.innerHTML = createIcon('x', 16);\n closeBtn.addEventListener('click', () => this.finish(false));\n header.append(this.titleEl, closeBtn);\n\n this.tabsEl = document.createElement('div');\n this.tabsEl.className = 'tcw-modal-tabs';\n this.tabsEl.setAttribute('role', 'tablist');\n this.tabsEl.addEventListener('keydown', (e) => this.onTabKey(e));\n\n this.bodyEl = document.createElement('div');\n this.bodyEl.className = 'tcw-modal-body';\n this.bodyEl.id = `tcw-indi-panel-${this.uid}`;\n this.bodyEl.setAttribute('role', 'tabpanel');\n\n this.defaultsBtn = this.footerButton('defaults', 'tcw-footer-menu', () => this.openDefaults());\n this.modal.append(header, this.tabsEl, this.bodyEl, this.buildFooter());\n this.backdrop.appendChild(this.modal);\n host.appendChild(this.backdrop);\n }\n\n /** The Defaults menu on the left; Cancel and OK on the right. */\n private buildFooter(): HTMLDivElement {\n this.defaultsBtn.setAttribute('aria-haspopup', 'menu');\n this.defaultsBtn.setAttribute('aria-expanded', 'false');\n this.defaultsBtn.innerHTML = `<span></span>${createIcon('chevronDown', 12)}`;\n this.defaultsBtn.querySelector('span')!.textContent = this.t('indicatorSettings.defaults');\n const end = document.createElement('div');\n end.className = 'tcw-footer-end';\n end.append(\n this.footerButton('cancel', 'tcw-footer-cancel', () => this.finish(false), this.t('common.cancel')),\n this.footerButton('ok', 'tcw-done-btn', () => this.finish(true), this.t('drawingSettings.ok')),\n );\n const footer = document.createElement('div');\n footer.className = 'tcw-modal-footer';\n footer.append(this.defaultsBtn, end);\n return footer;\n }\n\n open(target: IndicatorSettingsTarget): void {\n if (this.target) this.finish(true);\n if (this.backdrop.hidden) this.returnFocus = document.activeElement instanceof HTMLElement ? document.activeElement : null;\n this.target = target;\n this.titleEl.textContent = this.t('indicatorSettings.title').replace('{name}', target.name);\n this.draft = {};\n this.look = {\n colors: [...(target.colors ?? [])],\n lineWidth: target.lineWidth ?? DEFAULT_LINE_WIDTH,\n plots: Object.fromEntries(Object.entries(target.plotStyles ?? {}).map(([key, plot]) => [key, { ...plot }])),\n levels: [...(target.levels ?? [])],\n scale: target.scale,\n intervals: target.intervals ?? null,\n };\n this.initial = { params: this.currentParams(), look: copy(this.look) };\n this.tab = 'inputs';\n this.renderTabs();\n this.renderBody();\n this.backdrop.hidden = false;\n // Start on the first input, else the tabs, else the dialog itself.\n const firstTab = this.tabsEl.hidden ? null : this.tabButtons[0] ?? null;\n (this.bodyEl.querySelector<HTMLElement>(FOCUSABLE) ?? firstTab ?? this.modal).focus();\n }\n\n /** Close, keeping what was changed (as Ok does). */\n close(): void {\n this.finish(true);\n }\n\n isOpen(): boolean {\n return !this.backdrop.hidden;\n }\n\n destroy(): void {\n this.menu?.destroy();\n this.backdrop.remove();\n }\n\n /** Close with Ok (`keep`: a value still being typed applies) or Cancel (everything goes back). */\n private finish(keep: boolean): void {\n if (!this.target) return;\n if (keep) {\n const active = document.activeElement;\n if ((active instanceof HTMLInputElement || active instanceof HTMLSelectElement) && this.modal.contains(active)) {\n active.dispatchEvent(new Event('change'));\n }\n } else {\n this.revert();\n }\n // No target: a field's change on its way out (its blur) no longer applies.\n this.target = null;\n this.menu?.close();\n this.backdrop.hidden = true;\n this.callbacks.onClose();\n // Back to where the dialog was opened from (a legend row, the object tree).\n if (this.returnFocus?.isConnected) this.returnFocus.focus();\n this.returnFocus = null;\n }\n\n /** Everything as it was on opening: only what changed is sent back. */\n private revert(): void {\n const target = this.target;\n const initial = this.initial;\n if (!target || !initial) return;\n const id = target.instanceId;\n if (!same(this.currentParams(), initial.params)) this.callbacks.onApply(id, { ...initial.params });\n const was = initial.look;\n const style: IndicatorStyleConfig = {};\n if (!same(this.look.colors, was.colors)) style.colors = [...was.colors];\n if (this.look.lineWidth !== was.lineWidth) style.lineWidths = [was.lineWidth];\n if (!same(this.look.plots, was.plots)) style.plots = plotsBack(this.look.plots, was.plots, target.plots);\n if (Object.keys(style).length > 0) this.callbacks.onStyle?.(id, style);\n if (!same(this.look.levels, was.levels)) this.callbacks.onLevels?.(id, target.ownLevels ? [...was.levels] : null);\n if (this.look.scale !== was.scale && was.scale) this.callbacks.onScale?.(id, was.scale);\n if (!same(this.look.intervals, was.intervals)) this.callbacks.onIntervals?.(id, was.intervals);\n }\n\n private currentParams(): Record<string, IndicatorParamValue> {\n const target = this.target;\n if (!target) return {};\n return { ...target.defaults, ...target.params, ...this.draft } as Record<string, IndicatorParamValue>;\n }\n\n // --- Tabs ---\n\n private tabs(): Tab[] {\n const target = this.target;\n if (!target) return [];\n const lines = !!(target.plots?.length && target.colors && this.callbacks.onStyle);\n const levels = !!(target.levels && this.callbacks.onLevels);\n return [\n 'inputs',\n ...(lines || levels ? ['style' as const] : []),\n ...(this.callbacks.onIntervals ? ['visibility' as const] : []),\n ];\n }\n\n /** The tab buttons for this indicator; built on open, then only marked (focus stays put). */\n private renderTabs(): void {\n const tabs = this.tabs();\n this.tabsEl.hidden = tabs.length < 2;\n this.tabButtons = tabs.map((tab) => {\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.className = 'tcw-modal-tab';\n btn.id = `tcw-indi-tab-${this.uid}-${tab}`;\n btn.setAttribute('role', 'tab');\n btn.setAttribute('aria-controls', this.bodyEl.id);\n btn.dataset.tab = tab;\n btn.textContent = this.t(`indicatorSettings.tab.${tab}`);\n btn.addEventListener('click', () => this.selectTab(tab));\n return btn;\n });\n this.tabsEl.replaceChildren(...this.tabButtons);\n this.markTabs();\n }\n\n private markTabs(): void {\n for (const btn of this.tabButtons) {\n const selected = btn.dataset.tab === this.tab;\n btn.classList.toggle('tcw-active', selected);\n btn.setAttribute('aria-selected', String(selected));\n btn.tabIndex = selected ? 0 : -1;\n if (selected) this.bodyEl.setAttribute('aria-labelledby', btn.id);\n }\n }\n\n private selectTab(tab: Tab, focus = false): void {\n this.tab = tab;\n this.markTabs();\n this.renderBody();\n if (focus) this.tabButtons.find((b) => b.dataset.tab === tab)?.focus();\n }\n\n /** Arrow keys (the other way right to left), Home and End move between tabs. */\n private onTabKey(e: KeyboardEvent): void {\n const tabs = this.tabButtons.map((b) => b.dataset.tab as Tab);\n const at = tabs.indexOf(this.tab);\n const rtl = this.modal.closest('[dir]')?.getAttribute('dir') === 'rtl';\n const [forward, back] = rtl ? ['ArrowLeft', 'ArrowRight'] : ['ArrowRight', 'ArrowLeft'];\n const next = e.key === forward ? (at + 1) % tabs.length\n : e.key === back ? (at - 1 + tabs.length) % tabs.length\n : e.key === 'Home' ? 0\n : e.key === 'End' ? tabs.length - 1\n : -1;\n if (next < 0 || tabs.length === 0) return;\n e.preventDefault();\n this.selectTab(tabs[next], true);\n }\n\n /** Escape cancels; Tab stays inside the dialog. */\n private onKeyDown(e: KeyboardEvent): void {\n if (e.key === 'Escape') {\n e.preventDefault();\n e.stopPropagation();\n this.finish(false);\n return;\n }\n keepTabInside(e, this.modal);\n }\n\n private renderBody(): void {\n this.bodyEl.replaceChildren();\n const target = this.target;\n if (!target) return;\n if (this.tab === 'style') this.renderStyle(target);\n else if (this.tab === 'visibility') renderVisibilityTab(this.bodyEl, this.look.intervals, (next) => this.setIntervals(next), this.t);\n else this.renderInputs();\n }\n\n // --- Inputs ---\n\n private currentValue(key: string): IndicatorParamValue {\n if (key in this.draft) return this.draft[key];\n const cur = this.target?.params[key];\n const def = this.target?.defaults[key];\n return (cur ?? def) as IndicatorParamValue;\n }\n\n private setValue(key: string, value: IndicatorParamValue): void {\n if (!this.target) return;\n this.draft[key] = value;\n this.callbacks.onApply(this.target.instanceId, { ...this.draft });\n }\n\n private renderInputs(): void {\n const section = document.createElement('div');\n section.className = 'tcw-settings-section';\n const inputs = describeInputs({ defaultConfig: this.target!.defaults, inputs: this.target!.inputs });\n if (inputs.length === 0) section.appendChild(this.note(this.t('indicatorSettings.noInputs')));\n for (const input of inputs) section.appendChild(this.fieldRow(input));\n this.bodyEl.appendChild(section);\n }\n\n /** One input's row: its control follows the kind the indicator declares, or the one its default implies. */\n private fieldRow(input: IndicatorInputInfo): HTMLLabelElement {\n const key = input.name;\n const value = this.currentValue(key);\n let control: HTMLElement;\n switch (input.kind) {\n case 'source': control = this.sourceControl(key, String(value ?? 'close')); break;\n case 'choice': control = this.choiceControl(key, input.options ?? [], value); break;\n case 'boolean': control = toggleSwitch(Boolean(value), (v) => this.setValue(key, v)); break;\n case 'int':\n case 'number':\n control = numberInput(Number(value), { min: input.min, max: input.max, step: input.step, whole: input.kind === 'int' }, (v) => this.setValue(key, v));\n break;\n case 'color': control = colorInput(String(value), (v) => this.setValue(key, v)); break;\n default: control = this.textControl(key, String(value ?? ''));\n }\n return settingsRow(input.label ?? inputLabel(key, this.t) ?? titleCase(key), control);\n }\n\n private sourceControl(key: string, value: string): HTMLSelectElement {\n const select = document.createElement('select');\n select.className = 'tcw-indi-input tcw-indi-select';\n const prices = document.createElement('optgroup');\n prices.label = this.t('indicatorSettings.source.prices');\n for (const source of PRICE_SOURCES) prices.appendChild(this.option(source, source));\n select.appendChild(prices);\n const lines = this.target?.lineSources ?? [];\n if (lines.length) {\n const group = document.createElement('optgroup');\n group.label = this.t('indicatorSettings.source.indicators');\n for (const line of lines) group.appendChild(this.option(line.value, line.label));\n select.appendChild(group);\n }\n select.value = value;\n if (select.value !== value) select.value = 'close'; // a line that is gone\n select.addEventListener('change', () => this.setValue(key, select.value));\n return select;\n }\n\n private choiceControl(key: string, options: readonly (string | number)[], value: IndicatorParamValue): HTMLSelectElement {\n const select = document.createElement('select');\n select.className = 'tcw-indi-input tcw-indi-select';\n for (const opt of options) select.appendChild(this.option(String(opt), choiceLabel(key, opt, this.t) ?? String(opt)));\n select.value = String(value);\n select.addEventListener('change', () => {\n const picked = options.find((o) => String(o) === select.value);\n if (picked !== undefined) this.setValue(key, picked);\n });\n return select;\n }\n\n private textControl(key: string, value: string): HTMLInputElement {\n const input = document.createElement('input');\n input.type = 'text';\n input.className = 'tcw-indi-input';\n input.value = value;\n input.addEventListener('change', () => this.setValue(key, input.value));\n return input;\n }\n\n // --- Style and visibility ---\n\n private renderStyle(target: IndicatorSettingsTarget): void {\n const id = target.instanceId;\n // Once closed, nothing: a field's change on its way out (its blur after a Cancel) no longer applies.\n const open = () => this.target === target;\n const onStyle = (style: IndicatorStyleConfig) => open() && this.callbacks.onStyle?.(id, style);\n const onLevels = (levels: number[]) => open() && this.callbacks.onLevels?.(id, levels);\n const onScale = (scale: 'right' | 'left') => open() && this.callbacks.onScale?.(id, scale);\n renderStyleTab(this.bodyEl, {\n plots: target.colors && this.callbacks.onStyle ? target.plots ?? [] : [],\n colors: this.look.colors,\n plotStyles: this.look.plots,\n lineWidth: this.look.lineWidth,\n scale: target.scale && this.callbacks.onScale ? this.look.scale : undefined,\n levels: target.levels && this.callbacks.onLevels ? this.look.levels : undefined,\n }, {\n color: (index, value) => {\n if (!open()) return;\n const next = [...this.look.colors];\n while (next.length <= index) next.push(next[0] ?? value);\n next[index] = value;\n this.look.colors = next;\n onStyle({ colors: [...next] });\n },\n plot: (key, patch) => {\n if (!open()) return;\n this.look.plots = { ...this.look.plots, [key]: { ...this.look.plots[key], ...patch } };\n onStyle({ plots: { [key]: patch } });\n },\n lineWidth: (width) => {\n if (!open()) return;\n this.look.lineWidth = width;\n onStyle({ lineWidths: [width] });\n },\n scale: (scale) => {\n if (!open()) return;\n this.look.scale = scale;\n onScale(scale);\n },\n levels: (levels) => {\n if (!open()) return;\n this.look.levels = levels;\n onLevels([...levels]);\n },\n }, this.t, this.uid);\n }\n\n private setIntervals(intervals: IndicatorIntervals | null): void {\n if (!this.target) return;\n this.look.intervals = intervals;\n this.callbacks.onIntervals?.(this.target.instanceId, intervals);\n }\n\n // --- Defaults ---\n\n private openDefaults(): void {\n if (this.menu?.isOpen()) {\n this.menu.close();\n return;\n }\n this.menu ??= new WidgetContextMenu(this.modal, this.t('indicatorSettings.defaults'));\n const at = this.defaultsBtn.getBoundingClientRect();\n const box = this.modal.getBoundingClientRect();\n const entries = [\n { id: 'reset', label: this.t('indicatorSettings.resetSettings'), icon: 'reset' },\n ...(this.callbacks.onSaveDefault ? [{ id: 'save', label: this.t('indicatorSettings.saveAsDefault'), icon: 'save' }] : []),\n ];\n // From the button's top: in the footer there is no room below, so it opens above the button.\n this.menu.open(entries, at.left - box.left, at.top - box.top, (pick) => {\n if (pick === 'reset') this.resetAll();\n else this.saveDefault();\n }, this.defaultsBtn);\n }\n\n /** The inputs, the lines' own styles, the levels and the intervals back to the indicator's own. */\n private resetAll(): void {\n const target = this.target;\n if (!target) return;\n const id = target.instanceId;\n const params = { ...target.defaults } as Record<string, IndicatorParamValue>;\n this.draft = { ...params };\n this.callbacks.onApply(id, { ...params });\n if (Object.keys(this.look.plots).length > 0) {\n this.callbacks.onStyle?.(id, { plots: plotsBack(this.look.plots, {}, target.plots) });\n this.look.plots = {};\n }\n if (target.levels && this.callbacks.onLevels) {\n this.callbacks.onLevels(id, null);\n this.look.levels = [...(target.defaultLevels ?? [])];\n }\n if (this.look.intervals) this.setIntervals(null);\n this.renderBody();\n }\n\n /**\n * Keep what was chosen for the next ones of the kind: the inputs that differ\n * from the indicator's own (not a line of another indicator, which the next\n * one won't have), and of the look only what someone picked. Colours the\n * chart gave stay its to give, so a second one still gets a colour of its own.\n */\n private saveDefault(): void {\n const target = this.target;\n if (!target) return;\n const params = Object.fromEntries(Object.entries(this.currentParams()).filter(([key, value]) =>\n !same(value, target.defaults[key]) && !(typeof value === 'string' && parseIndicatorSource(value)))) as Record<string, IndicatorParamValue>;\n const style: IndicatorStyleConfig = {};\n if (!isSeriesPalette(this.look.colors)) style.colors = [...this.look.colors];\n if (this.look.lineWidth !== DEFAULT_LINE_WIDTH) style.lineWidths = [this.look.lineWidth];\n if (Object.keys(this.look.plots).length > 0) style.plots = copy(this.look.plots);\n this.callbacks.onSaveDefault?.(target.indicatorId, { params, style });\n }\n\n // --- Pieces ---\n\n private footerButton(act: string, className: string, onClick: () => void, text?: string): HTMLButtonElement {\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.className = className;\n btn.dataset.act = act;\n if (text) btn.textContent = text;\n btn.addEventListener('click', onClick);\n return btn;\n }\n\n private note(text: string): HTMLDivElement {\n const el = document.createElement('div');\n el.className = 'tcw-settings-label';\n el.textContent = text;\n return el;\n }\n\n private option(value: string, label: string): HTMLOptionElement {\n const opt = document.createElement('option');\n opt.value = value;\n opt.textContent = label;\n return opt;\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { a as e, c as t, d as n, f as r, i, m as a, o, p as s, r as c, s as l, t as u, u as d, y as f } from "./widgetLocales-BX7VRdl5.js";
|
|
2
2
|
import { i as p, n as m, r as h, t as g } from "./focusTrap-Br9Zfje0.js";
|
|
3
3
|
import { f as _, l as v } from "./settingsControls-DIjzCFHI.js";
|
|
4
|
-
import { r as y } from "./i18n-
|
|
4
|
+
import { r as y } from "./i18n-COYPJUtF.js";
|
|
5
5
|
import { CHART_STYLE_KEYS as b } from "@tradecanvas/commons";
|
|
6
6
|
//#region src/widget/settingsRows.ts
|
|
7
7
|
var x = 0, S = 0, C = 20;
|
|
@@ -569,4 +569,4 @@ function V(e) {
|
|
|
569
569
|
//#endregion
|
|
570
570
|
export { L as WidgetSettings };
|
|
571
571
|
|
|
572
|
-
//# sourceMappingURL=WidgetSettings-
|
|
572
|
+
//# sourceMappingURL=WidgetSettings-BsjEg-Dj.js.map
|