@tradecanvas/chart 1.80.0 → 1.82.0
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/Chart.d.ts +14 -1
- package/dist/Chart.d.ts.map +1 -1
- package/dist/{LayoutSession-CX3eWUA1.js → LayoutSession-CLlUszTA.js} +388 -370
- package/dist/LayoutSession-CLlUszTA.js.map +1 -0
- package/dist/LayoutSession-Cs8SJ1LU.cjs +2 -0
- package/dist/LayoutSession-Cs8SJ1LU.cjs.map +1 -0
- package/dist/{WidgetCommandPalette-B1jaXJ-I.cjs → WidgetCommandPalette-BNBL1mO2.cjs} +2 -2
- package/dist/{WidgetCommandPalette-B1jaXJ-I.cjs.map → WidgetCommandPalette-BNBL1mO2.cjs.map} +1 -1
- package/dist/{WidgetCommandPalette-Bvr6Od3p.js → WidgetCommandPalette-p4DQCe57.js} +2 -2
- package/dist/{WidgetCommandPalette-Bvr6Od3p.js.map → WidgetCommandPalette-p4DQCe57.js.map} +1 -1
- package/dist/{WidgetDrawingSettings-B8JKpke1.cjs → WidgetDrawingSettings-BqGuAMz3.cjs} +2 -2
- package/dist/{WidgetDrawingSettings-B8JKpke1.cjs.map → WidgetDrawingSettings-BqGuAMz3.cjs.map} +1 -1
- package/dist/{WidgetDrawingSettings-CsR3GuFG.js → WidgetDrawingSettings-D7l2NkVj.js} +2 -2
- package/dist/{WidgetDrawingSettings-CsR3GuFG.js.map → WidgetDrawingSettings-D7l2NkVj.js.map} +1 -1
- package/dist/{WidgetIndicatorPicker-B72LdJy1.cjs → WidgetIndicatorPicker-BOxIKUn-.cjs} +2 -2
- package/dist/{WidgetIndicatorPicker-B72LdJy1.cjs.map → WidgetIndicatorPicker-BOxIKUn-.cjs.map} +1 -1
- package/dist/{WidgetIndicatorPicker-Cb2m62oH.js → WidgetIndicatorPicker-Bk0y6c35.js} +2 -2
- package/dist/{WidgetIndicatorPicker-Cb2m62oH.js.map → WidgetIndicatorPicker-Bk0y6c35.js.map} +1 -1
- package/dist/{WidgetIndicatorSettings-BiX1ZOhS.js → WidgetIndicatorSettings-BmOqq2Fv.js} +3 -3
- package/dist/{WidgetIndicatorSettings-BiX1ZOhS.js.map → WidgetIndicatorSettings-BmOqq2Fv.js.map} +1 -1
- package/dist/{WidgetIndicatorSettings-DhFQasHT.cjs → WidgetIndicatorSettings-DrPcNSxS.cjs} +2 -2
- package/dist/{WidgetIndicatorSettings-DhFQasHT.cjs.map → WidgetIndicatorSettings-DrPcNSxS.cjs.map} +1 -1
- package/dist/{WidgetSettings-bO72SYQ6.cjs → WidgetSettings-B57_tUke.cjs} +2 -2
- package/dist/WidgetSettings-B57_tUke.cjs.map +1 -0
- package/dist/{WidgetSettings-CkvK-3d9.js → WidgetSettings-C6m55YF0.js} +17 -4
- package/dist/WidgetSettings-C6m55YF0.js.map +1 -0
- package/dist/{WidgetSymbolSearch-BW7HxOMk.cjs → WidgetSymbolSearch-8HMKUv2P.cjs} +2 -2
- package/dist/{WidgetSymbolSearch-BW7HxOMk.cjs.map → WidgetSymbolSearch-8HMKUv2P.cjs.map} +1 -1
- package/dist/{WidgetSymbolSearch-CYYd9zoA.js → WidgetSymbolSearch-BdI1egoq.js} +25 -25
- package/dist/{WidgetSymbolSearch-CYYd9zoA.js.map → WidgetSymbolSearch-BdI1egoq.js.map} +1 -1
- package/dist/{i18n-Ci4pfuXw.js → i18n-BMzvClhl.js} +11 -1
- package/dist/i18n-BMzvClhl.js.map +1 -0
- package/dist/i18n-Dq-eMISy.cjs +2 -0
- package/dist/i18n-Dq-eMISy.cjs.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/{indicatorInputWords-BfWHWL0K.cjs → indicatorInputWords-DGObUFDn.cjs} +2 -2
- package/dist/{indicatorInputWords-BfWHWL0K.cjs.map → indicatorInputWords-DGObUFDn.cjs.map} +1 -1
- package/dist/{indicatorInputWords-DpkxBon4.js → indicatorInputWords-Dn_gEPug.js} +2 -2
- package/dist/{indicatorInputWords-DpkxBon4.js.map → indicatorInputWords-Dn_gEPug.js.map} +1 -1
- package/dist/{symbolCatalog-DffhUQSJ.js → symbolCatalog-B_9kDAjE.js} +30 -27
- package/dist/symbolCatalog-B_9kDAjE.js.map +1 -0
- package/dist/symbolCatalog-CXqRu23x.cjs +2 -0
- package/dist/symbolCatalog-CXqRu23x.cjs.map +1 -0
- package/dist/widget/ChartWidget.d.ts +9 -1
- package/dist/widget/ChartWidget.d.ts.map +1 -1
- package/dist/widget/WidgetChartSettings.d.ts +2 -0
- package/dist/widget/WidgetChartSettings.d.ts.map +1 -1
- package/dist/widget/WidgetDrawingSidebar.d.ts +27 -0
- package/dist/widget/WidgetDrawingSidebar.d.ts.map +1 -1
- package/dist/widget/WidgetLoadingOverlay.d.ts.map +1 -1
- package/dist/widget/WidgetSettings.d.ts.map +1 -1
- package/dist/widget/chartSettingsStore.d.ts.map +1 -1
- package/dist/widget/index.cjs +30 -62
- package/dist/widget/index.cjs.map +1 -1
- package/dist/widget/index.js +1220 -1101
- package/dist/widget/index.js.map +1 -1
- package/dist/widget/locales/ar.d.ts.map +1 -1
- package/dist/widget/locales/cs.d.ts.map +1 -1
- package/dist/widget/locales/da.d.ts.map +1 -1
- package/dist/widget/locales/de.d.ts.map +1 -1
- package/dist/widget/locales/el.d.ts.map +1 -1
- package/dist/widget/locales/en.d.ts +5 -0
- package/dist/widget/locales/en.d.ts.map +1 -1
- package/dist/widget/locales/es.d.ts.map +1 -1
- package/dist/widget/locales/et.d.ts.map +1 -1
- package/dist/widget/locales/fa.d.ts.map +1 -1
- package/dist/widget/locales/fr.d.ts.map +1 -1
- package/dist/widget/locales/he.d.ts.map +1 -1
- package/dist/widget/locales/hu.d.ts.map +1 -1
- package/dist/widget/locales/id.d.ts.map +1 -1
- package/dist/widget/locales/index.cjs +1 -1
- package/dist/widget/locales/index.cjs.map +1 -1
- package/dist/widget/locales/index.js +141 -1
- package/dist/widget/locales/index.js.map +1 -1
- package/dist/widget/locales/it.d.ts.map +1 -1
- package/dist/widget/locales/ja.d.ts.map +1 -1
- package/dist/widget/locales/ko.d.ts.map +1 -1
- package/dist/widget/locales/ms.d.ts.map +1 -1
- package/dist/widget/locales/nb.d.ts.map +1 -1
- package/dist/widget/locales/nl.d.ts.map +1 -1
- package/dist/widget/locales/pl.d.ts.map +1 -1
- package/dist/widget/locales/pt.d.ts.map +1 -1
- package/dist/widget/locales/ro.d.ts.map +1 -1
- package/dist/widget/locales/ru.d.ts.map +1 -1
- package/dist/widget/locales/sk.d.ts.map +1 -1
- package/dist/widget/locales/sv.d.ts.map +1 -1
- package/dist/widget/locales/th.d.ts.map +1 -1
- package/dist/widget/locales/tr.d.ts.map +1 -1
- package/dist/widget/locales/vi.d.ts.map +1 -1
- package/dist/widget/locales/zh.d.ts.map +1 -1
- package/dist/widget/locales/zhHant.d.ts.map +1 -1
- package/dist/widget/symbolCatalog.d.ts +2 -7
- package/dist/widget/symbolCatalog.d.ts.map +1 -1
- package/dist/widget/types.d.ts +4 -0
- package/dist/widget/types.d.ts.map +1 -1
- package/dist/widget/widgetConfig.d.ts.map +1 -1
- package/dist/{widgetLocales-D-QYU_9U.js → widgetLocales-BX7VRdl5.js} +2 -1
- package/dist/widgetLocales-BX7VRdl5.js.map +1 -0
- package/dist/widgetLocales-C_XTkni5.cjs +2 -0
- package/dist/widgetLocales-C_XTkni5.cjs.map +1 -0
- package/package.json +3 -3
- package/dist/LayoutSession-CX3eWUA1.js.map +0 -1
- package/dist/LayoutSession-DFvb5ekw.cjs +0 -2
- package/dist/LayoutSession-DFvb5ekw.cjs.map +0 -1
- package/dist/WidgetSettings-CkvK-3d9.js.map +0 -1
- package/dist/WidgetSettings-bO72SYQ6.cjs.map +0 -1
- package/dist/i18n-Ci4pfuXw.js.map +0 -1
- package/dist/i18n-hMXCt55K.cjs +0 -2
- package/dist/i18n-hMXCt55K.cjs.map +0 -1
- package/dist/symbolCatalog-CSbI2R5e.cjs +0 -2
- package/dist/symbolCatalog-CSbI2R5e.cjs.map +0 -1
- package/dist/symbolCatalog-DffhUQSJ.js.map +0 -1
- package/dist/widgetLocales-D-QYU_9U.js.map +0 -1
- package/dist/widgetLocales-fhdzLs5l.cjs +0 -2
- package/dist/widgetLocales-fhdzLs5l.cjs.map +0 -1
package/dist/{WidgetIndicatorSettings-BiX1ZOhS.js.map → WidgetIndicatorSettings-BmOqq2Fv.js.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WidgetIndicatorSettings-BiX1ZOhS.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-BmOqq2Fv.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-hMXCt55K.cjs`),r=require(`./indicatorInputWords-BfWHWL0K.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-Dq-eMISy.cjs`),r=require(`./indicatorInputWords-DGObUFDn.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-DrPcNSxS.cjs.map
|
package/dist/{WidgetIndicatorSettings-DhFQasHT.cjs.map → WidgetIndicatorSettings-DrPcNSxS.cjs.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WidgetIndicatorSettings-DhFQasHT.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-DrPcNSxS.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,2 +1,2 @@
|
|
|
1
|
-
const e=require(`./widgetLocales-fhdzLs5l.cjs`),t=require(`./focusTrap-DrhhmG0V.cjs`),n=require(`./settingsControls-Tn8_vJJv.cjs`),r=require(`./i18n-hMXCt55K.cjs`);let i=require(`@tradecanvas/commons`);var a=0,o=0,s=20;function c(e){let t=document.createElement(`div`);if(t.className=`tcw-settings-section`,e){let n=document.createElement(`div`);n.className=`tcw-settings-section-title`,n.textContent=e,t.appendChild(n),t.setAttribute(`role`,`group`),t.setAttribute(`aria-label`,e)}return t}function l(e){let t=document.createElement(`div`);t.className=`tcw-settings-row`;let n=document.createElement(`span`);return n.className=`tcw-settings-label`,n.textContent=e,t.appendChild(n),t}function u(e,t,r,i,o){let s=l(e),c=document.createElement(`div`);c.className=`tcw-color-picker-wrap`;let u=document.createElement(`input`);u.type=`color`,u.setAttribute(`aria-label`,e);let d=document.createElement(`span`);d.className=`tcw-color-hex`,d.id=`tcw-color-${++a}`,u.setAttribute(`aria-describedby`,d.id);let f=null,p=t=>{u.value=n.l(t??i.start?.()),d.textContent=t??o(`settings.auto`),s.classList.toggle(`tcw-color-auto`,t===null),t!==null&&i.start&&!f?(f=document.createElement(`button`),f.type=`button`,f.className=`tcw-color-auto-btn`,f.textContent=o(`settings.auto`),f.setAttribute(`aria-label`,`${e}: ${o(`settings.auto`)}`),f.addEventListener(`click`,()=>{r(null),u.focus(),p(i.read())}),c.appendChild(f)):t===null&&f&&(f.remove(),f=null)};return u.addEventListener(`input`,()=>{r(u.value),p(u.value)}),c.append(u,d),s.appendChild(c),p(t),{row:s,sync:()=>p(i.read())}}function d(e,t,n){let r=l(e),i=document.createElement(`button`);return i.type=`button`,i.className=`tcw-toggle${t?` tcw-on`:``}`,i.setAttribute(`role`,`switch`),i.setAttribute(`aria-checked`,String(t)),i.setAttribute(`aria-label`,e),i.addEventListener(`click`,()=>{let e=!i.classList.contains(`tcw-on`);i.classList.toggle(`tcw-on`,e),i.setAttribute(`aria-checked`,String(e)),n(e)}),r.appendChild(i),r}function f(e,t,n,r){let i=l(e),a=document.createElement(`select`);a.className=`tcw-settings-select`,a.setAttribute(`aria-label`,e);for(let e of n){let n=document.createElement(`option`);n.value=e.value,n.textContent=e.label,e.value===t&&(n.selected=!0),a.appendChild(n)}return n.some(e=>e.value===t)||(a.selectedIndex=-1),a.addEventListener(`change`,()=>r(a.value)),i.appendChild(a),i}function p(e,t,n,r,i=e=>String(e)){let a=l(e),o=document.createElement(`div`);o.className=`tcw-settings-range`;let s=document.createElement(`input`);s.type=`range`,s.min=String(n.min),s.max=String(n.max),s.step=String(n.step),s.value=String(t),s.setAttribute(`aria-label`,e);let c=document.createElement(`span`);return c.className=`tcw-settings-range-val`,c.textContent=i(t),s.addEventListener(`input`,()=>{let e=Number(s.value);c.textContent=i(e),r(e)}),o.append(s,c),a.appendChild(o),a}function m(e,t,n){let i=new Intl.NumberFormat(n,{maximumFractionDigits:s}).format;return e.step===`1`&&Number.isFinite(e.max)?r.r(t(`settings.numberWhole`),{min:i(Math.max(1,e.min??1)),max:i(e.max)}):r.r(t(`settings.numberAtLeast`),{min:i(e.min??0)})}function h(e,t,n,r,i,a=`en-US`){let s=document.createElement(`div`);s.className=`tcw-settings-row tcw-settings-row--number`;let c=document.createElement(`label`);c.className=`tcw-settings-label`,c.textContent=e,s.appendChild(c);let l=document.createElement(`input`);l.type=`number`,l.className=`tcw-settings-number`,l.min=`0`,l.step=n.step,Number.isFinite(n.max)&&(l.max=String(n.max)),l.placeholder=i(`settings.auto`),l.value=t===null?``:String(t),l.setAttribute(`aria-label`,e);let u=document.createElement(`div`);u.className=`tcw-settings-hint`,u.id=`tcw-number-hint-${++o}`,u.setAttribute(`role`,`status`),l.setAttribute(`aria-describedby`,u.id);let d=e=>{e?l.removeAttribute(`aria-invalid`):l.setAttribute(`aria-invalid`,`true`),u.textContent=e?``:m(n,i,a)};return l.addEventListener(`change`,()=>{if(l.value.trim()===``){d(!0),r(null);return}let e=Number(l.value),t=Number.isFinite(e)&&e>0&&e>=(n.min??0)&&e<=n.max&&(n.step!==`1`||Number.isInteger(e));d(t),t&&r(e)}),s.append(l,u),s}var g=[{id:`symbol`,icon:`chartType:candlestick`},{id:`statusLine`,icon:`type`},{id:`scales`,icon:`ruler`},{id:`appearance`,icon:`palette`},{id:`layers`,icon:`layers`},{id:`trading`,icon:`trendingUp`}],_=[1,2,3,4],v=`t:`,y={step:`any`,max:1/0,min:1e-8},b=1,x=class{callbacks;t;backdrop=null;modal=null;bodyEl=null;tabButtons=[];templateBtn=null;menu=null;currentTab=`symbol`;currentSettings=null;chartType=null;lookRows=[];returnFocus=null;uid=b++;constructor(e,t,n={},r=()=>document.body){this.available=n,this.host=r,this.callbacks=e,this.t=t}open(e,t=null,n=`symbol`){this.modal&&this.remove(),this.returnFocus=document.activeElement instanceof HTMLElement?document.activeElement:null,this.currentSettings={...e},this.chartType=t,this.currentTab=n,this.buildModal(),this.tabButtons.find(e=>e.dataset.tab===n)?.focus()}isOpen(){return this.modal!==null}follow(e){!this.modal||this.currentSettings&&S(this.currentSettings,e)||this.refresh(e)}refresh(e,t){if(!this.modal)return;this.currentSettings={...e},t!==void 0&&(this.chartType=t);let n=this.focusedControl();this.renderTabContent(),n!==null&&this.focusControl(n)}focusedControl(){let e=document.activeElement;if(!this.bodyEl||!(e instanceof HTMLElement)||!this.bodyEl.contains(e))return null;let t=this.panelControls().indexOf(e);return t<0?null:t}focusControl(e){let t=this.panelControls();(t[Math.min(e,t.length-1)]??this.tabButtons.find(e=>e.dataset.tab===this.currentTab))?.focus()}panelControls(){return this.bodyEl?[...this.bodyEl.querySelectorAll(t.t)]:[]}close(){this.finish(!0)}destroy(){this.remove()}finish(e){this.modal&&(e?this.callbacks.onClose():this.callbacks.onCancel?.(),this.remove(),this.returnFocus?.isConnected&&this.returnFocus.focus(),this.returnFocus=null)}remove(){this.menu?.destroy(),this.menu=null,this.backdrop?.remove(),this.backdrop=this.modal=this.bodyEl=this.templateBtn=null,this.tabButtons=[]}buildModal(){let e=document.createElement(`div`);e.className=`tcw-modal-backdrop`;let n=!1;e.addEventListener(`pointerdown`,t=>{n=t.target===e}),e.addEventListener(`click`,t=>{t.target===e&&n&&this.finish(!1),n=!1});let r=document.createElement(`div`);r.className=`tcw-modal tcw-set`,r.setAttribute(`role`,`dialog`),r.setAttribute(`aria-modal`,`true`),r.setAttribute(`aria-labelledby`,`tcw-set-title-${this.uid}`),r.tabIndex=-1,r.addEventListener(`keydown`,e=>{if(e.key===`Escape`){e.preventDefault(),e.stopPropagation(),this.finish(!1);return}t.n(e,r)}),this.backdrop=e,this.modal=r,r.append(this.buildHeader(),this.buildBody(),this.buildFooter()),e.appendChild(r),this.host().appendChild(e),this.markTabs(),this.renderTabContent()}buildHeader(){let e=document.createElement(`div`);e.className=`tcw-modal-header`;let n=document.createElement(`h3`);n.id=`tcw-set-title-${this.uid}`,n.textContent=this.t(`settings.title`);let r=document.createElement(`button`);return r.type=`button`,r.className=`tcw-modal-close`,r.setAttribute(`aria-label`,this.t(`common.close`)),r.innerHTML=t.i(`x`,16),r.addEventListener(`click`,()=>this.finish(!1)),e.append(n,r),e}buildBody(){let e=document.createElement(`div`);e.className=`tcw-set-body`;let n=document.createElement(`div`);n.className=`tcw-set-nav`,n.setAttribute(`role`,`tablist`),n.setAttribute(`aria-orientation`,`vertical`),n.setAttribute(`aria-label`,this.t(`settings.title`)),n.addEventListener(`keydown`,e=>this.onTabKey(e)),this.tabButtons=g.map(({id:e,icon:n})=>{let r=document.createElement(`button`);return r.type=`button`,r.className=`tcw-set-tab`,r.id=`tcw-set-tab-${this.uid}-${e}`,r.dataset.tab=e,r.setAttribute(`role`,`tab`),r.setAttribute(`aria-controls`,`tcw-set-panel-${this.uid}`),r.innerHTML=`${n.startsWith(`chartType:`)?t.r(n.slice(10),16):t.i(n,16)}<span></span>`,r.querySelector(`span`).textContent=this.t(`settings.tab.${e}`),r.addEventListener(`click`,()=>this.selectTab(e)),r}),n.append(...this.tabButtons);let r=document.createElement(`div`);return r.className=`tcw-modal-body tcw-set-panel`,r.id=`tcw-set-panel-${this.uid}`,r.setAttribute(`role`,`tabpanel`),this.bodyEl=r,e.append(n,r),e}buildFooter(){let e=document.createElement(`div`);e.className=`tcw-modal-footer`;let n=this.footerButton(`template`,`tcw-footer-menu`,()=>this.openTemplates());n.setAttribute(`aria-haspopup`,`menu`),n.setAttribute(`aria-expanded`,`false`),n.innerHTML=`<span></span>${t.i(`chevronDown`,12)}`,n.querySelector(`span`).textContent=this.t(`settings.template`),this.templateBtn=n;let r=document.createElement(`div`);return r.className=`tcw-footer-end`,r.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`))),e.append(n,r),e}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}markTabs(){for(let e of this.tabButtons){let t=e.dataset.tab===this.currentTab;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.currentTab=e,this.markTabs(),this.renderTabContent(),t&&this.tabButtons.find(t=>t.dataset.tab===e)?.focus()}onTabKey(e){let t=g.findIndex(e=>e.id===this.currentTab),n=e.key===`ArrowDown`||e.key===`ArrowRight`?(t+1)%g.length:e.key===`ArrowUp`||e.key===`ArrowLeft`?(t-1+g.length)%g.length:e.key===`Home`?0:e.key===`End`?g.length-1:-1;n<0||(e.preventDefault(),this.selectTab(g[n].id,!0))}openTemplates(){let e=this.templateBtn;if(!this.modal||!e)return;if(this.menu?.isOpen()){this.menu.close();return}let t=this.callbacks.templates,i=(t?.list()??[]).map(e=>({id:`${v}${e}`,label:e,icon:`layers`,remove:r.r(this.t(`templates.delete`),{name:e})})),a=[...i,...i.length>0?[`separator`]:[],...t?[{id:`save`,label:this.t(`settings.saveTemplate`),icon:`save`}]:[],{id:`reset`,label:this.t(`settings.resetToDefaults`),icon:`reset`}];this.menu??=new n.f(this.modal,this.t(`settings.template`));let o=e.getBoundingClientRect(),s=this.modal.getBoundingClientRect();this.menu.open(a,o.left-s.left,o.top-s.top,e=>{e===`reset`?this.callbacks.onReset():e===`save`?t?.save():e.startsWith(v)&&t?.apply(e.slice(2))},e,e=>t?.remove(e.slice(2)))}renderTabContent(){if(!(!this.bodyEl||!this.currentSettings))switch(this.bodyEl.replaceChildren(),this.lookRows=[],this.currentTab){case`symbol`:this.renderSymbolTab(this.currentSettings);break;case`statusLine`:this.renderStatusLineTab(this.currentSettings);break;case`scales`:this.renderScalesTab(this.currentSettings);break;case`appearance`:this.renderAppearanceTab(this.currentSettings);break;case`layers`:this.renderLayersTab(this.currentSettings);break;case`trading`:for(let t of e.d)(!t.trading||this.available.trading!==!1)&&this.renderLookSection(t);break}}renderSymbolTab(t){this.renderChartTypeSection(),this.addSection(void 0,[d(this.t(`settings.mainSeries`),t.mainSeriesVisible,e=>this.patch({mainSeriesVisible:e})),d(this.t(`settings.extendedHours`),t.extendedHours,e=>this.patch({extendedHours:e}))]);let n=this.t(`common.show`);this.renderLookSection(e.o),this.available.volume!==!1&&this.renderLookSection(e.f,[d(n,t.volumeVisible,e=>this.patch({volumeVisible:e}))]),this.renderLookSection(e.a,[d(n,t.highLowLines,e=>this.patch({highLowLines:e}))])}renderStatusLineTab(t){let n=this.available.legend!==!1,r=n?e.u:e.u.filter(t=>!e.s.includes(t));this.addSection(void 0,[...n?[d(this.t(`settings.ohlcLegend`),t.legendVisible,e=>this.patch({legendVisible:e}))]:[],...r.map(e=>this.lookRow(e))])}renderScalesTab(t){let n=[{value:`regular`,label:this.t(`settings.scale.regular`)},{value:`logarithmic`,label:this.t(`settings.scale.logarithmic`)},{value:`percentage`,label:this.t(`settings.scale.percentage`)},{value:`indexedTo100`,label:this.t(`settings.scale.indexedTo100`)}].filter(e=>e.value!==`logarithmic`||this.available.logScale!==!1),r=this.available.exchangeZone?.(),i=e.y(t.timezone,Date.now(),this.t(`settings.timezone.local`),r===void 0?void 0:{label:this.t(`settings.timezone.exchange`),zone:r});this.addSection(void 0,[f(this.t(`settings.priceScale`),t.scaleMode,n,e=>this.patch({scaleMode:e})),d(this.t(`settings.autoScale`),t.autoScale,e=>this.patch({autoScale:e})),d(this.t(`settings.invertScale`),t.invertScale,e=>this.patch({invertScale:e})),d(this.t(`settings.leftScale`),t.leftPriceScale,e=>this.patch({leftPriceScale:e})),d(this.t(`settings.indicatorValues`),t.indicatorValueLabels,e=>this.patch({indicatorValueLabels:e})),d(this.t(`settings.alignAxisLabels`),t.alignAxisLabels,e=>this.patch({alignAxisLabels:e})),...this.available.barCountdown===!1?[]:[d(this.t(`settings.barCountdown`),t.barCountdown,e=>this.patch({barCountdown:e}))],f(this.t(`settings.timezone`),t.timezone,i,e=>this.patch({timezone:e})),f(this.t(`settings.numberLocale`),t.numberLocale,e.r(t.numberLocale),e=>this.patch({numberLocale:e}))]),this.addSection(this.t(`settings.section.axes`),e.c.map(e=>this.lookRow(e)))}renderAppearanceTab(t){let[n,r,i,...a]=e.i;this.renderLookSection(n),this.renderLookSection(r,[d(this.t(`common.show`),t.gridVisible,e=>this.patch({gridVisible:e}))]),this.renderLookSection(i,[f(this.t(`settings.crosshairMode`),t.crosshairMode,[{label:this.t(`settings.crosshair.magnet`),value:`magnet`},{label:this.t(`settings.crosshair.normal`),value:`normal`},{label:this.t(`settings.crosshair.hidden`),value:`hidden`}],e=>this.patch({crosshairMode:e}))]);for(let e of a)this.renderLookSection(e)}renderLayersTab(e){let t=e=>`${Math.round(e*100)}%`,n=e=>this.t(e);this.addSection(void 0,[d(n(`settings.volumeProfile`),e.volumeProfileVisible,e=>this.patch({volumeProfileVisible:e}))]),this.addSection(void 0,[d(n(`settings.marketProfile`),e.marketProfileVisible,e=>this.patch({marketProfileVisible:e})),d(n(`settings.marketProfileSplit`),e.marketProfileSplit,e=>this.patch({marketProfileSplit:e})),d(n(`settings.marketProfileLetters`),e.marketProfileLetters,e=>this.patch({marketProfileLetters:e})),p(n(`settings.marketProfileBuckets`),e.marketProfileBuckets,{min:8,max:200,step:1},e=>this.patch({marketProfileBuckets:e})),p(n(`settings.marketProfileOpacity`),e.marketProfileOpacity,{min:.05,max:1,step:.05},e=>this.patch({marketProfileOpacity:e}),t)]),this.addSection(void 0,[d(n(`settings.depthHeatmap`),e.depthHeatmapVisible,e=>this.patch({depthHeatmapVisible:e})),p(n(`settings.depthHeatmapOpacity`),e.depthHeatmapOpacity,{min:.1,max:1,step:.05},e=>this.patch({depthHeatmapOpacity:e}),t)]),this.addSection(void 0,[d(n(`settings.swingMarkers`),e.pivotMarkersVisible,e=>this.patch({pivotMarkersVisible:e})),p(n(`settings.swingStrength`),e.pivotStrength,{min:2,max:20,step:1},e=>this.patch({pivotStrength:e})),d(n(`settings.swingStructure`),e.pivotStructureLabels,e=>this.patch({pivotStructureLabels:e}))]),this.addSection(void 0,[d(n(`settings.sessionShading`),e.sessionShadingVisible,e=>this.patch({sessionShadingVisible:e})),d(n(`settings.periodLevels`),e.periodLevelsVisible,e=>this.patch({periodLevelsVisible:e})),f(n(`settings.periodLevelsBasis`),e.periodLevelsPeriod,[{value:`day`,label:n(`settings.priorDay`)},{value:`week`,label:n(`settings.priorWeek`)}],e=>this.patch({periodLevelsPeriod:e}))])}addSection(e,t){let n=c(e);return n.append(...t),this.bodyEl?.appendChild(n),n}renderLookSection(e,t=[]){this.addSection(this.t(e.title),[...t,...e.rows.map(e=>this.lookRow(e))])}lookRow(e){let t=this.t(e.label),n=()=>this.callbacks.styleValue(e.keys[0]),r=()=>{let t=e.keys.map(e=>this.callbacks.styleValue(e));return t.every(e=>e===t[0])&&t[0]!==null?String(t[0]):``},a,o=t=>{this.callbacks.onStyleChange(Object.fromEntries(e.keys.map(e=>[e,t])));for(let e of this.lookRows)e.el!==a&&e.sync()},s=e.auto?[{value:``,label:this.t(`settings.auto`)}]:[],c;switch(i.CHART_STYLE_KEYS[e.keys[0]]){case`boolean`:{a=d(t,n()===!0,o);let e=a.querySelector(`button`);c=()=>{let t=n()===!0;e.classList.toggle(`tcw-on`,t),e.setAttribute(`aria-checked`,String(t))};break}case`lineStyle`:a=f(t,r(),[...s,...this.lineStyles()],e=>o(e===``?null:T(e))),c=()=>{a.querySelector(`select`).value=r()};break;case`width`:{let e=n(),i=typeof e==`number`&&!_.includes(e)?[..._,e].sort((e,t)=>e-t):_;a=f(t,r(),[...s,...i.map(e=>({value:String(e),label:`${e}px`}))],e=>o(e===``?null:Number(e))),c=()=>{a.querySelector(`select`).value=r()};break}default:{let r=n(),i=u(t,typeof r==`string`?r:null,o,{read:()=>{let e=n();return typeof e==`string`?e:null},start:e.auto?()=>(e.auto?.from?this.callbacks.styleValue(e.auto.from):e.auto?.hint)??null:void 0},this.t);a=i.row,c=i.sync}}return this.lookRows.push({el:a,sync:c}),a}lineStyles(){return[{value:`solid`,label:this.t(`drawingSettings.lineStyle.solid`)},{value:`dashed`,label:this.t(`drawingSettings.lineStyle.dashed`)},{value:`dotted`,label:this.t(`drawingSettings.lineStyle.dotted`)}]}renderChartTypeSection(){if(!this.bodyEl||!this.currentSettings||!this.chartType)return;let t=this.chartType,n=this.currentSettings.chartTypeOptions??{},r=e.m.find(e=>e.value===t),i=c(r?e.t(r,this.t):t),a=()=>this.currentSettings?.chartTypeOptions??{},o=(e,t)=>this.patch({chartTypeOptions:{...a(),[e]:t}}),s=(e,t,n,r)=>i.appendChild(h(this.t(e),t,n,r,this.t,this.currentSettings?.numberLocale||void 0));switch(t){case`renko`:{let e=n.renko??{};s(`settings.boxSize`,typeof e.boxSize==`number`?e.boxSize:null,y,e=>o(`renko`,{...a().renko??{},boxSize:e??`atr`})),s(`settings.atrPeriod`,e.atrPeriod??null,{step:`1`,max:500},e=>o(`renko`,w(a().renko,`atrPeriod`,e)));break}case`lineBreak`:s(`settings.lineBreakLines`,n.lineBreak?.lines??null,{step:`1`,max:10},e=>o(`lineBreak`,w(a().lineBreak,`lines`,e)));break;case`kagi`:s(`settings.reversal`,n.kagi?.reversal??null,y,e=>o(`kagi`,w(a().kagi,`reversal`,e))),i.appendChild(f(this.t(`settings.reversalType`),n.kagi?.reversalType??`percent`,[{value:`percent`,label:this.t(`settings.reversalType.percent`)},{value:`price`,label:this.t(`settings.reversalType.price`)}],e=>o(`kagi`,{...a().kagi??{},reversalType:e===`price`?`price`:`percent`})));break;case`pointAndFigure`:s(`settings.boxSize`,typeof n.pointAndFigure?.boxSize==`number`?n.pointAndFigure.boxSize:null,y,e=>o(`pointAndFigure`,{...a().pointAndFigure??{},boxSize:e??`auto`})),s(`settings.reversalBoxes`,n.pointAndFigure?.reversal??null,{step:`1`,max:10},e=>o(`pointAndFigure`,w(a().pointAndFigure,`reversal`,e)));break;case`rangeBars`:s(`settings.barRange`,typeof n.rangeBars?.range==`number`?n.rangeBars.range:null,y,e=>o(`rangeBars`,{range:e??`auto`}));break;default:break}for(let n of e.p(t))i.appendChild(this.lookRow(n));this.bodyEl.appendChild(i)}patch(e){this.currentSettings&&Object.assign(this.currentSettings,e),this.callbacks.onChange(e)}};function S(e,t){return C(e,t)}function C(e,t){if(e===t)return!0;if(typeof e!=`object`||typeof t!=`object`||e===null||t===null||Array.isArray(e)!==Array.isArray(t))return!1;let n=e,r=t,i=new Set([...Object.keys(n),...Object.keys(r)].filter(e=>n[e]!==void 0||r[e]!==void 0));for(let e of i)if(!C(n[e],r[e]))return!1;return!0}function w(e,t,n){let r={...e??{}};return n===null?delete r[t]:r[t]=n,r}function T(e){return e===`dashed`||e===`dotted`?e:`solid`}exports.WidgetSettings=x;
|
|
2
|
-
//# sourceMappingURL=WidgetSettings-
|
|
1
|
+
const e=require(`./widgetLocales-C_XTkni5.cjs`),t=require(`./focusTrap-DrhhmG0V.cjs`),n=require(`./settingsControls-Tn8_vJJv.cjs`),r=require(`./i18n-Dq-eMISy.cjs`);let i=require(`@tradecanvas/commons`);var a=0,o=0,s=20;function c(e){let t=document.createElement(`div`);if(t.className=`tcw-settings-section`,e){let n=document.createElement(`div`);n.className=`tcw-settings-section-title`,n.textContent=e,t.appendChild(n),t.setAttribute(`role`,`group`),t.setAttribute(`aria-label`,e)}return t}function l(e){let t=document.createElement(`div`);t.className=`tcw-settings-row`;let n=document.createElement(`span`);return n.className=`tcw-settings-label`,n.textContent=e,t.appendChild(n),t}function u(e,t,r,i,o){let s=l(e),c=document.createElement(`div`);c.className=`tcw-color-picker-wrap`;let u=document.createElement(`input`);u.type=`color`,u.setAttribute(`aria-label`,e);let d=document.createElement(`span`);d.className=`tcw-color-hex`,d.id=`tcw-color-${++a}`,u.setAttribute(`aria-describedby`,d.id);let f=null,p=t=>{u.value=n.l(t??i.start?.()),d.textContent=t??o(`settings.auto`),s.classList.toggle(`tcw-color-auto`,t===null),t!==null&&i.start&&!f?(f=document.createElement(`button`),f.type=`button`,f.className=`tcw-color-auto-btn`,f.textContent=o(`settings.auto`),f.setAttribute(`aria-label`,`${e}: ${o(`settings.auto`)}`),f.addEventListener(`click`,()=>{r(null),u.focus(),p(i.read())}),c.appendChild(f)):t===null&&f&&(f.remove(),f=null)};return u.addEventListener(`input`,()=>{r(u.value),p(u.value)}),c.append(u,d),s.appendChild(c),p(t),{row:s,sync:()=>p(i.read())}}function d(e,t,n){let r=l(e),i=document.createElement(`button`);return i.type=`button`,i.className=`tcw-toggle${t?` tcw-on`:``}`,i.setAttribute(`role`,`switch`),i.setAttribute(`aria-checked`,String(t)),i.setAttribute(`aria-label`,e),i.addEventListener(`click`,()=>{let e=!i.classList.contains(`tcw-on`);i.classList.toggle(`tcw-on`,e),i.setAttribute(`aria-checked`,String(e)),n(e)}),r.appendChild(i),r}function f(e,t,n,r){let i=l(e),a=document.createElement(`select`);a.className=`tcw-settings-select`,a.setAttribute(`aria-label`,e);for(let e of n){let n=document.createElement(`option`);n.value=e.value,n.textContent=e.label,e.value===t&&(n.selected=!0),a.appendChild(n)}return n.some(e=>e.value===t)||(a.selectedIndex=-1),a.addEventListener(`change`,()=>r(a.value)),i.appendChild(a),i}function p(e,t,n,r,i=e=>String(e)){let a=l(e),o=document.createElement(`div`);o.className=`tcw-settings-range`;let s=document.createElement(`input`);s.type=`range`,s.min=String(n.min),s.max=String(n.max),s.step=String(n.step),s.value=String(t),s.setAttribute(`aria-label`,e);let c=document.createElement(`span`);return c.className=`tcw-settings-range-val`,c.textContent=i(t),s.addEventListener(`input`,()=>{let e=Number(s.value);c.textContent=i(e),r(e)}),o.append(s,c),a.appendChild(o),a}function m(e,t,n){let i=new Intl.NumberFormat(n,{maximumFractionDigits:s}).format;return e.step===`1`&&Number.isFinite(e.max)?r.r(t(`settings.numberWhole`),{min:i(Math.max(1,e.min??1)),max:i(e.max)}):r.r(t(`settings.numberAtLeast`),{min:i(e.min??0)})}function h(e,t,n,r,i,a=`en-US`){let s=document.createElement(`div`);s.className=`tcw-settings-row tcw-settings-row--number`;let c=document.createElement(`label`);c.className=`tcw-settings-label`,c.textContent=e,s.appendChild(c);let l=document.createElement(`input`);l.type=`number`,l.className=`tcw-settings-number`,l.min=`0`,l.step=n.step,Number.isFinite(n.max)&&(l.max=String(n.max)),l.placeholder=i(`settings.auto`),l.value=t===null?``:String(t),l.setAttribute(`aria-label`,e);let u=document.createElement(`div`);u.className=`tcw-settings-hint`,u.id=`tcw-number-hint-${++o}`,u.setAttribute(`role`,`status`),l.setAttribute(`aria-describedby`,u.id);let d=e=>{e?l.removeAttribute(`aria-invalid`):l.setAttribute(`aria-invalid`,`true`),u.textContent=e?``:m(n,i,a)};return l.addEventListener(`change`,()=>{if(l.value.trim()===``){d(!0),r(null);return}let e=Number(l.value),t=Number.isFinite(e)&&e>0&&e>=(n.min??0)&&e<=n.max&&(n.step!==`1`||Number.isInteger(e));d(t),t&&r(e)}),s.append(l,u),s}var g=[{id:`symbol`,icon:`chartType:candlestick`},{id:`statusLine`,icon:`type`},{id:`scales`,icon:`ruler`},{id:`appearance`,icon:`palette`},{id:`layers`,icon:`layers`},{id:`trading`,icon:`trendingUp`}],_=[1,2,3,4],v=`t:`,y={step:`any`,max:1/0,min:1e-8},b=1,x=class{callbacks;t;backdrop=null;modal=null;bodyEl=null;tabButtons=[];templateBtn=null;menu=null;currentTab=`symbol`;currentSettings=null;chartType=null;lookRows=[];returnFocus=null;uid=b++;constructor(e,t,n={},r=()=>document.body){this.available=n,this.host=r,this.callbacks=e,this.t=t}open(e,t=null,n=`symbol`){this.modal&&this.remove(),this.returnFocus=document.activeElement instanceof HTMLElement?document.activeElement:null,this.currentSettings={...e},this.chartType=t,this.currentTab=n,this.buildModal(),this.tabButtons.find(e=>e.dataset.tab===n)?.focus()}isOpen(){return this.modal!==null}follow(e){!this.modal||this.currentSettings&&S(this.currentSettings,e)||this.refresh(e)}refresh(e,t){if(!this.modal)return;this.currentSettings={...e},t!==void 0&&(this.chartType=t);let n=this.focusedControl();this.renderTabContent(),n!==null&&this.focusControl(n)}focusedControl(){let e=document.activeElement;if(!this.bodyEl||!(e instanceof HTMLElement)||!this.bodyEl.contains(e))return null;let t=this.panelControls().indexOf(e);return t<0?null:t}focusControl(e){let t=this.panelControls();(t[Math.min(e,t.length-1)]??this.tabButtons.find(e=>e.dataset.tab===this.currentTab))?.focus()}panelControls(){return this.bodyEl?[...this.bodyEl.querySelectorAll(t.t)]:[]}close(){this.finish(!0)}destroy(){this.remove()}finish(e){this.modal&&(e?this.callbacks.onClose():this.callbacks.onCancel?.(),this.remove(),this.returnFocus?.isConnected&&this.returnFocus.focus(),this.returnFocus=null)}remove(){this.menu?.destroy(),this.menu=null,this.backdrop?.remove(),this.backdrop=this.modal=this.bodyEl=this.templateBtn=null,this.tabButtons=[]}buildModal(){let e=document.createElement(`div`);e.className=`tcw-modal-backdrop`;let n=!1;e.addEventListener(`pointerdown`,t=>{n=t.target===e}),e.addEventListener(`click`,t=>{t.target===e&&n&&this.finish(!1),n=!1});let r=document.createElement(`div`);r.className=`tcw-modal tcw-set`,r.setAttribute(`role`,`dialog`),r.setAttribute(`aria-modal`,`true`),r.setAttribute(`aria-labelledby`,`tcw-set-title-${this.uid}`),r.tabIndex=-1,r.addEventListener(`keydown`,e=>{if(e.key===`Escape`){e.preventDefault(),e.stopPropagation(),this.finish(!1);return}t.n(e,r)}),this.backdrop=e,this.modal=r,r.append(this.buildHeader(),this.buildBody(),this.buildFooter()),e.appendChild(r),this.host().appendChild(e),this.markTabs(),this.renderTabContent()}buildHeader(){let e=document.createElement(`div`);e.className=`tcw-modal-header`;let n=document.createElement(`h3`);n.id=`tcw-set-title-${this.uid}`,n.textContent=this.t(`settings.title`);let r=document.createElement(`button`);return r.type=`button`,r.className=`tcw-modal-close`,r.setAttribute(`aria-label`,this.t(`common.close`)),r.innerHTML=t.i(`x`,16),r.addEventListener(`click`,()=>this.finish(!1)),e.append(n,r),e}buildBody(){let e=document.createElement(`div`);e.className=`tcw-set-body`;let n=document.createElement(`div`);n.className=`tcw-set-nav`,n.setAttribute(`role`,`tablist`),n.setAttribute(`aria-orientation`,`vertical`),n.setAttribute(`aria-label`,this.t(`settings.title`)),n.addEventListener(`keydown`,e=>this.onTabKey(e)),this.tabButtons=g.map(({id:e,icon:n})=>{let r=document.createElement(`button`);return r.type=`button`,r.className=`tcw-set-tab`,r.id=`tcw-set-tab-${this.uid}-${e}`,r.dataset.tab=e,r.setAttribute(`role`,`tab`),r.setAttribute(`aria-controls`,`tcw-set-panel-${this.uid}`),r.innerHTML=`${n.startsWith(`chartType:`)?t.r(n.slice(10),16):t.i(n,16)}<span></span>`,r.querySelector(`span`).textContent=this.t(`settings.tab.${e}`),r.addEventListener(`click`,()=>this.selectTab(e)),r}),n.append(...this.tabButtons);let r=document.createElement(`div`);return r.className=`tcw-modal-body tcw-set-panel`,r.id=`tcw-set-panel-${this.uid}`,r.setAttribute(`role`,`tabpanel`),this.bodyEl=r,e.append(n,r),e}buildFooter(){let e=document.createElement(`div`);e.className=`tcw-modal-footer`;let n=this.footerButton(`template`,`tcw-footer-menu`,()=>this.openTemplates());n.setAttribute(`aria-haspopup`,`menu`),n.setAttribute(`aria-expanded`,`false`),n.innerHTML=`<span></span>${t.i(`chevronDown`,12)}`,n.querySelector(`span`).textContent=this.t(`settings.template`),this.templateBtn=n;let r=document.createElement(`div`);return r.className=`tcw-footer-end`,r.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`))),e.append(n,r),e}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}markTabs(){for(let e of this.tabButtons){let t=e.dataset.tab===this.currentTab;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.currentTab=e,this.markTabs(),this.renderTabContent(),t&&this.tabButtons.find(t=>t.dataset.tab===e)?.focus()}onTabKey(e){let t=g.findIndex(e=>e.id===this.currentTab),n=e.key===`ArrowDown`||e.key===`ArrowRight`?(t+1)%g.length:e.key===`ArrowUp`||e.key===`ArrowLeft`?(t-1+g.length)%g.length:e.key===`Home`?0:e.key===`End`?g.length-1:-1;n<0||(e.preventDefault(),this.selectTab(g[n].id,!0))}openTemplates(){let e=this.templateBtn;if(!this.modal||!e)return;if(this.menu?.isOpen()){this.menu.close();return}let t=this.callbacks.templates,i=(t?.list()??[]).map(e=>({id:`${v}${e}`,label:e,icon:`layers`,remove:r.r(this.t(`templates.delete`),{name:e})})),a=[...i,...i.length>0?[`separator`]:[],...t?[{id:`save`,label:this.t(`settings.saveTemplate`),icon:`save`}]:[],{id:`reset`,label:this.t(`settings.resetToDefaults`),icon:`reset`}];this.menu??=new n.f(this.modal,this.t(`settings.template`));let o=e.getBoundingClientRect(),s=this.modal.getBoundingClientRect();this.menu.open(a,o.left-s.left,o.top-s.top,e=>{e===`reset`?this.callbacks.onReset():e===`save`?t?.save():e.startsWith(v)&&t?.apply(e.slice(2))},e,e=>t?.remove(e.slice(2)))}renderTabContent(){if(!(!this.bodyEl||!this.currentSettings))switch(this.bodyEl.replaceChildren(),this.lookRows=[],this.currentTab){case`symbol`:this.renderSymbolTab(this.currentSettings);break;case`statusLine`:this.renderStatusLineTab(this.currentSettings);break;case`scales`:this.renderScalesTab(this.currentSettings);break;case`appearance`:this.renderAppearanceTab(this.currentSettings);break;case`layers`:this.renderLayersTab(this.currentSettings);break;case`trading`:for(let t of e.d)(!t.trading||this.available.trading!==!1)&&this.renderLookSection(t);break}}renderSymbolTab(t){this.renderChartTypeSection(),this.addSection(void 0,[d(this.t(`settings.mainSeries`),t.mainSeriesVisible,e=>this.patch({mainSeriesVisible:e})),d(this.t(`settings.extendedHours`),t.extendedHours,e=>this.patch({extendedHours:e}))]);let n=this.t(`common.show`);this.renderLookSection(e.o),this.available.volume!==!1&&this.renderLookSection(e.f,[d(n,t.volumeVisible,e=>this.patch({volumeVisible:e}))]),this.renderLookSection(e.a,[d(n,t.highLowLines,e=>this.patch({highLowLines:e}))])}renderStatusLineTab(t){let n=this.available.legend!==!1,r=n?e.u:e.u.filter(t=>!e.s.includes(t));this.addSection(void 0,[...n?[d(this.t(`settings.ohlcLegend`),t.legendVisible,e=>this.patch({legendVisible:e}))]:[],...r.map(e=>this.lookRow(e))])}renderScalesTab(t){let n=[{value:`regular`,label:this.t(`settings.scale.regular`)},{value:`logarithmic`,label:this.t(`settings.scale.logarithmic`)},{value:`percentage`,label:this.t(`settings.scale.percentage`)},{value:`indexedTo100`,label:this.t(`settings.scale.indexedTo100`)}].filter(e=>e.value!==`logarithmic`||this.available.logScale!==!1),r=this.available.exchangeZone?.(),i=e.y(t.timezone,Date.now(),this.t(`settings.timezone.local`),r===void 0?void 0:{label:this.t(`settings.timezone.exchange`),zone:r});this.addSection(void 0,[f(this.t(`settings.priceScale`),t.scaleMode,n,e=>this.patch({scaleMode:e})),d(this.t(`settings.autoScale`),t.autoScale,e=>this.patch({autoScale:e})),d(this.t(`settings.invertScale`),t.invertScale,e=>this.patch({invertScale:e})),d(this.t(`settings.leftScale`),t.leftPriceScale,e=>this.patch({leftPriceScale:e})),d(this.t(`settings.indicatorValues`),t.indicatorValueLabels,e=>this.patch({indicatorValueLabels:e})),d(this.t(`settings.alignAxisLabels`),t.alignAxisLabels,e=>this.patch({alignAxisLabels:e})),...this.available.barCountdown===!1?[]:[d(this.t(`settings.barCountdown`),t.barCountdown,e=>this.patch({barCountdown:e}))],f(this.t(`settings.timezone`),t.timezone,i,e=>this.patch({timezone:e})),f(this.t(`settings.numberLocale`),t.numberLocale,e.r(t.numberLocale),e=>this.patch({numberLocale:e}))]),this.addSection(this.t(`settings.section.axes`),e.c.map(e=>this.lookRow(e)))}renderAppearanceTab(t){let[n,r,i,...a]=e.i;this.renderLookSection(n),this.renderLookSection(r,[d(this.t(`common.show`),t.gridVisible,e=>this.patch({gridVisible:e}))]),this.renderLookSection(i,[f(this.t(`settings.crosshairMode`),t.crosshairMode,[{label:this.t(`settings.crosshair.magnet`),value:`magnet`},{label:this.t(`settings.crosshair.normal`),value:`normal`},{label:this.t(`settings.crosshair.hidden`),value:`hidden`}],e=>this.patch({crosshairMode:e})),f(this.t(`settings.crosshairCursor`),t.crosshairCursor,[{label:this.t(`drawing.cursorCross`),value:`cross`},{label:this.t(`drawing.cursorDot`),value:`dot`},{label:this.t(`drawing.cursorArrow`),value:`arrow`}],e=>this.patch({crosshairCursor:e}))]);for(let e of a)this.renderLookSection(e)}renderLayersTab(e){let t=e=>`${Math.round(e*100)}%`,n=e=>this.t(e);this.addSection(void 0,[d(n(`settings.volumeProfile`),e.volumeProfileVisible,e=>this.patch({volumeProfileVisible:e}))]),this.addSection(void 0,[d(n(`settings.marketProfile`),e.marketProfileVisible,e=>this.patch({marketProfileVisible:e})),d(n(`settings.marketProfileSplit`),e.marketProfileSplit,e=>this.patch({marketProfileSplit:e})),d(n(`settings.marketProfileLetters`),e.marketProfileLetters,e=>this.patch({marketProfileLetters:e})),p(n(`settings.marketProfileBuckets`),e.marketProfileBuckets,{min:8,max:200,step:1},e=>this.patch({marketProfileBuckets:e})),p(n(`settings.marketProfileOpacity`),e.marketProfileOpacity,{min:.05,max:1,step:.05},e=>this.patch({marketProfileOpacity:e}),t)]),this.addSection(void 0,[d(n(`settings.depthHeatmap`),e.depthHeatmapVisible,e=>this.patch({depthHeatmapVisible:e})),p(n(`settings.depthHeatmapOpacity`),e.depthHeatmapOpacity,{min:.1,max:1,step:.05},e=>this.patch({depthHeatmapOpacity:e}),t)]),this.addSection(void 0,[d(n(`settings.swingMarkers`),e.pivotMarkersVisible,e=>this.patch({pivotMarkersVisible:e})),p(n(`settings.swingStrength`),e.pivotStrength,{min:2,max:20,step:1},e=>this.patch({pivotStrength:e})),d(n(`settings.swingStructure`),e.pivotStructureLabels,e=>this.patch({pivotStructureLabels:e}))]),this.addSection(void 0,[d(n(`settings.sessionShading`),e.sessionShadingVisible,e=>this.patch({sessionShadingVisible:e})),d(n(`settings.periodLevels`),e.periodLevelsVisible,e=>this.patch({periodLevelsVisible:e})),f(n(`settings.periodLevelsBasis`),e.periodLevelsPeriod,[{value:`day`,label:n(`settings.priorDay`)},{value:`week`,label:n(`settings.priorWeek`)}],e=>this.patch({periodLevelsPeriod:e}))])}addSection(e,t){let n=c(e);return n.append(...t),this.bodyEl?.appendChild(n),n}renderLookSection(e,t=[]){this.addSection(this.t(e.title),[...t,...e.rows.map(e=>this.lookRow(e))])}lookRow(e){let t=this.t(e.label),n=()=>this.callbacks.styleValue(e.keys[0]),r=()=>{let t=e.keys.map(e=>this.callbacks.styleValue(e));return t.every(e=>e===t[0])&&t[0]!==null?String(t[0]):``},a,o=t=>{this.callbacks.onStyleChange(Object.fromEntries(e.keys.map(e=>[e,t])));for(let e of this.lookRows)e.el!==a&&e.sync()},s=e.auto?[{value:``,label:this.t(`settings.auto`)}]:[],c;switch(i.CHART_STYLE_KEYS[e.keys[0]]){case`boolean`:{a=d(t,n()===!0,o);let e=a.querySelector(`button`);c=()=>{let t=n()===!0;e.classList.toggle(`tcw-on`,t),e.setAttribute(`aria-checked`,String(t))};break}case`lineStyle`:a=f(t,r(),[...s,...this.lineStyles()],e=>o(e===``?null:T(e))),c=()=>{a.querySelector(`select`).value=r()};break;case`width`:{let e=n(),i=typeof e==`number`&&!_.includes(e)?[..._,e].sort((e,t)=>e-t):_;a=f(t,r(),[...s,...i.map(e=>({value:String(e),label:`${e}px`}))],e=>o(e===``?null:Number(e))),c=()=>{a.querySelector(`select`).value=r()};break}default:{let r=n(),i=u(t,typeof r==`string`?r:null,o,{read:()=>{let e=n();return typeof e==`string`?e:null},start:e.auto?()=>(e.auto?.from?this.callbacks.styleValue(e.auto.from):e.auto?.hint)??null:void 0},this.t);a=i.row,c=i.sync}}return this.lookRows.push({el:a,sync:c}),a}lineStyles(){return[{value:`solid`,label:this.t(`drawingSettings.lineStyle.solid`)},{value:`dashed`,label:this.t(`drawingSettings.lineStyle.dashed`)},{value:`dotted`,label:this.t(`drawingSettings.lineStyle.dotted`)}]}renderChartTypeSection(){if(!this.bodyEl||!this.currentSettings||!this.chartType)return;let t=this.chartType,n=this.currentSettings.chartTypeOptions??{},r=e.m.find(e=>e.value===t),i=c(r?e.t(r,this.t):t),a=()=>this.currentSettings?.chartTypeOptions??{},o=(e,t)=>this.patch({chartTypeOptions:{...a(),[e]:t}}),s=(e,t,n,r)=>i.appendChild(h(this.t(e),t,n,r,this.t,this.currentSettings?.numberLocale||void 0));switch(t){case`renko`:{let e=n.renko??{};s(`settings.boxSize`,typeof e.boxSize==`number`?e.boxSize:null,y,e=>o(`renko`,{...a().renko??{},boxSize:e??`atr`})),s(`settings.atrPeriod`,e.atrPeriod??null,{step:`1`,max:500},e=>o(`renko`,w(a().renko,`atrPeriod`,e)));break}case`lineBreak`:s(`settings.lineBreakLines`,n.lineBreak?.lines??null,{step:`1`,max:10},e=>o(`lineBreak`,w(a().lineBreak,`lines`,e)));break;case`kagi`:s(`settings.reversal`,n.kagi?.reversal??null,y,e=>o(`kagi`,w(a().kagi,`reversal`,e))),i.appendChild(f(this.t(`settings.reversalType`),n.kagi?.reversalType??`percent`,[{value:`percent`,label:this.t(`settings.reversalType.percent`)},{value:`price`,label:this.t(`settings.reversalType.price`)}],e=>o(`kagi`,{...a().kagi??{},reversalType:e===`price`?`price`:`percent`})));break;case`pointAndFigure`:s(`settings.boxSize`,typeof n.pointAndFigure?.boxSize==`number`?n.pointAndFigure.boxSize:null,y,e=>o(`pointAndFigure`,{...a().pointAndFigure??{},boxSize:e??`auto`})),s(`settings.reversalBoxes`,n.pointAndFigure?.reversal??null,{step:`1`,max:10},e=>o(`pointAndFigure`,w(a().pointAndFigure,`reversal`,e)));break;case`rangeBars`:s(`settings.barRange`,typeof n.rangeBars?.range==`number`?n.rangeBars.range:null,y,e=>o(`rangeBars`,{range:e??`auto`}));break;default:break}for(let n of e.p(t))i.appendChild(this.lookRow(n));this.bodyEl.appendChild(i)}patch(e){this.currentSettings&&Object.assign(this.currentSettings,e),this.callbacks.onChange(e)}};function S(e,t){return C(e,t)}function C(e,t){if(e===t)return!0;if(typeof e!=`object`||typeof t!=`object`||e===null||t===null||Array.isArray(e)!==Array.isArray(t))return!1;let n=e,r=t,i=new Set([...Object.keys(n),...Object.keys(r)].filter(e=>n[e]!==void 0||r[e]!==void 0));for(let e of i)if(!C(n[e],r[e]))return!1;return!0}function w(e,t,n){let r={...e??{}};return n===null?delete r[t]:r[t]=n,r}function T(e){return e===`dashed`||e===`dotted`?e:`solid`}exports.WidgetSettings=x;
|
|
2
|
+
//# sourceMappingURL=WidgetSettings-B57_tUke.cjs.map
|