@tradecanvas/chart 1.82.0 → 1.83.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.
Files changed (111) hide show
  1. package/dist/LayoutSession-B4qMfaP7.cjs +2 -0
  2. package/dist/LayoutSession-B4qMfaP7.cjs.map +1 -0
  3. package/dist/{LayoutSession-CLlUszTA.js → LayoutSession-BoiToS9y.js} +617 -379
  4. package/dist/LayoutSession-BoiToS9y.js.map +1 -0
  5. package/dist/{WidgetCommandPalette-p4DQCe57.js → WidgetCommandPalette-DHGvEl9n.js} +2 -2
  6. package/dist/{WidgetCommandPalette-p4DQCe57.js.map → WidgetCommandPalette-DHGvEl9n.js.map} +1 -1
  7. package/dist/{WidgetCommandPalette-BNBL1mO2.cjs → WidgetCommandPalette-Ol8Z923q.cjs} +2 -2
  8. package/dist/{WidgetCommandPalette-BNBL1mO2.cjs.map → WidgetCommandPalette-Ol8Z923q.cjs.map} +1 -1
  9. package/dist/{WidgetDrawingSettings-D7l2NkVj.js → WidgetDrawingSettings-CSB88SvC.js} +2 -2
  10. package/dist/{WidgetDrawingSettings-D7l2NkVj.js.map → WidgetDrawingSettings-CSB88SvC.js.map} +1 -1
  11. package/dist/{WidgetDrawingSettings-BqGuAMz3.cjs → WidgetDrawingSettings-XL1Z9-ly.cjs} +2 -2
  12. package/dist/{WidgetDrawingSettings-BqGuAMz3.cjs.map → WidgetDrawingSettings-XL1Z9-ly.cjs.map} +1 -1
  13. package/dist/{WidgetIndicatorPicker-BOxIKUn-.cjs → WidgetIndicatorPicker-K01RJSLs.cjs} +2 -2
  14. package/dist/{WidgetIndicatorPicker-BOxIKUn-.cjs.map → WidgetIndicatorPicker-K01RJSLs.cjs.map} +1 -1
  15. package/dist/{WidgetIndicatorPicker-Bk0y6c35.js → WidgetIndicatorPicker-a4rpptDc.js} +2 -2
  16. package/dist/{WidgetIndicatorPicker-Bk0y6c35.js.map → WidgetIndicatorPicker-a4rpptDc.js.map} +1 -1
  17. package/dist/{WidgetIndicatorSettings-BmOqq2Fv.js → WidgetIndicatorSettings-BmIFp19I.js} +3 -3
  18. package/dist/{WidgetIndicatorSettings-BmOqq2Fv.js.map → WidgetIndicatorSettings-BmIFp19I.js.map} +1 -1
  19. package/dist/{WidgetIndicatorSettings-DrPcNSxS.cjs → WidgetIndicatorSettings-CJWR-x9q.cjs} +2 -2
  20. package/dist/{WidgetIndicatorSettings-DrPcNSxS.cjs.map → WidgetIndicatorSettings-CJWR-x9q.cjs.map} +1 -1
  21. package/dist/{WidgetSettings-C6m55YF0.js → WidgetSettings-9clgY3_1.js} +2 -2
  22. package/dist/{WidgetSettings-C6m55YF0.js.map → WidgetSettings-9clgY3_1.js.map} +1 -1
  23. package/dist/{WidgetSettings-B57_tUke.cjs → WidgetSettings-D6COgRaB.cjs} +2 -2
  24. package/dist/{WidgetSettings-B57_tUke.cjs.map → WidgetSettings-D6COgRaB.cjs.map} +1 -1
  25. package/dist/{WidgetSymbolSearch-8HMKUv2P.cjs → WidgetSymbolSearch-6EB31v-O.cjs} +2 -2
  26. package/dist/{WidgetSymbolSearch-8HMKUv2P.cjs.map → WidgetSymbolSearch-6EB31v-O.cjs.map} +1 -1
  27. package/dist/{WidgetSymbolSearch-BdI1egoq.js → WidgetSymbolSearch-DToOqJoG.js} +2 -2
  28. package/dist/{WidgetSymbolSearch-BdI1egoq.js.map → WidgetSymbolSearch-DToOqJoG.js.map} +1 -1
  29. package/dist/grid/ChartGrid.d.ts +9 -1
  30. package/dist/grid/ChartGrid.d.ts.map +1 -1
  31. package/dist/grid/gridShapes.d.ts +43 -0
  32. package/dist/grid/gridShapes.d.ts.map +1 -0
  33. package/dist/i18n-D0SPfH2s.cjs +2 -0
  34. package/dist/i18n-D0SPfH2s.cjs.map +1 -0
  35. package/dist/{i18n-BMzvClhl.js → i18n-nWmX3G55.js} +11 -1
  36. package/dist/i18n-nWmX3G55.js.map +1 -0
  37. package/dist/index.cjs +1 -1
  38. package/dist/index.cjs.map +1 -1
  39. package/dist/index.d.ts +1 -1
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/index.js +155 -179
  42. package/dist/index.js.map +1 -1
  43. package/dist/{indicatorInputWords-Dn_gEPug.js → indicatorInputWords-D0SGBaUD.js} +2 -2
  44. package/dist/{indicatorInputWords-Dn_gEPug.js.map → indicatorInputWords-D0SGBaUD.js.map} +1 -1
  45. package/dist/{indicatorInputWords-DGObUFDn.cjs → indicatorInputWords-HqTGFokF.cjs} +2 -2
  46. package/dist/{indicatorInputWords-DGObUFDn.cjs.map → indicatorInputWords-HqTGFokF.cjs.map} +1 -1
  47. package/dist/widget/ChartWidget.d.ts +2 -0
  48. package/dist/widget/ChartWidget.d.ts.map +1 -1
  49. package/dist/widget/ChartWidgetGrid.d.ts +7 -10
  50. package/dist/widget/ChartWidgetGrid.d.ts.map +1 -1
  51. package/dist/widget/GridLayoutPicker.d.ts +55 -0
  52. package/dist/widget/GridLayoutPicker.d.ts.map +1 -0
  53. package/dist/widget/WatchlistStore.d.ts +11 -0
  54. package/dist/widget/WatchlistStore.d.ts.map +1 -1
  55. package/dist/widget/WidgetDockArea.d.ts +1 -0
  56. package/dist/widget/WidgetDockArea.d.ts.map +1 -1
  57. package/dist/widget/WidgetToolbar.d.ts.map +1 -1
  58. package/dist/widget/WidgetWatchlist.d.ts +3 -0
  59. package/dist/widget/WidgetWatchlist.d.ts.map +1 -1
  60. package/dist/widget/WidgetWatchlists.d.ts +1 -0
  61. package/dist/widget/WidgetWatchlists.d.ts.map +1 -1
  62. package/dist/widget/index.cjs +50 -4
  63. package/dist/widget/index.cjs.map +1 -1
  64. package/dist/widget/index.d.ts +1 -0
  65. package/dist/widget/index.d.ts.map +1 -1
  66. package/dist/widget/index.js +1662 -1532
  67. package/dist/widget/index.js.map +1 -1
  68. package/dist/widget/locales/ar.d.ts.map +1 -1
  69. package/dist/widget/locales/cs.d.ts.map +1 -1
  70. package/dist/widget/locales/da.d.ts.map +1 -1
  71. package/dist/widget/locales/de.d.ts.map +1 -1
  72. package/dist/widget/locales/el.d.ts.map +1 -1
  73. package/dist/widget/locales/en.d.ts +5 -0
  74. package/dist/widget/locales/en.d.ts.map +1 -1
  75. package/dist/widget/locales/es.d.ts.map +1 -1
  76. package/dist/widget/locales/et.d.ts.map +1 -1
  77. package/dist/widget/locales/fa.d.ts.map +1 -1
  78. package/dist/widget/locales/fr.d.ts.map +1 -1
  79. package/dist/widget/locales/he.d.ts.map +1 -1
  80. package/dist/widget/locales/hu.d.ts.map +1 -1
  81. package/dist/widget/locales/id.d.ts.map +1 -1
  82. package/dist/widget/locales/index.cjs +1 -1
  83. package/dist/widget/locales/index.cjs.map +1 -1
  84. package/dist/widget/locales/index.js +141 -1
  85. package/dist/widget/locales/index.js.map +1 -1
  86. package/dist/widget/locales/it.d.ts.map +1 -1
  87. package/dist/widget/locales/ja.d.ts.map +1 -1
  88. package/dist/widget/locales/ko.d.ts.map +1 -1
  89. package/dist/widget/locales/ms.d.ts.map +1 -1
  90. package/dist/widget/locales/nb.d.ts.map +1 -1
  91. package/dist/widget/locales/nl.d.ts.map +1 -1
  92. package/dist/widget/locales/pl.d.ts.map +1 -1
  93. package/dist/widget/locales/pt.d.ts.map +1 -1
  94. package/dist/widget/locales/ro.d.ts.map +1 -1
  95. package/dist/widget/locales/ru.d.ts.map +1 -1
  96. package/dist/widget/locales/sk.d.ts.map +1 -1
  97. package/dist/widget/locales/sv.d.ts.map +1 -1
  98. package/dist/widget/locales/th.d.ts.map +1 -1
  99. package/dist/widget/locales/tr.d.ts.map +1 -1
  100. package/dist/widget/locales/vi.d.ts.map +1 -1
  101. package/dist/widget/locales/zh.d.ts.map +1 -1
  102. package/dist/widget/locales/zhHant.d.ts.map +1 -1
  103. package/dist/widget/types.d.ts +7 -1
  104. package/dist/widget/types.d.ts.map +1 -1
  105. package/package.json +3 -3
  106. package/dist/LayoutSession-CLlUszTA.js.map +0 -1
  107. package/dist/LayoutSession-Cs8SJ1LU.cjs +0 -2
  108. package/dist/LayoutSession-Cs8SJ1LU.cjs.map +0 -1
  109. package/dist/i18n-BMzvClhl.js.map +0 -1
  110. package/dist/i18n-Dq-eMISy.cjs +0 -2
  111. package/dist/i18n-Dq-eMISy.cjs.map +0 -1
@@ -1 +1 @@
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
+ {"version":3,"file":"WidgetIndicatorSettings-CJWR-x9q.cjs","names":[],"sources":["../src/widget/WidgetIndicatorSettings.ts"],"sourcesContent":["import type { IndicatorInputSpec, IndicatorIntervals, IndicatorPlot, IndicatorPlotStyle, IndicatorStyleConfig } from '@tradecanvas/commons';\nimport { PRICE_SOURCES, parseIndicatorSource } from '@tradecanvas/commons';\nimport { describeInputs, isSeriesPalette, type IndicatorInputInfo } from '@tradecanvas/core';\nimport { FOCUSABLE, keepTabInside } from './focusTrap.js';\nimport { createIcon } from './icons.js';\nimport { EN_MESSAGES, type Translator } from './i18n.js';\nimport { WidgetContextMenu } from './WidgetContextMenu.js';\nimport { colorInput, numberInput, settingsRow, toggleSwitch } from './settingsControls.js';\nimport { renderStyleTab, renderVisibilityTab } from './indicatorSettingsTabs.js';\nimport { choiceLabel, inputLabel } from './indicatorInputWords.js';\n\nexport type IndicatorParamValue = number | string | boolean;\n\nexport interface IndicatorSettingsTarget {\n instanceId: string;\n /** The kind of indicator (`'rsi'`): what \"Save as default\" is for. */\n indicatorId: string;\n name: string;\n /** Default config — defines the editable keys and their types. */\n defaults: Record<string, unknown>;\n /** Current values, overlaid on defaults. */\n params: Record<string, unknown>;\n /** How parameters are edited (the descriptor's `inputs`). */\n inputs?: Readonly<Record<string, IndicatorInputSpec>>;\n /** Other indicators' lines a source can be: value `ind:<instanceId>:<key>`. */\n lineSources?: readonly { value: string; label: string }[];\n /** What it draws; with `colors`, the lines of the Style tab. */\n plots?: readonly IndicatorPlot[];\n colors?: readonly string[];\n lineWidth?: number;\n /** Each line's own dash and visibility, by its key. */\n plotStyles?: Readonly<Record<string, IndicatorPlotStyle>>;\n /** Its levels and its indicator's defaults; with them, the levels under the lines. */\n levels?: readonly number[];\n defaultLevels?: readonly number[];\n /** Whether `levels` are the instance's own; not: its indicator's defaults, which Cancel leaves it on. */\n ownLevels?: boolean;\n /** The price scale an overlay is on; set only for price-pane overlays. */\n scale?: 'right' | 'left';\n /** The intervals it shows on; unset or null: all of them. */\n intervals?: IndicatorIntervals | null;\n}\n\n/** What \"Save as default\" keeps: new indicators of the kind start with it. */\nexport interface IndicatorDefault {\n params: Record<string, IndicatorParamValue>;\n style: IndicatorStyleConfig;\n}\n\nexport interface IndicatorSettingsCallbacks {\n onApply: (instanceId: string, params: Record<string, IndicatorParamValue>) => void;\n onStyle?: (instanceId: string, style: IndicatorStyleConfig) => void;\n /** `null` restores the indicator's default levels. */\n onLevels?: (instanceId: string, levels: number[] | null) => void;\n /** Move an overlay to the left price scale or back to the price scale. */\n onScale?: (instanceId: string, scale: 'right' | 'left') => void;\n /** The intervals it shows on (`null`: all); with it, the Visibility tab. */\n onIntervals?: (instanceId: string, intervals: IndicatorIntervals | null) => void;\n /** \"Save as default\" in the Defaults menu. */\n onSaveDefault?: (indicatorId: string, saved: IndicatorDefault) => void;\n onClose: () => void;\n}\n\ntype Tab = 'inputs' | 'style' | 'visibility';\n\n/** What the dialog changes besides the inputs: as it was on opening, and as it is. */\ninterface Look {\n colors: string[];\n lineWidth: number;\n plots: Record<string, IndicatorPlotStyle>;\n levels: number[];\n scale?: 'right' | 'left';\n intervals: IndicatorIntervals | null;\n}\n\n/** Ids for the dialog's labelling (several widgets may share a page). */\nlet nextDialogId = 1;\n\nconst DEFAULT_LINE_WIDTH = 1.5;\n/** A line as it is with no style of its own: shown, and dashed as its indicator declares it. */\nfunction plainPlot(plots: readonly IndicatorPlot[] | undefined, key: string): IndicatorPlotStyle {\n return { visible: true, lineStyle: plots?.find((p) => p.key === key)?.lineStyle ?? 'solid' };\n}\n\nconst same = (a: unknown, b: unknown) => JSON.stringify(a) === JSON.stringify(b);\n/** A copy of plain values (numbers, strings, flags, lists of them). */\nconst copy = <T>(value: T): T => JSON.parse(JSON.stringify(value)) as T;\n\nfunction titleCase(key: string): string {\n // camelCase / snake_case → \"Title Case\"\n return key\n .replace(/_/g, ' ')\n .replace(/([a-z0-9])([A-Z])/g, '$1 $2')\n .replace(/^./, (c) => c.toUpperCase());\n}\n\n/** Each line put back as `to` has it; one `to` has no style for goes back to plain. */\nfunction plotsBack(\n from: Record<string, IndicatorPlotStyle>,\n to: Record<string, IndicatorPlotStyle>,\n plots: readonly IndicatorPlot[] | undefined,\n): Record<string, IndicatorPlotStyle> {\n return Object.fromEntries([...new Set([...Object.keys(from), ...Object.keys(to)])].map((key) => [key, { ...plainPlot(plots, key), ...to[key] }]));\n}\n\n/**\n * Per-indicator settings: its inputs; its style (each line shown or not, its\n * colours and dash, the line width, the scale, the levels); and the\n * intervals it shows on. Every edit shows at once; Cancel (or Escape) puts\n * it all back as it was, Ok keeps it. The Defaults menu resets it, or keeps\n * it for the next ones of its kind.\n */\nexport class WidgetIndicatorSettings {\n private backdrop: HTMLDivElement;\n private modal: HTMLDivElement;\n private bodyEl: HTMLDivElement;\n private titleEl: HTMLHeadingElement;\n private tabsEl: HTMLDivElement;\n private defaultsBtn: HTMLButtonElement;\n private menu: WidgetContextMenu | null = null;\n private callbacks: IndicatorSettingsCallbacks;\n private target: IndicatorSettingsTarget | null = null;\n private draft: Record<string, IndicatorParamValue> = {};\n private initial: { params: Record<string, IndicatorParamValue>; look: Look } | null = null;\n private look: Look = { colors: [], lineWidth: DEFAULT_LINE_WIDTH, plots: {}, levels: [], intervals: null };\n private tab: Tab = 'inputs';\n private tabButtons: HTMLButtonElement[] = [];\n private returnFocus: HTMLElement | null = null;\n private readonly uid = nextDialogId++;\n private readonly t: Translator;\n\n constructor(host: HTMLElement, callbacks: IndicatorSettingsCallbacks, t?: Translator) {\n this.callbacks = callbacks;\n this.t = t ?? ((key) => EN_MESSAGES[key]);\n\n this.backdrop = document.createElement('div');\n this.backdrop.className = 'tcw-modal-backdrop';\n this.backdrop.hidden = true;\n // A press that starts in the dialog (a slider dragged, text selected) and ends on the backdrop doesn't cancel it.\n let pressedBackdrop = false;\n this.backdrop.addEventListener('pointerdown', (e) => {\n pressedBackdrop = e.target === this.backdrop;\n });\n this.backdrop.addEventListener('click', (e) => {\n if (e.target === this.backdrop && pressedBackdrop) this.finish(false);\n pressedBackdrop = false;\n });\n\n this.modal = document.createElement('div');\n this.modal.className = 'tcw-modal tcw-modal-narrow tcw-indi';\n this.modal.setAttribute('role', 'dialog');\n this.modal.setAttribute('aria-modal', 'true');\n this.modal.setAttribute('aria-labelledby', `tcw-indi-title-${this.uid}`);\n // Focusable, so keys still reach it after a click on its blank parts.\n this.modal.tabIndex = -1;\n this.modal.addEventListener('keydown', (e) => this.onKeyDown(e));\n\n const header = document.createElement('div');\n header.className = 'tcw-modal-header';\n this.titleEl = document.createElement('h3');\n this.titleEl.id = `tcw-indi-title-${this.uid}`;\n const closeBtn = document.createElement('button');\n closeBtn.type = 'button';\n closeBtn.className = 'tcw-modal-close';\n closeBtn.setAttribute('aria-label', this.t('indicatorSettings.close'));\n closeBtn.innerHTML = createIcon('x', 16);\n closeBtn.addEventListener('click', () => this.finish(false));\n header.append(this.titleEl, closeBtn);\n\n this.tabsEl = document.createElement('div');\n this.tabsEl.className = 'tcw-modal-tabs';\n this.tabsEl.setAttribute('role', 'tablist');\n this.tabsEl.addEventListener('keydown', (e) => this.onTabKey(e));\n\n this.bodyEl = document.createElement('div');\n this.bodyEl.className = 'tcw-modal-body';\n this.bodyEl.id = `tcw-indi-panel-${this.uid}`;\n this.bodyEl.setAttribute('role', 'tabpanel');\n\n this.defaultsBtn = this.footerButton('defaults', 'tcw-footer-menu', () => this.openDefaults());\n this.modal.append(header, this.tabsEl, this.bodyEl, this.buildFooter());\n this.backdrop.appendChild(this.modal);\n host.appendChild(this.backdrop);\n }\n\n /** The Defaults menu on the left; Cancel and OK on the right. */\n private buildFooter(): HTMLDivElement {\n this.defaultsBtn.setAttribute('aria-haspopup', 'menu');\n this.defaultsBtn.setAttribute('aria-expanded', 'false');\n this.defaultsBtn.innerHTML = `<span></span>${createIcon('chevronDown', 12)}`;\n this.defaultsBtn.querySelector('span')!.textContent = this.t('indicatorSettings.defaults');\n const end = document.createElement('div');\n end.className = 'tcw-footer-end';\n end.append(\n this.footerButton('cancel', 'tcw-footer-cancel', () => this.finish(false), this.t('common.cancel')),\n this.footerButton('ok', 'tcw-done-btn', () => this.finish(true), this.t('drawingSettings.ok')),\n );\n const footer = document.createElement('div');\n footer.className = 'tcw-modal-footer';\n footer.append(this.defaultsBtn, end);\n return footer;\n }\n\n open(target: IndicatorSettingsTarget): void {\n if (this.target) this.finish(true);\n if (this.backdrop.hidden) this.returnFocus = document.activeElement instanceof HTMLElement ? document.activeElement : null;\n this.target = target;\n this.titleEl.textContent = this.t('indicatorSettings.title').replace('{name}', target.name);\n this.draft = {};\n this.look = {\n colors: [...(target.colors ?? [])],\n lineWidth: target.lineWidth ?? DEFAULT_LINE_WIDTH,\n plots: Object.fromEntries(Object.entries(target.plotStyles ?? {}).map(([key, plot]) => [key, { ...plot }])),\n levels: [...(target.levels ?? [])],\n scale: target.scale,\n intervals: target.intervals ?? null,\n };\n this.initial = { params: this.currentParams(), look: copy(this.look) };\n this.tab = 'inputs';\n this.renderTabs();\n this.renderBody();\n this.backdrop.hidden = false;\n // Start on the first input, else the tabs, else the dialog itself.\n const firstTab = this.tabsEl.hidden ? null : this.tabButtons[0] ?? null;\n (this.bodyEl.querySelector<HTMLElement>(FOCUSABLE) ?? firstTab ?? this.modal).focus();\n }\n\n /** Close, keeping what was changed (as Ok does). */\n close(): void {\n this.finish(true);\n }\n\n isOpen(): boolean {\n return !this.backdrop.hidden;\n }\n\n destroy(): void {\n this.menu?.destroy();\n this.backdrop.remove();\n }\n\n /** Close with Ok (`keep`: a value still being typed applies) or Cancel (everything goes back). */\n private finish(keep: boolean): void {\n if (!this.target) return;\n if (keep) {\n const active = document.activeElement;\n if ((active instanceof HTMLInputElement || active instanceof HTMLSelectElement) && this.modal.contains(active)) {\n active.dispatchEvent(new Event('change'));\n }\n } else {\n this.revert();\n }\n // No target: a field's change on its way out (its blur) no longer applies.\n this.target = null;\n this.menu?.close();\n this.backdrop.hidden = true;\n this.callbacks.onClose();\n // Back to where the dialog was opened from (a legend row, the object tree).\n if (this.returnFocus?.isConnected) this.returnFocus.focus();\n this.returnFocus = null;\n }\n\n /** Everything as it was on opening: only what changed is sent back. */\n private revert(): void {\n const target = this.target;\n const initial = this.initial;\n if (!target || !initial) return;\n const id = target.instanceId;\n if (!same(this.currentParams(), initial.params)) this.callbacks.onApply(id, { ...initial.params });\n const was = initial.look;\n const style: IndicatorStyleConfig = {};\n if (!same(this.look.colors, was.colors)) style.colors = [...was.colors];\n if (this.look.lineWidth !== was.lineWidth) style.lineWidths = [was.lineWidth];\n if (!same(this.look.plots, was.plots)) style.plots = plotsBack(this.look.plots, was.plots, target.plots);\n if (Object.keys(style).length > 0) this.callbacks.onStyle?.(id, style);\n if (!same(this.look.levels, was.levels)) this.callbacks.onLevels?.(id, target.ownLevels ? [...was.levels] : null);\n if (this.look.scale !== was.scale && was.scale) this.callbacks.onScale?.(id, was.scale);\n if (!same(this.look.intervals, was.intervals)) this.callbacks.onIntervals?.(id, was.intervals);\n }\n\n private currentParams(): Record<string, IndicatorParamValue> {\n const target = this.target;\n if (!target) return {};\n return { ...target.defaults, ...target.params, ...this.draft } as Record<string, IndicatorParamValue>;\n }\n\n // --- Tabs ---\n\n private tabs(): Tab[] {\n const target = this.target;\n if (!target) return [];\n const lines = !!(target.plots?.length && target.colors && this.callbacks.onStyle);\n const levels = !!(target.levels && this.callbacks.onLevels);\n return [\n 'inputs',\n ...(lines || levels ? ['style' as const] : []),\n ...(this.callbacks.onIntervals ? ['visibility' as const] : []),\n ];\n }\n\n /** The tab buttons for this indicator; built on open, then only marked (focus stays put). */\n private renderTabs(): void {\n const tabs = this.tabs();\n this.tabsEl.hidden = tabs.length < 2;\n this.tabButtons = tabs.map((tab) => {\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.className = 'tcw-modal-tab';\n btn.id = `tcw-indi-tab-${this.uid}-${tab}`;\n btn.setAttribute('role', 'tab');\n btn.setAttribute('aria-controls', this.bodyEl.id);\n btn.dataset.tab = tab;\n btn.textContent = this.t(`indicatorSettings.tab.${tab}`);\n btn.addEventListener('click', () => this.selectTab(tab));\n return btn;\n });\n this.tabsEl.replaceChildren(...this.tabButtons);\n this.markTabs();\n }\n\n private markTabs(): void {\n for (const btn of this.tabButtons) {\n const selected = btn.dataset.tab === this.tab;\n btn.classList.toggle('tcw-active', selected);\n btn.setAttribute('aria-selected', String(selected));\n btn.tabIndex = selected ? 0 : -1;\n if (selected) this.bodyEl.setAttribute('aria-labelledby', btn.id);\n }\n }\n\n private selectTab(tab: Tab, focus = false): void {\n this.tab = tab;\n this.markTabs();\n this.renderBody();\n if (focus) this.tabButtons.find((b) => b.dataset.tab === tab)?.focus();\n }\n\n /** Arrow keys (the other way right to left), Home and End move between tabs. */\n private onTabKey(e: KeyboardEvent): void {\n const tabs = this.tabButtons.map((b) => b.dataset.tab as Tab);\n const at = tabs.indexOf(this.tab);\n const rtl = this.modal.closest('[dir]')?.getAttribute('dir') === 'rtl';\n const [forward, back] = rtl ? ['ArrowLeft', 'ArrowRight'] : ['ArrowRight', 'ArrowLeft'];\n const next = e.key === forward ? (at + 1) % tabs.length\n : e.key === back ? (at - 1 + tabs.length) % tabs.length\n : e.key === 'Home' ? 0\n : e.key === 'End' ? tabs.length - 1\n : -1;\n if (next < 0 || tabs.length === 0) return;\n e.preventDefault();\n this.selectTab(tabs[next], true);\n }\n\n /** Escape cancels; Tab stays inside the dialog. */\n private onKeyDown(e: KeyboardEvent): void {\n if (e.key === 'Escape') {\n e.preventDefault();\n e.stopPropagation();\n this.finish(false);\n return;\n }\n keepTabInside(e, this.modal);\n }\n\n private renderBody(): void {\n this.bodyEl.replaceChildren();\n const target = this.target;\n if (!target) return;\n if (this.tab === 'style') this.renderStyle(target);\n else if (this.tab === 'visibility') renderVisibilityTab(this.bodyEl, this.look.intervals, (next) => this.setIntervals(next), this.t);\n else this.renderInputs();\n }\n\n // --- Inputs ---\n\n private currentValue(key: string): IndicatorParamValue {\n if (key in this.draft) return this.draft[key];\n const cur = this.target?.params[key];\n const def = this.target?.defaults[key];\n return (cur ?? def) as IndicatorParamValue;\n }\n\n private setValue(key: string, value: IndicatorParamValue): void {\n if (!this.target) return;\n this.draft[key] = value;\n this.callbacks.onApply(this.target.instanceId, { ...this.draft });\n }\n\n private renderInputs(): void {\n const section = document.createElement('div');\n section.className = 'tcw-settings-section';\n const inputs = describeInputs({ defaultConfig: this.target!.defaults, inputs: this.target!.inputs });\n if (inputs.length === 0) section.appendChild(this.note(this.t('indicatorSettings.noInputs')));\n for (const input of inputs) section.appendChild(this.fieldRow(input));\n this.bodyEl.appendChild(section);\n }\n\n /** One input's row: its control follows the kind the indicator declares, or the one its default implies. */\n private fieldRow(input: IndicatorInputInfo): HTMLLabelElement {\n const key = input.name;\n const value = this.currentValue(key);\n let control: HTMLElement;\n switch (input.kind) {\n case 'source': control = this.sourceControl(key, String(value ?? 'close')); break;\n case 'choice': control = this.choiceControl(key, input.options ?? [], value); break;\n case 'boolean': control = toggleSwitch(Boolean(value), (v) => this.setValue(key, v)); break;\n case 'int':\n case 'number':\n control = numberInput(Number(value), { min: input.min, max: input.max, step: input.step, whole: input.kind === 'int' }, (v) => this.setValue(key, v));\n break;\n case 'color': control = colorInput(String(value), (v) => this.setValue(key, v)); break;\n default: control = this.textControl(key, String(value ?? ''));\n }\n return settingsRow(input.label ?? inputLabel(key, this.t) ?? titleCase(key), control);\n }\n\n private sourceControl(key: string, value: string): HTMLSelectElement {\n const select = document.createElement('select');\n select.className = 'tcw-indi-input tcw-indi-select';\n const prices = document.createElement('optgroup');\n prices.label = this.t('indicatorSettings.source.prices');\n for (const source of PRICE_SOURCES) prices.appendChild(this.option(source, source));\n select.appendChild(prices);\n const lines = this.target?.lineSources ?? [];\n if (lines.length) {\n const group = document.createElement('optgroup');\n group.label = this.t('indicatorSettings.source.indicators');\n for (const line of lines) group.appendChild(this.option(line.value, line.label));\n select.appendChild(group);\n }\n select.value = value;\n if (select.value !== value) select.value = 'close'; // a line that is gone\n select.addEventListener('change', () => this.setValue(key, select.value));\n return select;\n }\n\n private choiceControl(key: string, options: readonly (string | number)[], value: IndicatorParamValue): HTMLSelectElement {\n const select = document.createElement('select');\n select.className = 'tcw-indi-input tcw-indi-select';\n for (const opt of options) select.appendChild(this.option(String(opt), choiceLabel(key, opt, this.t) ?? String(opt)));\n select.value = String(value);\n select.addEventListener('change', () => {\n const picked = options.find((o) => String(o) === select.value);\n if (picked !== undefined) this.setValue(key, picked);\n });\n return select;\n }\n\n private textControl(key: string, value: string): HTMLInputElement {\n const input = document.createElement('input');\n input.type = 'text';\n input.className = 'tcw-indi-input';\n input.value = value;\n input.addEventListener('change', () => this.setValue(key, input.value));\n return input;\n }\n\n // --- Style and visibility ---\n\n private renderStyle(target: IndicatorSettingsTarget): void {\n const id = target.instanceId;\n // Once closed, nothing: a field's change on its way out (its blur after a Cancel) no longer applies.\n const open = () => this.target === target;\n const onStyle = (style: IndicatorStyleConfig) => open() && this.callbacks.onStyle?.(id, style);\n const onLevels = (levels: number[]) => open() && this.callbacks.onLevels?.(id, levels);\n const onScale = (scale: 'right' | 'left') => open() && this.callbacks.onScale?.(id, scale);\n renderStyleTab(this.bodyEl, {\n plots: target.colors && this.callbacks.onStyle ? target.plots ?? [] : [],\n colors: this.look.colors,\n plotStyles: this.look.plots,\n lineWidth: this.look.lineWidth,\n scale: target.scale && this.callbacks.onScale ? this.look.scale : undefined,\n levels: target.levels && this.callbacks.onLevels ? this.look.levels : undefined,\n }, {\n color: (index, value) => {\n if (!open()) return;\n const next = [...this.look.colors];\n while (next.length <= index) next.push(next[0] ?? value);\n next[index] = value;\n this.look.colors = next;\n onStyle({ colors: [...next] });\n },\n plot: (key, patch) => {\n if (!open()) return;\n this.look.plots = { ...this.look.plots, [key]: { ...this.look.plots[key], ...patch } };\n onStyle({ plots: { [key]: patch } });\n },\n lineWidth: (width) => {\n if (!open()) return;\n this.look.lineWidth = width;\n onStyle({ lineWidths: [width] });\n },\n scale: (scale) => {\n if (!open()) return;\n this.look.scale = scale;\n onScale(scale);\n },\n levels: (levels) => {\n if (!open()) return;\n this.look.levels = levels;\n onLevels([...levels]);\n },\n }, this.t, this.uid);\n }\n\n private setIntervals(intervals: IndicatorIntervals | null): void {\n if (!this.target) return;\n this.look.intervals = intervals;\n this.callbacks.onIntervals?.(this.target.instanceId, intervals);\n }\n\n // --- Defaults ---\n\n private openDefaults(): void {\n if (this.menu?.isOpen()) {\n this.menu.close();\n return;\n }\n this.menu ??= new WidgetContextMenu(this.modal, this.t('indicatorSettings.defaults'));\n const at = this.defaultsBtn.getBoundingClientRect();\n const box = this.modal.getBoundingClientRect();\n const entries = [\n { id: 'reset', label: this.t('indicatorSettings.resetSettings'), icon: 'reset' },\n ...(this.callbacks.onSaveDefault ? [{ id: 'save', label: this.t('indicatorSettings.saveAsDefault'), icon: 'save' }] : []),\n ];\n // From the button's top: in the footer there is no room below, so it opens above the button.\n this.menu.open(entries, at.left - box.left, at.top - box.top, (pick) => {\n if (pick === 'reset') this.resetAll();\n else this.saveDefault();\n }, this.defaultsBtn);\n }\n\n /** The inputs, the lines' own styles, the levels and the intervals back to the indicator's own. */\n private resetAll(): void {\n const target = this.target;\n if (!target) return;\n const id = target.instanceId;\n const params = { ...target.defaults } as Record<string, IndicatorParamValue>;\n this.draft = { ...params };\n this.callbacks.onApply(id, { ...params });\n if (Object.keys(this.look.plots).length > 0) {\n this.callbacks.onStyle?.(id, { plots: plotsBack(this.look.plots, {}, target.plots) });\n this.look.plots = {};\n }\n if (target.levels && this.callbacks.onLevels) {\n this.callbacks.onLevels(id, null);\n this.look.levels = [...(target.defaultLevels ?? [])];\n }\n if (this.look.intervals) this.setIntervals(null);\n this.renderBody();\n }\n\n /**\n * Keep what was chosen for the next ones of the kind: the inputs that differ\n * from the indicator's own (not a line of another indicator, which the next\n * one won't have), and of the look only what someone picked. Colours the\n * chart gave stay its to give, so a second one still gets a colour of its own.\n */\n private saveDefault(): void {\n const target = this.target;\n if (!target) return;\n const params = Object.fromEntries(Object.entries(this.currentParams()).filter(([key, value]) =>\n !same(value, target.defaults[key]) && !(typeof value === 'string' && parseIndicatorSource(value)))) as Record<string, IndicatorParamValue>;\n const style: IndicatorStyleConfig = {};\n if (!isSeriesPalette(this.look.colors)) style.colors = [...this.look.colors];\n if (this.look.lineWidth !== DEFAULT_LINE_WIDTH) style.lineWidths = [this.look.lineWidth];\n if (Object.keys(this.look.plots).length > 0) style.plots = copy(this.look.plots);\n this.callbacks.onSaveDefault?.(target.indicatorId, { params, style });\n }\n\n // --- Pieces ---\n\n private footerButton(act: string, className: string, onClick: () => void, text?: string): HTMLButtonElement {\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.className = className;\n btn.dataset.act = act;\n if (text) btn.textContent = text;\n btn.addEventListener('click', onClick);\n return btn;\n }\n\n private note(text: string): HTMLDivElement {\n const el = document.createElement('div');\n el.className = 'tcw-settings-label';\n el.textContent = text;\n return el;\n }\n\n private option(value: string, label: string): HTMLOptionElement {\n const opt = document.createElement('option');\n opt.value = value;\n opt.textContent = label;\n return opt;\n }\n}\n"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { a as e, c as t, d as n, f as r, i, m as a, o, p as s, r as c, s as l, t as u, u as d, y as f } from "./widgetLocales-BX7VRdl5.js";
2
2
  import { i as p, n as m, r as h, t as g } from "./focusTrap-Br9Zfje0.js";
3
3
  import { f as _, l as v } from "./settingsControls-DIjzCFHI.js";
4
- import { r as y } from "./i18n-BMzvClhl.js";
4
+ import { r as y } from "./i18n-nWmX3G55.js";
5
5
  import { CHART_STYLE_KEYS as b } from "@tradecanvas/commons";
6
6
  //#region src/widget/settingsRows.ts
7
7
  var x = 0, S = 0, C = 20;
@@ -569,4 +569,4 @@ function V(e) {
569
569
  //#endregion
570
570
  export { L as WidgetSettings };
571
571
 
572
- //# sourceMappingURL=WidgetSettings-C6m55YF0.js.map
572
+ //# sourceMappingURL=WidgetSettings-9clgY3_1.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"WidgetSettings-C6m55YF0.js","names":[],"sources":["../src/widget/settingsRows.ts","../src/widget/WidgetSettings.ts"],"sourcesContent":["import { toHex } from './settingsControls.js';\nimport { fill, type Translator } from './i18n.js';\n\n/**\n * The rows of the chart settings: a section, a colour, a switch, a choice,\n * a slider and a number, each with its label and calling back on change.\n */\n\n/** Ids for the colour rows' descriptions. */\nlet colorRowCount = 0;\n/** Ids for the number rows' hints. */\nlet numberRowCount = 0;\n/** The most decimals a number in a hint shows: the smallest size taken, 0.00000001, has 8. */\nconst HINT_MAX_DECIMALS = 20;\n\n/** A group of rows; a title names it to a screen reader too (rows named alike under several titles). */\nexport function settingsSection(title?: string): HTMLDivElement {\n const div = document.createElement('div');\n div.className = 'tcw-settings-section';\n if (title) {\n const heading = document.createElement('div');\n heading.className = 'tcw-settings-section-title';\n heading.textContent = title;\n div.appendChild(heading);\n div.setAttribute('role', 'group');\n div.setAttribute('aria-label', title);\n }\n return div;\n}\n\nfunction labelledRow(label: string): HTMLDivElement {\n const row = document.createElement('div');\n row.className = 'tcw-settings-row';\n const lbl = document.createElement('span');\n lbl.className = 'tcw-settings-label';\n lbl.textContent = label;\n row.appendChild(lbl);\n return row;\n}\n\n/**\n * A colour of the look, shown again from `look.read()`. With `look.start`,\n * null is the part's own colour: shown as Auto, the picker starting from\n * `start()`, and once set a button takes it back.\n */\nexport function colorRow(\n label: string,\n value: string | null,\n onChange: (v: string | null) => void,\n look: { read: () => string | null; start?: () => string | number | boolean | null },\n t: Translator,\n): { row: HTMLDivElement; sync: () => void } {\n const row = labelledRow(label);\n const wrap = document.createElement('div');\n wrap.className = 'tcw-color-picker-wrap';\n const input = document.createElement('input');\n input.type = 'color';\n input.setAttribute('aria-label', label);\n // What the swatch can't say (Auto, a colour with transparency) is read with it.\n const hex = document.createElement('span');\n hex.className = 'tcw-color-hex';\n hex.id = `tcw-color-${++colorRowCount}`;\n input.setAttribute('aria-describedby', hex.id);\n\n let back: HTMLButtonElement | null = null;\n const show = (v: string | null) => {\n input.value = toHex(v ?? look.start?.());\n hex.textContent = v ?? t('settings.auto');\n row.classList.toggle('tcw-color-auto', v === null);\n if (v !== null && look.start && !back) {\n back = document.createElement('button');\n back.type = 'button';\n back.className = 'tcw-color-auto-btn';\n back.textContent = t('settings.auto');\n back.setAttribute('aria-label', `${label}: ${t('settings.auto')}`);\n back.addEventListener('click', () => {\n onChange(null);\n // Focus stays in the row as the button goes; the chart may still draw a colour the host set.\n input.focus();\n show(look.read());\n });\n wrap.appendChild(back);\n } else if (v === null && back) {\n back.remove();\n back = null;\n }\n };\n input.addEventListener('input', () => {\n onChange(input.value);\n show(input.value);\n });\n wrap.append(input, hex);\n row.appendChild(wrap);\n show(value);\n return { row, sync: () => show(look.read()) };\n}\n\nexport function toggleRow(label: string, value: boolean, onChange: (v: boolean) => void): HTMLDivElement {\n const row = labelledRow(label);\n const toggle = document.createElement('button');\n toggle.type = 'button';\n toggle.className = `tcw-toggle${value ? ' tcw-on' : ''}`;\n toggle.setAttribute('role', 'switch');\n toggle.setAttribute('aria-checked', String(value));\n toggle.setAttribute('aria-label', label);\n toggle.addEventListener('click', () => {\n const next = !toggle.classList.contains('tcw-on');\n toggle.classList.toggle('tcw-on', next);\n toggle.setAttribute('aria-checked', String(next));\n onChange(next);\n });\n row.appendChild(toggle);\n return row;\n}\n\nexport function selectRow(\n label: string,\n value: string,\n options: readonly { label: string; value: string }[],\n onChange: (v: string) => void,\n): HTMLDivElement {\n const row = labelledRow(label);\n const select = document.createElement('select');\n select.className = 'tcw-settings-select';\n select.setAttribute('aria-label', label);\n for (const opt of options) {\n const o = document.createElement('option');\n o.value = opt.value;\n o.textContent = opt.label;\n if (opt.value === value) o.selected = true;\n select.appendChild(o);\n }\n // A value none of the options has (two keys that differ): nothing chosen, so any pick is a change.\n if (!options.some((opt) => opt.value === value)) select.selectedIndex = -1;\n select.addEventListener('change', () => onChange(select.value));\n row.appendChild(select);\n return row;\n}\n\nexport function rangeRow(\n label: string,\n value: number,\n range: { min: number; max: number; step: number },\n onChange: (v: number) => void,\n format: (v: number) => string = (v) => String(v),\n): HTMLDivElement {\n const row = labelledRow(label);\n const wrap = document.createElement('div');\n wrap.className = 'tcw-settings-range';\n const input = document.createElement('input');\n input.type = 'range';\n input.min = String(range.min);\n input.max = String(range.max);\n input.step = String(range.step);\n input.value = String(value);\n input.setAttribute('aria-label', label);\n const out = document.createElement('span');\n out.className = 'tcw-settings-range-val';\n out.textContent = format(value);\n input.addEventListener('input', () => {\n const v = Number(input.value);\n out.textContent = format(v);\n onChange(v);\n });\n wrap.append(input, out);\n row.appendChild(wrap);\n return row;\n}\n\n/** What a number field takes. */\nexport interface NumberLimits {\n /** The field's step: '1' takes whole counts only, 'any' any number. */\n step: string;\n /** The largest number taken (Infinity: no limit). */\n max: number;\n /** The smallest number taken; without one, any number above 0. */\n min?: number;\n}\n\n/** What a number field takes, in words: the hint under it once it turned a number away. */\nfunction limitsText(limits: NumberLimits, t: Translator, locale: string): string {\n const format = new Intl.NumberFormat(locale, { maximumFractionDigits: HINT_MAX_DECIMALS }).format;\n if (limits.step === '1' && Number.isFinite(limits.max)) {\n return fill(t('settings.numberWhole'), { min: format(Math.max(1, limits.min ?? 1)), max: format(limits.max) });\n }\n return fill(t('settings.numberAtLeast'), { min: format(limits.min ?? 0) });\n}\n\n/**\n * A number field; empty means \"worked out from the data\" (shown as Auto). Only numbers above 0, and from\n * `limits.min` on, are taken; one turned away is marked invalid, and a hint under it, in `locale`, says what is.\n */\nexport function numberRow(\n label: string,\n value: number | null,\n limits: NumberLimits,\n onChange: (v: number | null) => void,\n t: Translator,\n locale = 'en-US',\n): HTMLDivElement {\n const row = document.createElement('div');\n row.className = 'tcw-settings-row tcw-settings-row--number';\n const lbl = document.createElement('label');\n lbl.className = 'tcw-settings-label';\n lbl.textContent = label;\n row.appendChild(lbl);\n const input = document.createElement('input');\n input.type = 'number';\n input.className = 'tcw-settings-number';\n // The min attribute is also where the arrows step from: at 1e-8, the up arrow turns 5 into 5.00000001 and an\n // empty field into 1e-8. It stays 0, and the smallest number taken is checked on change.\n input.min = '0';\n input.step = limits.step;\n if (Number.isFinite(limits.max)) input.max = String(limits.max);\n input.placeholder = t('settings.auto');\n input.value = value === null ? '' : String(value);\n input.setAttribute('aria-label', label);\n // Written when a number is turned away, and heard with the field: red alone says nothing to a screen reader.\n const hint = document.createElement('div');\n hint.className = 'tcw-settings-hint';\n hint.id = `tcw-number-hint-${++numberRowCount}`;\n hint.setAttribute('role', 'status');\n input.setAttribute('aria-describedby', hint.id);\n const mark = (ok: boolean) => {\n if (ok) input.removeAttribute('aria-invalid');\n else input.setAttribute('aria-invalid', 'true');\n hint.textContent = ok ? '' : limitsText(limits, t, locale);\n };\n input.addEventListener('change', () => {\n if (input.value.trim() === '') {\n mark(true);\n onChange(null);\n return;\n }\n const v = Number(input.value);\n const ok = Number.isFinite(v) && v > 0 && v >= (limits.min ?? 0) && v <= limits.max && (limits.step !== '1' || Number.isInteger(v));\n mark(ok);\n if (ok) onChange(v);\n });\n row.append(input, hint);\n return row;\n}\n","import { CHART_STYLE_KEYS, type ChartType, type ChartTypeOptions, type LineStyle } from '@tradecanvas/commons';\nimport type { ChartSettingsState, SettingsCallbacks, SettingsStylePatch } from './types.js';\nimport { CHART_TYPES } from './widgetConfig.js';\nimport {\n APPEARANCE_SECTIONS, HIGH_LOW_SECTION, LAST_PRICE_SECTION, LEGEND_ROWS, SCALE_ROWS, STATUS_LINE_ROWS, TRADING_SECTIONS, VOLUME_SECTION,\n seriesRows, type LookRow, type LookSection,\n} from './widgetSettingsLook.js';\nimport { chartTypeLabel, numberLocaleOptions } from './widgetLocales.js';\nimport { createChartTypeIcon, createIcon } from './icons.js';\nimport { timezoneOptions } from './widgetTimezones.js';\nimport { FOCUSABLE, keepTabInside } from './focusTrap.js';\nimport { WidgetContextMenu, type ContextMenuEntry } from './WidgetContextMenu.js';\nimport { colorRow, numberRow, rangeRow, selectRow, settingsSection, toggleRow, type NumberLimits } from './settingsRows.js';\nimport { fill, type MessageKey, type Translator } from './i18n.js';\n\nexport type SettingsTab = 'symbol' | 'statusLine' | 'scales' | 'appearance' | 'layers' | 'trading';\ntype Tab = SettingsTab;\n\n/** The tabs down the side, in the order a trader looks: the symbol first, the trading colours last. */\nconst TABS: readonly { id: Tab; icon: string }[] = [\n { id: 'symbol', icon: 'chartType:candlestick' },\n { id: 'statusLine', icon: 'type' },\n { id: 'scales', icon: 'ruler' },\n { id: 'appearance', icon: 'palette' },\n { id: 'layers', icon: 'layers' },\n { id: 'trading', icon: 'trendingUp' },\n];\n/** The widths a line takes in the settings, in pixels. */\nconst LINE_WIDTHS = [1, 2, 3, 4];\n/** A template's entry in the Template menu, by its name. */\nconst TEMPLATE_ID = 't:';\n/**\n * The smallest box, reversal or range the chart type's settings take: a\n * hundred-millionth, the finest price step markets commonly quote. A size\n * far under the prices is no use; the chart raises one that would make too\n * many bars anyway, but a slip like 1e-9 is turned away here.\n */\nconst MIN_TYPE_SIZE = 1e-8;\n/** A box, reversal or range: any number from MIN_TYPE_SIZE up. */\nconst SIZE_LIMITS: NumberLimits = { step: 'any', max: Infinity, min: MIN_TYPE_SIZE };\n\nlet nextSettingsId = 1;\n\n/**\n * The chart's settings: tabs down the side (Symbol, Status line, Scales,\n * Appearance, Layers, Trading), each change shown at once. Cancel (Escape,\n * the close button, a click outside) puts it all back, OK keeps it; the\n * Template menu applies, saves and deletes templates, and resets.\n */\nexport class WidgetSettings {\n private callbacks: SettingsCallbacks;\n private t: Translator;\n private backdrop: HTMLDivElement | null = null;\n private modal: HTMLDivElement | null = null;\n private bodyEl: HTMLDivElement | null = null;\n private tabButtons: HTMLButtonElement[] = [];\n private templateBtn: HTMLButtonElement | null = null;\n private menu: WidgetContextMenu | null = null;\n private currentTab: Tab = 'symbol';\n private currentSettings: ChartSettingsState | null = null;\n /** The chart's type, whose settings the Symbol tab offers. */\n private chartType: ChartType | null = null;\n /** The look's rows on view, each showing the chart's value again (a change to one moves others: a wick with its body). */\n private lookRows: { el: HTMLElement; sync: () => void }[] = [];\n private returnFocus: HTMLElement | null = null;\n private readonly uid = nextSettingsId++;\n\n /**\n * `barCountdown` / `logScale` / `legend` / `volume` false leave out the\n * controls for features the chart has switched off: there they would do\n * nothing.\n */\n constructor(\n callbacks: SettingsCallbacks,\n t: Translator,\n private readonly available: {\n barCountdown?: boolean;\n logScale?: boolean;\n legend?: boolean;\n volume?: boolean;\n /** The exchange's time zone to offer (null while unknown), or undefined when there is none to offer. */\n exchangeZone?: () => string | null | undefined;\n /** false: the chart doesn't trade, so the orders' colours aren't offered. */\n trading?: boolean;\n } = {},\n /** Where the panel mounts (the widget's portal: themed, and inside it when fullscreen). */\n private readonly host: () => HTMLElement = () => document.body,\n ) {\n this.callbacks = callbacks;\n this.t = t;\n }\n\n /** Open on `tab` (the Symbol tab unless asked: the price axis's menu asks for Scales). */\n open(currentSettings: ChartSettingsState, chartType: ChartType | null = null, tab: SettingsTab = 'symbol'): void {\n if (this.modal) this.remove();\n this.returnFocus = document.activeElement instanceof HTMLElement ? document.activeElement : null;\n this.currentSettings = { ...currentSettings };\n this.chartType = chartType;\n this.currentTab = tab;\n this.buildModal();\n this.tabButtons.find((b) => b.dataset.tab === tab)?.focus();\n }\n\n isOpen(): boolean {\n return this.modal !== null;\n }\n\n /** Show `settings` when they differ from what the dialog shows: a change made outside it while it is open. */\n follow(settings: ChartSettingsState): void {\n if (!this.modal || (this.currentSettings && sameSettings(this.currentSettings, settings))) return;\n this.refresh(settings);\n }\n\n /** The settings as they are now (after an undo, a template), on the tab that's open; `chartType` when it changed. */\n refresh(settings: ChartSettingsState, chartType?: ChartType | null): void {\n if (!this.modal) return;\n this.currentSettings = { ...settings };\n if (chartType !== undefined) this.chartType = chartType;\n // A change from outside (a compare's bars, the host's setSettings) builds the panel again under the\n // control in focus: focus goes to the one in its place, not to the page outside the dialog.\n const focused = this.focusedControl();\n this.renderTabContent();\n if (focused !== null) this.focusControl(focused);\n }\n\n /** The place of the control in focus among the panel's, or null when focus is not in the panel. */\n private focusedControl(): number | null {\n const active = document.activeElement;\n if (!this.bodyEl || !(active instanceof HTMLElement) || !this.bodyEl.contains(active)) return null;\n const at = this.panelControls().indexOf(active);\n return at < 0 ? null : at;\n }\n\n /** Focus the panel's control at `index`, or its last when it has fewer; the tab when it has none. */\n private focusControl(index: number): void {\n const controls = this.panelControls();\n const control = controls[Math.min(index, controls.length - 1)] ?? this.tabButtons.find((b) => b.dataset.tab === this.currentTab);\n control?.focus();\n }\n\n private panelControls(): HTMLElement[] {\n return this.bodyEl ? [...this.bodyEl.querySelectorAll<HTMLElement>(FOCUSABLE)] : [];\n }\n\n /** Close, keeping what was changed (as OK does). */\n close(): void {\n this.finish(true);\n }\n\n destroy(): void {\n this.remove();\n }\n\n /** OK (`keep`) or Cancel, then closed, focus back where the dialog was opened from. */\n private finish(keep: boolean): void {\n if (!this.modal) return;\n if (keep) this.callbacks.onClose();\n else this.callbacks.onCancel?.();\n this.remove();\n if (this.returnFocus?.isConnected) this.returnFocus.focus();\n this.returnFocus = null;\n }\n\n private remove(): void {\n this.menu?.destroy();\n this.menu = null;\n this.backdrop?.remove();\n this.backdrop = this.modal = this.bodyEl = this.templateBtn = null;\n this.tabButtons = [];\n }\n\n private buildModal(): void {\n const backdrop = document.createElement('div');\n backdrop.className = 'tcw-modal-backdrop';\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 backdrop.addEventListener('pointerdown', (e) => {\n pressedBackdrop = e.target === backdrop;\n });\n backdrop.addEventListener('click', (e) => {\n if (e.target === backdrop && pressedBackdrop) this.finish(false);\n pressedBackdrop = false;\n });\n const modal = document.createElement('div');\n modal.className = 'tcw-modal tcw-set';\n modal.setAttribute('role', 'dialog');\n modal.setAttribute('aria-modal', 'true');\n modal.setAttribute('aria-labelledby', `tcw-set-title-${this.uid}`);\n modal.tabIndex = -1;\n modal.addEventListener('keydown', (e) => {\n if (e.key === 'Escape') {\n e.preventDefault();\n e.stopPropagation();\n this.finish(false);\n return;\n }\n keepTabInside(e, modal);\n });\n this.backdrop = backdrop;\n this.modal = modal;\n modal.append(this.buildHeader(), this.buildBody(), this.buildFooter());\n backdrop.appendChild(modal);\n this.host().appendChild(backdrop);\n this.markTabs();\n this.renderTabContent();\n }\n\n private buildHeader(): HTMLDivElement {\n const header = document.createElement('div');\n header.className = 'tcw-modal-header';\n const title = document.createElement('h3');\n title.id = `tcw-set-title-${this.uid}`;\n title.textContent = this.t('settings.title');\n const closeBtn = document.createElement('button');\n closeBtn.type = 'button';\n closeBtn.className = 'tcw-modal-close';\n closeBtn.setAttribute('aria-label', this.t('common.close'));\n closeBtn.innerHTML = createIcon('x', 16);\n closeBtn.addEventListener('click', () => this.finish(false));\n header.append(title, closeBtn);\n return header;\n }\n\n /** The tabs down the side, and the open one's settings beside them. */\n private buildBody(): HTMLDivElement {\n const body = document.createElement('div');\n body.className = 'tcw-set-body';\n const nav = document.createElement('div');\n nav.className = 'tcw-set-nav';\n nav.setAttribute('role', 'tablist');\n nav.setAttribute('aria-orientation', 'vertical');\n nav.setAttribute('aria-label', this.t('settings.title'));\n nav.addEventListener('keydown', (e) => this.onTabKey(e));\n this.tabButtons = TABS.map(({ id, icon }) => {\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.className = 'tcw-set-tab';\n btn.id = `tcw-set-tab-${this.uid}-${id}`;\n btn.dataset.tab = id;\n btn.setAttribute('role', 'tab');\n btn.setAttribute('aria-controls', `tcw-set-panel-${this.uid}`);\n const svg = icon.startsWith('chartType:') ? createChartTypeIcon(icon.slice('chartType:'.length) as ChartType, 16) : createIcon(icon, 16);\n btn.innerHTML = `${svg}<span></span>`;\n btn.querySelector('span')!.textContent = this.t(`settings.tab.${id}` as MessageKey);\n btn.addEventListener('click', () => this.selectTab(id));\n return btn;\n });\n nav.append(...this.tabButtons);\n const panel = document.createElement('div');\n panel.className = 'tcw-modal-body tcw-set-panel';\n panel.id = `tcw-set-panel-${this.uid}`;\n panel.setAttribute('role', 'tabpanel');\n this.bodyEl = panel;\n body.append(nav, panel);\n return body;\n }\n\n /** Template on the left; Cancel and OK on the right. */\n private buildFooter(): HTMLDivElement {\n const footer = document.createElement('div');\n footer.className = 'tcw-modal-footer';\n const template = this.footerButton('template', 'tcw-footer-menu', () => this.openTemplates());\n template.setAttribute('aria-haspopup', 'menu');\n template.setAttribute('aria-expanded', 'false');\n template.innerHTML = `<span></span>${createIcon('chevronDown', 12)}`;\n template.querySelector('span')!.textContent = this.t('settings.template');\n this.templateBtn = template;\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 footer.append(template, end);\n return footer;\n }\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 markTabs(): void {\n for (const btn of this.tabButtons) {\n const on = btn.dataset.tab === this.currentTab;\n btn.classList.toggle('tcw-active', on);\n btn.setAttribute('aria-selected', String(on));\n btn.tabIndex = on ? 0 : -1;\n if (on) this.bodyEl?.setAttribute('aria-labelledby', btn.id);\n }\n }\n\n private selectTab(tab: Tab, focus = false): void {\n this.currentTab = tab;\n this.markTabs();\n this.renderTabContent();\n if (focus) this.tabButtons.find((b) => b.dataset.tab === tab)?.focus();\n }\n\n /** Through the tabs (Home, End), picking as they go: up and down, or along the row of chips on a phone. */\n private onTabKey(e: KeyboardEvent): void {\n const at = TABS.findIndex((tab) => tab.id === this.currentTab);\n const next = e.key === 'ArrowDown' || e.key === 'ArrowRight' ? (at + 1) % TABS.length\n : e.key === 'ArrowUp' || e.key === 'ArrowLeft' ? (at - 1 + TABS.length) % TABS.length\n : e.key === 'Home' ? 0\n : e.key === 'End' ? TABS.length - 1\n : -1;\n if (next < 0) return;\n e.preventDefault();\n this.selectTab(TABS[next].id, true);\n }\n\n /** The Template menu: the saved templates (each removable), Save as template, Reset to defaults. */\n private openTemplates(): void {\n const btn = this.templateBtn;\n if (!this.modal || !btn) return;\n if (this.menu?.isOpen()) {\n this.menu.close();\n return;\n }\n const templates = this.callbacks.templates;\n const saved: ContextMenuEntry[] = (templates?.list() ?? []).map((name) => ({\n id: `${TEMPLATE_ID}${name}`, label: name, icon: 'layers', remove: fill(this.t('templates.delete'), { name }),\n }));\n const entries: ContextMenuEntry[] = [\n ...saved,\n ...(saved.length > 0 ? ['separator' as const] : []),\n ...(templates ? [{ id: 'save', label: this.t('settings.saveTemplate'), icon: 'save' }] : []),\n { id: 'reset', label: this.t('settings.resetToDefaults'), icon: 'reset' },\n ];\n this.menu ??= new WidgetContextMenu(this.modal, this.t('settings.template'));\n const at = btn.getBoundingClientRect();\n const box = this.modal.getBoundingClientRect();\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, (id) => {\n if (id === 'reset') this.callbacks.onReset();\n else if (id === 'save') templates?.save();\n else if (id.startsWith(TEMPLATE_ID)) templates?.apply(id.slice(TEMPLATE_ID.length));\n }, btn, (id) => templates?.remove(id.slice(TEMPLATE_ID.length)));\n }\n\n private renderTabContent(): void {\n if (!this.bodyEl || !this.currentSettings) return;\n this.bodyEl.replaceChildren();\n this.lookRows = [];\n switch (this.currentTab) {\n case 'symbol': this.renderSymbolTab(this.currentSettings); break;\n case 'statusLine': this.renderStatusLineTab(this.currentSettings); break;\n case 'scales': this.renderScalesTab(this.currentSettings); break;\n case 'appearance': this.renderAppearanceTab(this.currentSettings); break;\n case 'layers': this.renderLayersTab(this.currentSettings); break;\n case 'trading':\n for (const section of TRADING_SECTIONS) {\n if (!section.trading || this.available.trading !== false) this.renderLookSection(section);\n }\n break;\n }\n }\n\n /** The chart type's own settings and colours, the main series, the last price, the volume, the high and low. */\n private renderSymbolTab(s: ChartSettingsState): void {\n this.renderChartTypeSection();\n this.addSection(undefined, [\n toggleRow(this.t('settings.mainSeries'), s.mainSeriesVisible, (v) => this.patch({ mainSeriesVisible: v })),\n toggleRow(this.t('settings.extendedHours'), s.extendedHours, (v) => this.patch({ extendedHours: v })),\n ]);\n // A section's own switch says \"Show\": its title names what it shows.\n const show = this.t('common.show');\n this.renderLookSection(LAST_PRICE_SECTION);\n if (this.available.volume !== false) {\n this.renderLookSection(VOLUME_SECTION, [toggleRow(show, s.volumeVisible, (v) => this.patch({ volumeVisible: v }))]);\n }\n this.renderLookSection(HIGH_LOW_SECTION, [toggleRow(show, s.highLowLines, (v) => this.patch({ highLowLines: v }))]);\n }\n\n /** The legend over the chart: shown or not, its colours; the panes' titles. */\n private renderStatusLineTab(s: ChartSettingsState): void {\n const legend = this.available.legend !== false;\n const rows = legend ? STATUS_LINE_ROWS : STATUS_LINE_ROWS.filter((row) => !LEGEND_ROWS.includes(row));\n this.addSection(undefined, [\n ...(legend ? [toggleRow(this.t('settings.ohlcLegend'), s.legendVisible, (v) => this.patch({ legendVisible: v }))] : []),\n ...rows.map((row) => this.lookRow(row)),\n ]);\n }\n\n /** The price scale's mode and behaviour, its labels, the time zone, how numbers are written, the axes' colours. */\n private renderScalesTab(s: ChartSettingsState): void {\n const scales = [\n { value: 'regular', label: this.t('settings.scale.regular') },\n { value: 'logarithmic', label: this.t('settings.scale.logarithmic') },\n { value: 'percentage', label: this.t('settings.scale.percentage') },\n { value: 'indexedTo100', label: this.t('settings.scale.indexedTo100') },\n ].filter((o) => o.value !== 'logarithmic' || this.available.logScale !== false);\n const exchangeZone = this.available.exchangeZone?.();\n const zones = timezoneOptions(s.timezone, Date.now(), this.t('settings.timezone.local'),\n exchangeZone !== undefined ? { label: this.t('settings.timezone.exchange'), zone: exchangeZone } : undefined);\n this.addSection(undefined, [\n selectRow(this.t('settings.priceScale'), s.scaleMode, scales, (v) => this.patch({ scaleMode: v as ChartSettingsState['scaleMode'] })),\n toggleRow(this.t('settings.autoScale'), s.autoScale, (v) => this.patch({ autoScale: v })),\n toggleRow(this.t('settings.invertScale'), s.invertScale, (v) => this.patch({ invertScale: v })),\n toggleRow(this.t('settings.leftScale'), s.leftPriceScale, (v) => this.patch({ leftPriceScale: v })),\n toggleRow(this.t('settings.indicatorValues'), s.indicatorValueLabels, (v) => this.patch({ indicatorValueLabels: v })),\n toggleRow(this.t('settings.alignAxisLabels'), s.alignAxisLabels, (v) => this.patch({ alignAxisLabels: v })),\n ...(this.available.barCountdown !== false\n ? [toggleRow(this.t('settings.barCountdown'), s.barCountdown, (v) => this.patch({ barCountdown: v }))]\n : []),\n selectRow(this.t('settings.timezone'), s.timezone, zones, (v) => this.patch({ timezone: v })),\n selectRow(this.t('settings.numberLocale'), s.numberLocale, numberLocaleOptions(s.numberLocale), (v) => this.patch({ numberLocale: v })),\n ]);\n this.addSection(this.t('settings.section.axes'), SCALE_ROWS.map((row) => this.lookRow(row)));\n }\n\n /** The background, the grid, the crosshair, the session breaks, the drawings' handles. */\n private renderAppearanceTab(s: ChartSettingsState): void {\n const [background, grid, crosshair, ...rest] = APPEARANCE_SECTIONS;\n this.renderLookSection(background);\n this.renderLookSection(grid, [toggleRow(this.t('common.show'), s.gridVisible, (v) => this.patch({ gridVisible: v }))]);\n this.renderLookSection(crosshair, [selectRow(this.t('settings.crosshairMode'), s.crosshairMode, [\n { label: this.t('settings.crosshair.magnet'), value: 'magnet' },\n { label: this.t('settings.crosshair.normal'), value: 'normal' },\n { label: this.t('settings.crosshair.hidden'), value: 'hidden' },\n ], (v) => this.patch({ crosshairMode: v as ChartSettingsState['crosshairMode'] })),\n selectRow(this.t('settings.crosshairCursor'), s.crosshairCursor, [\n { label: this.t('drawing.cursorCross'), value: 'cross' },\n { label: this.t('drawing.cursorDot'), value: 'dot' },\n { label: this.t('drawing.cursorArrow'), value: 'arrow' },\n ], (v) => this.patch({ crosshairCursor: v as ChartSettingsState['crosshairCursor'] }))]);\n for (const section of rest) this.renderLookSection(section);\n }\n\n /** What the chart can draw over the bars: the profiles, the depth, the swings, the sessions, the prior levels. */\n private renderLayersTab(s: ChartSettingsState): void {\n const percent = (v: number) => `${Math.round(v * 100)}%`;\n const t = (key: MessageKey) => this.t(key);\n this.addSection(undefined, [toggleRow(t('settings.volumeProfile'), s.volumeProfileVisible, (v) => this.patch({ volumeProfileVisible: v }))]);\n this.addSection(undefined, [\n toggleRow(t('settings.marketProfile'), s.marketProfileVisible, (v) => this.patch({ marketProfileVisible: v })),\n toggleRow(t('settings.marketProfileSplit'), s.marketProfileSplit, (v) => this.patch({ marketProfileSplit: v })),\n toggleRow(t('settings.marketProfileLetters'), s.marketProfileLetters, (v) => this.patch({ marketProfileLetters: v })),\n rangeRow(t('settings.marketProfileBuckets'), s.marketProfileBuckets, { min: 8, max: 200, step: 1 }, (v) => this.patch({ marketProfileBuckets: v })),\n rangeRow(t('settings.marketProfileOpacity'), s.marketProfileOpacity, { min: 0.05, max: 1, step: 0.05 }, (v) => this.patch({ marketProfileOpacity: v }), percent),\n ]);\n this.addSection(undefined, [\n toggleRow(t('settings.depthHeatmap'), s.depthHeatmapVisible, (v) => this.patch({ depthHeatmapVisible: v })),\n rangeRow(t('settings.depthHeatmapOpacity'), s.depthHeatmapOpacity, { min: 0.1, max: 1, step: 0.05 }, (v) => this.patch({ depthHeatmapOpacity: v }), percent),\n ]);\n this.addSection(undefined, [\n toggleRow(t('settings.swingMarkers'), s.pivotMarkersVisible, (v) => this.patch({ pivotMarkersVisible: v })),\n rangeRow(t('settings.swingStrength'), s.pivotStrength, { min: 2, max: 20, step: 1 }, (v) => this.patch({ pivotStrength: v })),\n toggleRow(t('settings.swingStructure'), s.pivotStructureLabels, (v) => this.patch({ pivotStructureLabels: v })),\n ]);\n this.addSection(undefined, [\n toggleRow(t('settings.sessionShading'), s.sessionShadingVisible, (v) => this.patch({ sessionShadingVisible: v })),\n toggleRow(t('settings.periodLevels'), s.periodLevelsVisible, (v) => this.patch({ periodLevelsVisible: v })),\n selectRow(t('settings.periodLevelsBasis'), s.periodLevelsPeriod, [\n { value: 'day', label: t('settings.priorDay') },\n { value: 'week', label: t('settings.priorWeek') },\n ], (v) => this.patch({ periodLevelsPeriod: v as ChartSettingsState['periodLevelsPeriod'] })),\n ]);\n }\n\n private addSection(title: string | undefined, rows: readonly HTMLElement[]): HTMLDivElement {\n const section = settingsSection(title);\n section.append(...rows);\n this.bodyEl?.appendChild(section);\n return section;\n }\n\n /** A section of the look, after `lead` (the switch it goes with, say). */\n private renderLookSection(section: LookSection, lead: readonly HTMLElement[] = []): void {\n this.addSection(this.t(section.title), [...lead, ...section.rows.map((row) => this.lookRow(row))]);\n }\n\n /** A row of the look: a colour, a switch, a dash or a width, as its key takes it, on the user's layer. */\n private lookRow(row: LookRow): HTMLDivElement {\n const label = this.t(row.label);\n const read = () => this.callbacks.styleValue(row.keys[0]);\n /** The keys' value, or '' when they differ (the host set each way apart): any pick then sets them all. */\n const shared = (): string => {\n const values = row.keys.map((key) => this.callbacks.styleValue(key));\n return values.every((v) => v === values[0]) && values[0] !== null ? String(values[0]) : '';\n };\n let el: HTMLDivElement;\n const set = (v: string | number | boolean | null) => {\n this.callbacks.onStyleChange(Object.fromEntries(row.keys.map((key) => [key, v])) as SettingsStylePatch);\n for (const other of this.lookRows) if (other.el !== el) other.sync();\n };\n const auto = row.auto ? [{ value: '', label: this.t('settings.auto') }] : [];\n let sync: () => void;\n switch (CHART_STYLE_KEYS[row.keys[0]]) {\n case 'boolean': {\n el = toggleRow(label, read() === true, set);\n const toggle = el.querySelector('button')!;\n sync = () => {\n const on = read() === true;\n toggle.classList.toggle('tcw-on', on);\n toggle.setAttribute('aria-checked', String(on));\n };\n break;\n }\n case 'lineStyle':\n el = selectRow(label, shared(), [...auto, ...this.lineStyles()], (v) => set(v === '' ? null : asLineStyle(v)));\n sync = () => { el.querySelector('select')!.value = shared(); };\n break;\n case 'width': {\n const value = read();\n const widths = typeof value === 'number' && !LINE_WIDTHS.includes(value) ? [...LINE_WIDTHS, value].sort((a, b) => a - b) : LINE_WIDTHS;\n el = selectRow(label, shared(), [...auto, ...widths.map((w) => ({ value: String(w), label: `${w}px` }))],\n (v) => set(v === '' ? null : Number(v)));\n sync = () => { el.querySelector('select')!.value = shared(); };\n break;\n }\n default: {\n const value = read();\n const colour = colorRow(label, typeof value === 'string' ? value : null, set, {\n read: () => { const v = read(); return typeof v === 'string' ? v : null; },\n start: row.auto ? () => (row.auto?.from ? this.callbacks.styleValue(row.auto.from) : row.auto?.hint) ?? null : undefined,\n }, this.t);\n el = colour.row;\n sync = colour.sync;\n }\n }\n this.lookRows.push({ el, sync });\n return el;\n }\n\n private lineStyles(): { value: string; label: string }[] {\n return [\n { value: 'solid', label: this.t('drawingSettings.lineStyle.solid') },\n { value: 'dashed', label: this.t('drawingSettings.lineStyle.dashed') },\n { value: 'dotted', label: this.t('drawingSettings.lineStyle.dotted') },\n ];\n }\n\n /** The chart's type: its settings (Renko's box, Kagi's reversal…) and its colours and widths. */\n private renderChartTypeSection(): void {\n if (!this.bodyEl || !this.currentSettings || !this.chartType) return;\n const type = this.chartType;\n const all = this.currentSettings.chartTypeOptions ?? {};\n const def = CHART_TYPES.find((c) => c.value === type);\n const section = settingsSection(def ? chartTypeLabel(def, this.t) : type);\n const own = () => this.currentSettings?.chartTypeOptions ?? {};\n /** Change one type's settings: the rest of its own stay. */\n const set = <K extends keyof ChartTypeOptions>(key: K, value: NonNullable<ChartTypeOptions[K]>) =>\n this.patch({ chartTypeOptions: { ...own(), [key]: value } });\n const number = (label: MessageKey, value: number | null, limits: NumberLimits, onChange: (v: number | null) => void) =>\n section.appendChild(numberRow(this.t(label), value, limits, onChange, this.t, this.currentSettings?.numberLocale || undefined));\n switch (type) {\n case 'renko': {\n const o = all.renko ?? {};\n number('settings.boxSize', typeof o.boxSize === 'number' ? o.boxSize : null, SIZE_LIMITS,\n (v) => set('renko', { ...(own().renko ?? {}), boxSize: v ?? 'atr' }));\n number('settings.atrPeriod', o.atrPeriod ?? null, { step: '1', max: 500 }, (v) => set('renko', withValue(own().renko, 'atrPeriod', v)));\n break;\n }\n case 'lineBreak':\n number('settings.lineBreakLines', all.lineBreak?.lines ?? null, { step: '1', max: 10 }, (v) => set('lineBreak', withValue(own().lineBreak, 'lines', v)));\n break;\n case 'kagi':\n number('settings.reversal', all.kagi?.reversal ?? null, SIZE_LIMITS, (v) => set('kagi', withValue(own().kagi, 'reversal', v)));\n section.appendChild(selectRow(this.t('settings.reversalType'), all.kagi?.reversalType ?? 'percent', [\n { value: 'percent', label: this.t('settings.reversalType.percent') },\n { value: 'price', label: this.t('settings.reversalType.price') },\n ], (v) => set('kagi', { ...(own().kagi ?? {}), reversalType: v === 'price' ? 'price' : 'percent' })));\n break;\n case 'pointAndFigure':\n number('settings.boxSize', typeof all.pointAndFigure?.boxSize === 'number' ? all.pointAndFigure.boxSize : null, SIZE_LIMITS,\n (v) => set('pointAndFigure', { ...(own().pointAndFigure ?? {}), boxSize: v ?? 'auto' }));\n number('settings.reversalBoxes', all.pointAndFigure?.reversal ?? null, { step: '1', max: 10 },\n (v) => set('pointAndFigure', withValue(own().pointAndFigure, 'reversal', v)));\n break;\n case 'rangeBars':\n number('settings.barRange', typeof all.rangeBars?.range === 'number' ? all.rangeBars.range : null, SIZE_LIMITS,\n (v) => set('rangeBars', { range: v ?? 'auto' }));\n break;\n default:\n break;\n }\n for (const row of seriesRows(type)) section.appendChild(this.lookRow(row));\n this.bodyEl.appendChild(section);\n }\n\n private patch(partial: Partial<ChartSettingsState>): void {\n if (this.currentSettings) Object.assign(this.currentSettings, partial);\n this.callbacks.onChange(partial);\n }\n}\n\n/** Whether two settings say the same, whatever order their keys were set in, at any depth. */\nfunction sameSettings(a: ChartSettingsState, b: ChartSettingsState): boolean {\n return sameValue(a, b);\n}\n\nfunction sameValue(a: unknown, b: unknown): boolean {\n if (a === b) return true;\n if (typeof a !== 'object' || typeof b !== 'object' || a === null || b === null) return false;\n if (Array.isArray(a) !== Array.isArray(b)) return false;\n const x = a as Record<string, unknown>;\n const y = b as Record<string, unknown>;\n // A key set to undefined is as one not there, as JSON has it.\n const keys = new Set([...Object.keys(x), ...Object.keys(y)].filter((k) => x[k] !== undefined || y[k] !== undefined));\n for (const key of keys) if (!sameValue(x[key], y[key])) return false;\n return true;\n}\n\n/** `settings` with `key` set to `value`, or without it (back to its default) for null. */\nfunction withValue<T extends object, K extends string>(settings: T | undefined, key: K, value: number | null): T {\n const next = { ...(settings ?? {}) } as Record<string, unknown>;\n if (value === null) delete next[key];\n else next[key] = value;\n return next as T;\n}\n\nfunction asLineStyle(v: string): LineStyle {\n return v === 'dashed' || v === 'dotted' ? v : 'solid';\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"WidgetSettings-9clgY3_1.js","names":[],"sources":["../src/widget/settingsRows.ts","../src/widget/WidgetSettings.ts"],"sourcesContent":["import { toHex } from './settingsControls.js';\nimport { fill, type Translator } from './i18n.js';\n\n/**\n * The rows of the chart settings: a section, a colour, a switch, a choice,\n * a slider and a number, each with its label and calling back on change.\n */\n\n/** Ids for the colour rows' descriptions. */\nlet colorRowCount = 0;\n/** Ids for the number rows' hints. */\nlet numberRowCount = 0;\n/** The most decimals a number in a hint shows: the smallest size taken, 0.00000001, has 8. */\nconst HINT_MAX_DECIMALS = 20;\n\n/** A group of rows; a title names it to a screen reader too (rows named alike under several titles). */\nexport function settingsSection(title?: string): HTMLDivElement {\n const div = document.createElement('div');\n div.className = 'tcw-settings-section';\n if (title) {\n const heading = document.createElement('div');\n heading.className = 'tcw-settings-section-title';\n heading.textContent = title;\n div.appendChild(heading);\n div.setAttribute('role', 'group');\n div.setAttribute('aria-label', title);\n }\n return div;\n}\n\nfunction labelledRow(label: string): HTMLDivElement {\n const row = document.createElement('div');\n row.className = 'tcw-settings-row';\n const lbl = document.createElement('span');\n lbl.className = 'tcw-settings-label';\n lbl.textContent = label;\n row.appendChild(lbl);\n return row;\n}\n\n/**\n * A colour of the look, shown again from `look.read()`. With `look.start`,\n * null is the part's own colour: shown as Auto, the picker starting from\n * `start()`, and once set a button takes it back.\n */\nexport function colorRow(\n label: string,\n value: string | null,\n onChange: (v: string | null) => void,\n look: { read: () => string | null; start?: () => string | number | boolean | null },\n t: Translator,\n): { row: HTMLDivElement; sync: () => void } {\n const row = labelledRow(label);\n const wrap = document.createElement('div');\n wrap.className = 'tcw-color-picker-wrap';\n const input = document.createElement('input');\n input.type = 'color';\n input.setAttribute('aria-label', label);\n // What the swatch can't say (Auto, a colour with transparency) is read with it.\n const hex = document.createElement('span');\n hex.className = 'tcw-color-hex';\n hex.id = `tcw-color-${++colorRowCount}`;\n input.setAttribute('aria-describedby', hex.id);\n\n let back: HTMLButtonElement | null = null;\n const show = (v: string | null) => {\n input.value = toHex(v ?? look.start?.());\n hex.textContent = v ?? t('settings.auto');\n row.classList.toggle('tcw-color-auto', v === null);\n if (v !== null && look.start && !back) {\n back = document.createElement('button');\n back.type = 'button';\n back.className = 'tcw-color-auto-btn';\n back.textContent = t('settings.auto');\n back.setAttribute('aria-label', `${label}: ${t('settings.auto')}`);\n back.addEventListener('click', () => {\n onChange(null);\n // Focus stays in the row as the button goes; the chart may still draw a colour the host set.\n input.focus();\n show(look.read());\n });\n wrap.appendChild(back);\n } else if (v === null && back) {\n back.remove();\n back = null;\n }\n };\n input.addEventListener('input', () => {\n onChange(input.value);\n show(input.value);\n });\n wrap.append(input, hex);\n row.appendChild(wrap);\n show(value);\n return { row, sync: () => show(look.read()) };\n}\n\nexport function toggleRow(label: string, value: boolean, onChange: (v: boolean) => void): HTMLDivElement {\n const row = labelledRow(label);\n const toggle = document.createElement('button');\n toggle.type = 'button';\n toggle.className = `tcw-toggle${value ? ' tcw-on' : ''}`;\n toggle.setAttribute('role', 'switch');\n toggle.setAttribute('aria-checked', String(value));\n toggle.setAttribute('aria-label', label);\n toggle.addEventListener('click', () => {\n const next = !toggle.classList.contains('tcw-on');\n toggle.classList.toggle('tcw-on', next);\n toggle.setAttribute('aria-checked', String(next));\n onChange(next);\n });\n row.appendChild(toggle);\n return row;\n}\n\nexport function selectRow(\n label: string,\n value: string,\n options: readonly { label: string; value: string }[],\n onChange: (v: string) => void,\n): HTMLDivElement {\n const row = labelledRow(label);\n const select = document.createElement('select');\n select.className = 'tcw-settings-select';\n select.setAttribute('aria-label', label);\n for (const opt of options) {\n const o = document.createElement('option');\n o.value = opt.value;\n o.textContent = opt.label;\n if (opt.value === value) o.selected = true;\n select.appendChild(o);\n }\n // A value none of the options has (two keys that differ): nothing chosen, so any pick is a change.\n if (!options.some((opt) => opt.value === value)) select.selectedIndex = -1;\n select.addEventListener('change', () => onChange(select.value));\n row.appendChild(select);\n return row;\n}\n\nexport function rangeRow(\n label: string,\n value: number,\n range: { min: number; max: number; step: number },\n onChange: (v: number) => void,\n format: (v: number) => string = (v) => String(v),\n): HTMLDivElement {\n const row = labelledRow(label);\n const wrap = document.createElement('div');\n wrap.className = 'tcw-settings-range';\n const input = document.createElement('input');\n input.type = 'range';\n input.min = String(range.min);\n input.max = String(range.max);\n input.step = String(range.step);\n input.value = String(value);\n input.setAttribute('aria-label', label);\n const out = document.createElement('span');\n out.className = 'tcw-settings-range-val';\n out.textContent = format(value);\n input.addEventListener('input', () => {\n const v = Number(input.value);\n out.textContent = format(v);\n onChange(v);\n });\n wrap.append(input, out);\n row.appendChild(wrap);\n return row;\n}\n\n/** What a number field takes. */\nexport interface NumberLimits {\n /** The field's step: '1' takes whole counts only, 'any' any number. */\n step: string;\n /** The largest number taken (Infinity: no limit). */\n max: number;\n /** The smallest number taken; without one, any number above 0. */\n min?: number;\n}\n\n/** What a number field takes, in words: the hint under it once it turned a number away. */\nfunction limitsText(limits: NumberLimits, t: Translator, locale: string): string {\n const format = new Intl.NumberFormat(locale, { maximumFractionDigits: HINT_MAX_DECIMALS }).format;\n if (limits.step === '1' && Number.isFinite(limits.max)) {\n return fill(t('settings.numberWhole'), { min: format(Math.max(1, limits.min ?? 1)), max: format(limits.max) });\n }\n return fill(t('settings.numberAtLeast'), { min: format(limits.min ?? 0) });\n}\n\n/**\n * A number field; empty means \"worked out from the data\" (shown as Auto). Only numbers above 0, and from\n * `limits.min` on, are taken; one turned away is marked invalid, and a hint under it, in `locale`, says what is.\n */\nexport function numberRow(\n label: string,\n value: number | null,\n limits: NumberLimits,\n onChange: (v: number | null) => void,\n t: Translator,\n locale = 'en-US',\n): HTMLDivElement {\n const row = document.createElement('div');\n row.className = 'tcw-settings-row tcw-settings-row--number';\n const lbl = document.createElement('label');\n lbl.className = 'tcw-settings-label';\n lbl.textContent = label;\n row.appendChild(lbl);\n const input = document.createElement('input');\n input.type = 'number';\n input.className = 'tcw-settings-number';\n // The min attribute is also where the arrows step from: at 1e-8, the up arrow turns 5 into 5.00000001 and an\n // empty field into 1e-8. It stays 0, and the smallest number taken is checked on change.\n input.min = '0';\n input.step = limits.step;\n if (Number.isFinite(limits.max)) input.max = String(limits.max);\n input.placeholder = t('settings.auto');\n input.value = value === null ? '' : String(value);\n input.setAttribute('aria-label', label);\n // Written when a number is turned away, and heard with the field: red alone says nothing to a screen reader.\n const hint = document.createElement('div');\n hint.className = 'tcw-settings-hint';\n hint.id = `tcw-number-hint-${++numberRowCount}`;\n hint.setAttribute('role', 'status');\n input.setAttribute('aria-describedby', hint.id);\n const mark = (ok: boolean) => {\n if (ok) input.removeAttribute('aria-invalid');\n else input.setAttribute('aria-invalid', 'true');\n hint.textContent = ok ? '' : limitsText(limits, t, locale);\n };\n input.addEventListener('change', () => {\n if (input.value.trim() === '') {\n mark(true);\n onChange(null);\n return;\n }\n const v = Number(input.value);\n const ok = Number.isFinite(v) && v > 0 && v >= (limits.min ?? 0) && v <= limits.max && (limits.step !== '1' || Number.isInteger(v));\n mark(ok);\n if (ok) onChange(v);\n });\n row.append(input, hint);\n return row;\n}\n","import { CHART_STYLE_KEYS, type ChartType, type ChartTypeOptions, type LineStyle } from '@tradecanvas/commons';\nimport type { ChartSettingsState, SettingsCallbacks, SettingsStylePatch } from './types.js';\nimport { CHART_TYPES } from './widgetConfig.js';\nimport {\n APPEARANCE_SECTIONS, HIGH_LOW_SECTION, LAST_PRICE_SECTION, LEGEND_ROWS, SCALE_ROWS, STATUS_LINE_ROWS, TRADING_SECTIONS, VOLUME_SECTION,\n seriesRows, type LookRow, type LookSection,\n} from './widgetSettingsLook.js';\nimport { chartTypeLabel, numberLocaleOptions } from './widgetLocales.js';\nimport { createChartTypeIcon, createIcon } from './icons.js';\nimport { timezoneOptions } from './widgetTimezones.js';\nimport { FOCUSABLE, keepTabInside } from './focusTrap.js';\nimport { WidgetContextMenu, type ContextMenuEntry } from './WidgetContextMenu.js';\nimport { colorRow, numberRow, rangeRow, selectRow, settingsSection, toggleRow, type NumberLimits } from './settingsRows.js';\nimport { fill, type MessageKey, type Translator } from './i18n.js';\n\nexport type SettingsTab = 'symbol' | 'statusLine' | 'scales' | 'appearance' | 'layers' | 'trading';\ntype Tab = SettingsTab;\n\n/** The tabs down the side, in the order a trader looks: the symbol first, the trading colours last. */\nconst TABS: readonly { id: Tab; icon: string }[] = [\n { id: 'symbol', icon: 'chartType:candlestick' },\n { id: 'statusLine', icon: 'type' },\n { id: 'scales', icon: 'ruler' },\n { id: 'appearance', icon: 'palette' },\n { id: 'layers', icon: 'layers' },\n { id: 'trading', icon: 'trendingUp' },\n];\n/** The widths a line takes in the settings, in pixels. */\nconst LINE_WIDTHS = [1, 2, 3, 4];\n/** A template's entry in the Template menu, by its name. */\nconst TEMPLATE_ID = 't:';\n/**\n * The smallest box, reversal or range the chart type's settings take: a\n * hundred-millionth, the finest price step markets commonly quote. A size\n * far under the prices is no use; the chart raises one that would make too\n * many bars anyway, but a slip like 1e-9 is turned away here.\n */\nconst MIN_TYPE_SIZE = 1e-8;\n/** A box, reversal or range: any number from MIN_TYPE_SIZE up. */\nconst SIZE_LIMITS: NumberLimits = { step: 'any', max: Infinity, min: MIN_TYPE_SIZE };\n\nlet nextSettingsId = 1;\n\n/**\n * The chart's settings: tabs down the side (Symbol, Status line, Scales,\n * Appearance, Layers, Trading), each change shown at once. Cancel (Escape,\n * the close button, a click outside) puts it all back, OK keeps it; the\n * Template menu applies, saves and deletes templates, and resets.\n */\nexport class WidgetSettings {\n private callbacks: SettingsCallbacks;\n private t: Translator;\n private backdrop: HTMLDivElement | null = null;\n private modal: HTMLDivElement | null = null;\n private bodyEl: HTMLDivElement | null = null;\n private tabButtons: HTMLButtonElement[] = [];\n private templateBtn: HTMLButtonElement | null = null;\n private menu: WidgetContextMenu | null = null;\n private currentTab: Tab = 'symbol';\n private currentSettings: ChartSettingsState | null = null;\n /** The chart's type, whose settings the Symbol tab offers. */\n private chartType: ChartType | null = null;\n /** The look's rows on view, each showing the chart's value again (a change to one moves others: a wick with its body). */\n private lookRows: { el: HTMLElement; sync: () => void }[] = [];\n private returnFocus: HTMLElement | null = null;\n private readonly uid = nextSettingsId++;\n\n /**\n * `barCountdown` / `logScale` / `legend` / `volume` false leave out the\n * controls for features the chart has switched off: there they would do\n * nothing.\n */\n constructor(\n callbacks: SettingsCallbacks,\n t: Translator,\n private readonly available: {\n barCountdown?: boolean;\n logScale?: boolean;\n legend?: boolean;\n volume?: boolean;\n /** The exchange's time zone to offer (null while unknown), or undefined when there is none to offer. */\n exchangeZone?: () => string | null | undefined;\n /** false: the chart doesn't trade, so the orders' colours aren't offered. */\n trading?: boolean;\n } = {},\n /** Where the panel mounts (the widget's portal: themed, and inside it when fullscreen). */\n private readonly host: () => HTMLElement = () => document.body,\n ) {\n this.callbacks = callbacks;\n this.t = t;\n }\n\n /** Open on `tab` (the Symbol tab unless asked: the price axis's menu asks for Scales). */\n open(currentSettings: ChartSettingsState, chartType: ChartType | null = null, tab: SettingsTab = 'symbol'): void {\n if (this.modal) this.remove();\n this.returnFocus = document.activeElement instanceof HTMLElement ? document.activeElement : null;\n this.currentSettings = { ...currentSettings };\n this.chartType = chartType;\n this.currentTab = tab;\n this.buildModal();\n this.tabButtons.find((b) => b.dataset.tab === tab)?.focus();\n }\n\n isOpen(): boolean {\n return this.modal !== null;\n }\n\n /** Show `settings` when they differ from what the dialog shows: a change made outside it while it is open. */\n follow(settings: ChartSettingsState): void {\n if (!this.modal || (this.currentSettings && sameSettings(this.currentSettings, settings))) return;\n this.refresh(settings);\n }\n\n /** The settings as they are now (after an undo, a template), on the tab that's open; `chartType` when it changed. */\n refresh(settings: ChartSettingsState, chartType?: ChartType | null): void {\n if (!this.modal) return;\n this.currentSettings = { ...settings };\n if (chartType !== undefined) this.chartType = chartType;\n // A change from outside (a compare's bars, the host's setSettings) builds the panel again under the\n // control in focus: focus goes to the one in its place, not to the page outside the dialog.\n const focused = this.focusedControl();\n this.renderTabContent();\n if (focused !== null) this.focusControl(focused);\n }\n\n /** The place of the control in focus among the panel's, or null when focus is not in the panel. */\n private focusedControl(): number | null {\n const active = document.activeElement;\n if (!this.bodyEl || !(active instanceof HTMLElement) || !this.bodyEl.contains(active)) return null;\n const at = this.panelControls().indexOf(active);\n return at < 0 ? null : at;\n }\n\n /** Focus the panel's control at `index`, or its last when it has fewer; the tab when it has none. */\n private focusControl(index: number): void {\n const controls = this.panelControls();\n const control = controls[Math.min(index, controls.length - 1)] ?? this.tabButtons.find((b) => b.dataset.tab === this.currentTab);\n control?.focus();\n }\n\n private panelControls(): HTMLElement[] {\n return this.bodyEl ? [...this.bodyEl.querySelectorAll<HTMLElement>(FOCUSABLE)] : [];\n }\n\n /** Close, keeping what was changed (as OK does). */\n close(): void {\n this.finish(true);\n }\n\n destroy(): void {\n this.remove();\n }\n\n /** OK (`keep`) or Cancel, then closed, focus back where the dialog was opened from. */\n private finish(keep: boolean): void {\n if (!this.modal) return;\n if (keep) this.callbacks.onClose();\n else this.callbacks.onCancel?.();\n this.remove();\n if (this.returnFocus?.isConnected) this.returnFocus.focus();\n this.returnFocus = null;\n }\n\n private remove(): void {\n this.menu?.destroy();\n this.menu = null;\n this.backdrop?.remove();\n this.backdrop = this.modal = this.bodyEl = this.templateBtn = null;\n this.tabButtons = [];\n }\n\n private buildModal(): void {\n const backdrop = document.createElement('div');\n backdrop.className = 'tcw-modal-backdrop';\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 backdrop.addEventListener('pointerdown', (e) => {\n pressedBackdrop = e.target === backdrop;\n });\n backdrop.addEventListener('click', (e) => {\n if (e.target === backdrop && pressedBackdrop) this.finish(false);\n pressedBackdrop = false;\n });\n const modal = document.createElement('div');\n modal.className = 'tcw-modal tcw-set';\n modal.setAttribute('role', 'dialog');\n modal.setAttribute('aria-modal', 'true');\n modal.setAttribute('aria-labelledby', `tcw-set-title-${this.uid}`);\n modal.tabIndex = -1;\n modal.addEventListener('keydown', (e) => {\n if (e.key === 'Escape') {\n e.preventDefault();\n e.stopPropagation();\n this.finish(false);\n return;\n }\n keepTabInside(e, modal);\n });\n this.backdrop = backdrop;\n this.modal = modal;\n modal.append(this.buildHeader(), this.buildBody(), this.buildFooter());\n backdrop.appendChild(modal);\n this.host().appendChild(backdrop);\n this.markTabs();\n this.renderTabContent();\n }\n\n private buildHeader(): HTMLDivElement {\n const header = document.createElement('div');\n header.className = 'tcw-modal-header';\n const title = document.createElement('h3');\n title.id = `tcw-set-title-${this.uid}`;\n title.textContent = this.t('settings.title');\n const closeBtn = document.createElement('button');\n closeBtn.type = 'button';\n closeBtn.className = 'tcw-modal-close';\n closeBtn.setAttribute('aria-label', this.t('common.close'));\n closeBtn.innerHTML = createIcon('x', 16);\n closeBtn.addEventListener('click', () => this.finish(false));\n header.append(title, closeBtn);\n return header;\n }\n\n /** The tabs down the side, and the open one's settings beside them. */\n private buildBody(): HTMLDivElement {\n const body = document.createElement('div');\n body.className = 'tcw-set-body';\n const nav = document.createElement('div');\n nav.className = 'tcw-set-nav';\n nav.setAttribute('role', 'tablist');\n nav.setAttribute('aria-orientation', 'vertical');\n nav.setAttribute('aria-label', this.t('settings.title'));\n nav.addEventListener('keydown', (e) => this.onTabKey(e));\n this.tabButtons = TABS.map(({ id, icon }) => {\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.className = 'tcw-set-tab';\n btn.id = `tcw-set-tab-${this.uid}-${id}`;\n btn.dataset.tab = id;\n btn.setAttribute('role', 'tab');\n btn.setAttribute('aria-controls', `tcw-set-panel-${this.uid}`);\n const svg = icon.startsWith('chartType:') ? createChartTypeIcon(icon.slice('chartType:'.length) as ChartType, 16) : createIcon(icon, 16);\n btn.innerHTML = `${svg}<span></span>`;\n btn.querySelector('span')!.textContent = this.t(`settings.tab.${id}` as MessageKey);\n btn.addEventListener('click', () => this.selectTab(id));\n return btn;\n });\n nav.append(...this.tabButtons);\n const panel = document.createElement('div');\n panel.className = 'tcw-modal-body tcw-set-panel';\n panel.id = `tcw-set-panel-${this.uid}`;\n panel.setAttribute('role', 'tabpanel');\n this.bodyEl = panel;\n body.append(nav, panel);\n return body;\n }\n\n /** Template on the left; Cancel and OK on the right. */\n private buildFooter(): HTMLDivElement {\n const footer = document.createElement('div');\n footer.className = 'tcw-modal-footer';\n const template = this.footerButton('template', 'tcw-footer-menu', () => this.openTemplates());\n template.setAttribute('aria-haspopup', 'menu');\n template.setAttribute('aria-expanded', 'false');\n template.innerHTML = `<span></span>${createIcon('chevronDown', 12)}`;\n template.querySelector('span')!.textContent = this.t('settings.template');\n this.templateBtn = template;\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 footer.append(template, end);\n return footer;\n }\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 markTabs(): void {\n for (const btn of this.tabButtons) {\n const on = btn.dataset.tab === this.currentTab;\n btn.classList.toggle('tcw-active', on);\n btn.setAttribute('aria-selected', String(on));\n btn.tabIndex = on ? 0 : -1;\n if (on) this.bodyEl?.setAttribute('aria-labelledby', btn.id);\n }\n }\n\n private selectTab(tab: Tab, focus = false): void {\n this.currentTab = tab;\n this.markTabs();\n this.renderTabContent();\n if (focus) this.tabButtons.find((b) => b.dataset.tab === tab)?.focus();\n }\n\n /** Through the tabs (Home, End), picking as they go: up and down, or along the row of chips on a phone. */\n private onTabKey(e: KeyboardEvent): void {\n const at = TABS.findIndex((tab) => tab.id === this.currentTab);\n const next = e.key === 'ArrowDown' || e.key === 'ArrowRight' ? (at + 1) % TABS.length\n : e.key === 'ArrowUp' || e.key === 'ArrowLeft' ? (at - 1 + TABS.length) % TABS.length\n : e.key === 'Home' ? 0\n : e.key === 'End' ? TABS.length - 1\n : -1;\n if (next < 0) return;\n e.preventDefault();\n this.selectTab(TABS[next].id, true);\n }\n\n /** The Template menu: the saved templates (each removable), Save as template, Reset to defaults. */\n private openTemplates(): void {\n const btn = this.templateBtn;\n if (!this.modal || !btn) return;\n if (this.menu?.isOpen()) {\n this.menu.close();\n return;\n }\n const templates = this.callbacks.templates;\n const saved: ContextMenuEntry[] = (templates?.list() ?? []).map((name) => ({\n id: `${TEMPLATE_ID}${name}`, label: name, icon: 'layers', remove: fill(this.t('templates.delete'), { name }),\n }));\n const entries: ContextMenuEntry[] = [\n ...saved,\n ...(saved.length > 0 ? ['separator' as const] : []),\n ...(templates ? [{ id: 'save', label: this.t('settings.saveTemplate'), icon: 'save' }] : []),\n { id: 'reset', label: this.t('settings.resetToDefaults'), icon: 'reset' },\n ];\n this.menu ??= new WidgetContextMenu(this.modal, this.t('settings.template'));\n const at = btn.getBoundingClientRect();\n const box = this.modal.getBoundingClientRect();\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, (id) => {\n if (id === 'reset') this.callbacks.onReset();\n else if (id === 'save') templates?.save();\n else if (id.startsWith(TEMPLATE_ID)) templates?.apply(id.slice(TEMPLATE_ID.length));\n }, btn, (id) => templates?.remove(id.slice(TEMPLATE_ID.length)));\n }\n\n private renderTabContent(): void {\n if (!this.bodyEl || !this.currentSettings) return;\n this.bodyEl.replaceChildren();\n this.lookRows = [];\n switch (this.currentTab) {\n case 'symbol': this.renderSymbolTab(this.currentSettings); break;\n case 'statusLine': this.renderStatusLineTab(this.currentSettings); break;\n case 'scales': this.renderScalesTab(this.currentSettings); break;\n case 'appearance': this.renderAppearanceTab(this.currentSettings); break;\n case 'layers': this.renderLayersTab(this.currentSettings); break;\n case 'trading':\n for (const section of TRADING_SECTIONS) {\n if (!section.trading || this.available.trading !== false) this.renderLookSection(section);\n }\n break;\n }\n }\n\n /** The chart type's own settings and colours, the main series, the last price, the volume, the high and low. */\n private renderSymbolTab(s: ChartSettingsState): void {\n this.renderChartTypeSection();\n this.addSection(undefined, [\n toggleRow(this.t('settings.mainSeries'), s.mainSeriesVisible, (v) => this.patch({ mainSeriesVisible: v })),\n toggleRow(this.t('settings.extendedHours'), s.extendedHours, (v) => this.patch({ extendedHours: v })),\n ]);\n // A section's own switch says \"Show\": its title names what it shows.\n const show = this.t('common.show');\n this.renderLookSection(LAST_PRICE_SECTION);\n if (this.available.volume !== false) {\n this.renderLookSection(VOLUME_SECTION, [toggleRow(show, s.volumeVisible, (v) => this.patch({ volumeVisible: v }))]);\n }\n this.renderLookSection(HIGH_LOW_SECTION, [toggleRow(show, s.highLowLines, (v) => this.patch({ highLowLines: v }))]);\n }\n\n /** The legend over the chart: shown or not, its colours; the panes' titles. */\n private renderStatusLineTab(s: ChartSettingsState): void {\n const legend = this.available.legend !== false;\n const rows = legend ? STATUS_LINE_ROWS : STATUS_LINE_ROWS.filter((row) => !LEGEND_ROWS.includes(row));\n this.addSection(undefined, [\n ...(legend ? [toggleRow(this.t('settings.ohlcLegend'), s.legendVisible, (v) => this.patch({ legendVisible: v }))] : []),\n ...rows.map((row) => this.lookRow(row)),\n ]);\n }\n\n /** The price scale's mode and behaviour, its labels, the time zone, how numbers are written, the axes' colours. */\n private renderScalesTab(s: ChartSettingsState): void {\n const scales = [\n { value: 'regular', label: this.t('settings.scale.regular') },\n { value: 'logarithmic', label: this.t('settings.scale.logarithmic') },\n { value: 'percentage', label: this.t('settings.scale.percentage') },\n { value: 'indexedTo100', label: this.t('settings.scale.indexedTo100') },\n ].filter((o) => o.value !== 'logarithmic' || this.available.logScale !== false);\n const exchangeZone = this.available.exchangeZone?.();\n const zones = timezoneOptions(s.timezone, Date.now(), this.t('settings.timezone.local'),\n exchangeZone !== undefined ? { label: this.t('settings.timezone.exchange'), zone: exchangeZone } : undefined);\n this.addSection(undefined, [\n selectRow(this.t('settings.priceScale'), s.scaleMode, scales, (v) => this.patch({ scaleMode: v as ChartSettingsState['scaleMode'] })),\n toggleRow(this.t('settings.autoScale'), s.autoScale, (v) => this.patch({ autoScale: v })),\n toggleRow(this.t('settings.invertScale'), s.invertScale, (v) => this.patch({ invertScale: v })),\n toggleRow(this.t('settings.leftScale'), s.leftPriceScale, (v) => this.patch({ leftPriceScale: v })),\n toggleRow(this.t('settings.indicatorValues'), s.indicatorValueLabels, (v) => this.patch({ indicatorValueLabels: v })),\n toggleRow(this.t('settings.alignAxisLabels'), s.alignAxisLabels, (v) => this.patch({ alignAxisLabels: v })),\n ...(this.available.barCountdown !== false\n ? [toggleRow(this.t('settings.barCountdown'), s.barCountdown, (v) => this.patch({ barCountdown: v }))]\n : []),\n selectRow(this.t('settings.timezone'), s.timezone, zones, (v) => this.patch({ timezone: v })),\n selectRow(this.t('settings.numberLocale'), s.numberLocale, numberLocaleOptions(s.numberLocale), (v) => this.patch({ numberLocale: v })),\n ]);\n this.addSection(this.t('settings.section.axes'), SCALE_ROWS.map((row) => this.lookRow(row)));\n }\n\n /** The background, the grid, the crosshair, the session breaks, the drawings' handles. */\n private renderAppearanceTab(s: ChartSettingsState): void {\n const [background, grid, crosshair, ...rest] = APPEARANCE_SECTIONS;\n this.renderLookSection(background);\n this.renderLookSection(grid, [toggleRow(this.t('common.show'), s.gridVisible, (v) => this.patch({ gridVisible: v }))]);\n this.renderLookSection(crosshair, [selectRow(this.t('settings.crosshairMode'), s.crosshairMode, [\n { label: this.t('settings.crosshair.magnet'), value: 'magnet' },\n { label: this.t('settings.crosshair.normal'), value: 'normal' },\n { label: this.t('settings.crosshair.hidden'), value: 'hidden' },\n ], (v) => this.patch({ crosshairMode: v as ChartSettingsState['crosshairMode'] })),\n selectRow(this.t('settings.crosshairCursor'), s.crosshairCursor, [\n { label: this.t('drawing.cursorCross'), value: 'cross' },\n { label: this.t('drawing.cursorDot'), value: 'dot' },\n { label: this.t('drawing.cursorArrow'), value: 'arrow' },\n ], (v) => this.patch({ crosshairCursor: v as ChartSettingsState['crosshairCursor'] }))]);\n for (const section of rest) this.renderLookSection(section);\n }\n\n /** What the chart can draw over the bars: the profiles, the depth, the swings, the sessions, the prior levels. */\n private renderLayersTab(s: ChartSettingsState): void {\n const percent = (v: number) => `${Math.round(v * 100)}%`;\n const t = (key: MessageKey) => this.t(key);\n this.addSection(undefined, [toggleRow(t('settings.volumeProfile'), s.volumeProfileVisible, (v) => this.patch({ volumeProfileVisible: v }))]);\n this.addSection(undefined, [\n toggleRow(t('settings.marketProfile'), s.marketProfileVisible, (v) => this.patch({ marketProfileVisible: v })),\n toggleRow(t('settings.marketProfileSplit'), s.marketProfileSplit, (v) => this.patch({ marketProfileSplit: v })),\n toggleRow(t('settings.marketProfileLetters'), s.marketProfileLetters, (v) => this.patch({ marketProfileLetters: v })),\n rangeRow(t('settings.marketProfileBuckets'), s.marketProfileBuckets, { min: 8, max: 200, step: 1 }, (v) => this.patch({ marketProfileBuckets: v })),\n rangeRow(t('settings.marketProfileOpacity'), s.marketProfileOpacity, { min: 0.05, max: 1, step: 0.05 }, (v) => this.patch({ marketProfileOpacity: v }), percent),\n ]);\n this.addSection(undefined, [\n toggleRow(t('settings.depthHeatmap'), s.depthHeatmapVisible, (v) => this.patch({ depthHeatmapVisible: v })),\n rangeRow(t('settings.depthHeatmapOpacity'), s.depthHeatmapOpacity, { min: 0.1, max: 1, step: 0.05 }, (v) => this.patch({ depthHeatmapOpacity: v }), percent),\n ]);\n this.addSection(undefined, [\n toggleRow(t('settings.swingMarkers'), s.pivotMarkersVisible, (v) => this.patch({ pivotMarkersVisible: v })),\n rangeRow(t('settings.swingStrength'), s.pivotStrength, { min: 2, max: 20, step: 1 }, (v) => this.patch({ pivotStrength: v })),\n toggleRow(t('settings.swingStructure'), s.pivotStructureLabels, (v) => this.patch({ pivotStructureLabels: v })),\n ]);\n this.addSection(undefined, [\n toggleRow(t('settings.sessionShading'), s.sessionShadingVisible, (v) => this.patch({ sessionShadingVisible: v })),\n toggleRow(t('settings.periodLevels'), s.periodLevelsVisible, (v) => this.patch({ periodLevelsVisible: v })),\n selectRow(t('settings.periodLevelsBasis'), s.periodLevelsPeriod, [\n { value: 'day', label: t('settings.priorDay') },\n { value: 'week', label: t('settings.priorWeek') },\n ], (v) => this.patch({ periodLevelsPeriod: v as ChartSettingsState['periodLevelsPeriod'] })),\n ]);\n }\n\n private addSection(title: string | undefined, rows: readonly HTMLElement[]): HTMLDivElement {\n const section = settingsSection(title);\n section.append(...rows);\n this.bodyEl?.appendChild(section);\n return section;\n }\n\n /** A section of the look, after `lead` (the switch it goes with, say). */\n private renderLookSection(section: LookSection, lead: readonly HTMLElement[] = []): void {\n this.addSection(this.t(section.title), [...lead, ...section.rows.map((row) => this.lookRow(row))]);\n }\n\n /** A row of the look: a colour, a switch, a dash or a width, as its key takes it, on the user's layer. */\n private lookRow(row: LookRow): HTMLDivElement {\n const label = this.t(row.label);\n const read = () => this.callbacks.styleValue(row.keys[0]);\n /** The keys' value, or '' when they differ (the host set each way apart): any pick then sets them all. */\n const shared = (): string => {\n const values = row.keys.map((key) => this.callbacks.styleValue(key));\n return values.every((v) => v === values[0]) && values[0] !== null ? String(values[0]) : '';\n };\n let el: HTMLDivElement;\n const set = (v: string | number | boolean | null) => {\n this.callbacks.onStyleChange(Object.fromEntries(row.keys.map((key) => [key, v])) as SettingsStylePatch);\n for (const other of this.lookRows) if (other.el !== el) other.sync();\n };\n const auto = row.auto ? [{ value: '', label: this.t('settings.auto') }] : [];\n let sync: () => void;\n switch (CHART_STYLE_KEYS[row.keys[0]]) {\n case 'boolean': {\n el = toggleRow(label, read() === true, set);\n const toggle = el.querySelector('button')!;\n sync = () => {\n const on = read() === true;\n toggle.classList.toggle('tcw-on', on);\n toggle.setAttribute('aria-checked', String(on));\n };\n break;\n }\n case 'lineStyle':\n el = selectRow(label, shared(), [...auto, ...this.lineStyles()], (v) => set(v === '' ? null : asLineStyle(v)));\n sync = () => { el.querySelector('select')!.value = shared(); };\n break;\n case 'width': {\n const value = read();\n const widths = typeof value === 'number' && !LINE_WIDTHS.includes(value) ? [...LINE_WIDTHS, value].sort((a, b) => a - b) : LINE_WIDTHS;\n el = selectRow(label, shared(), [...auto, ...widths.map((w) => ({ value: String(w), label: `${w}px` }))],\n (v) => set(v === '' ? null : Number(v)));\n sync = () => { el.querySelector('select')!.value = shared(); };\n break;\n }\n default: {\n const value = read();\n const colour = colorRow(label, typeof value === 'string' ? value : null, set, {\n read: () => { const v = read(); return typeof v === 'string' ? v : null; },\n start: row.auto ? () => (row.auto?.from ? this.callbacks.styleValue(row.auto.from) : row.auto?.hint) ?? null : undefined,\n }, this.t);\n el = colour.row;\n sync = colour.sync;\n }\n }\n this.lookRows.push({ el, sync });\n return el;\n }\n\n private lineStyles(): { value: string; label: string }[] {\n return [\n { value: 'solid', label: this.t('drawingSettings.lineStyle.solid') },\n { value: 'dashed', label: this.t('drawingSettings.lineStyle.dashed') },\n { value: 'dotted', label: this.t('drawingSettings.lineStyle.dotted') },\n ];\n }\n\n /** The chart's type: its settings (Renko's box, Kagi's reversal…) and its colours and widths. */\n private renderChartTypeSection(): void {\n if (!this.bodyEl || !this.currentSettings || !this.chartType) return;\n const type = this.chartType;\n const all = this.currentSettings.chartTypeOptions ?? {};\n const def = CHART_TYPES.find((c) => c.value === type);\n const section = settingsSection(def ? chartTypeLabel(def, this.t) : type);\n const own = () => this.currentSettings?.chartTypeOptions ?? {};\n /** Change one type's settings: the rest of its own stay. */\n const set = <K extends keyof ChartTypeOptions>(key: K, value: NonNullable<ChartTypeOptions[K]>) =>\n this.patch({ chartTypeOptions: { ...own(), [key]: value } });\n const number = (label: MessageKey, value: number | null, limits: NumberLimits, onChange: (v: number | null) => void) =>\n section.appendChild(numberRow(this.t(label), value, limits, onChange, this.t, this.currentSettings?.numberLocale || undefined));\n switch (type) {\n case 'renko': {\n const o = all.renko ?? {};\n number('settings.boxSize', typeof o.boxSize === 'number' ? o.boxSize : null, SIZE_LIMITS,\n (v) => set('renko', { ...(own().renko ?? {}), boxSize: v ?? 'atr' }));\n number('settings.atrPeriod', o.atrPeriod ?? null, { step: '1', max: 500 }, (v) => set('renko', withValue(own().renko, 'atrPeriod', v)));\n break;\n }\n case 'lineBreak':\n number('settings.lineBreakLines', all.lineBreak?.lines ?? null, { step: '1', max: 10 }, (v) => set('lineBreak', withValue(own().lineBreak, 'lines', v)));\n break;\n case 'kagi':\n number('settings.reversal', all.kagi?.reversal ?? null, SIZE_LIMITS, (v) => set('kagi', withValue(own().kagi, 'reversal', v)));\n section.appendChild(selectRow(this.t('settings.reversalType'), all.kagi?.reversalType ?? 'percent', [\n { value: 'percent', label: this.t('settings.reversalType.percent') },\n { value: 'price', label: this.t('settings.reversalType.price') },\n ], (v) => set('kagi', { ...(own().kagi ?? {}), reversalType: v === 'price' ? 'price' : 'percent' })));\n break;\n case 'pointAndFigure':\n number('settings.boxSize', typeof all.pointAndFigure?.boxSize === 'number' ? all.pointAndFigure.boxSize : null, SIZE_LIMITS,\n (v) => set('pointAndFigure', { ...(own().pointAndFigure ?? {}), boxSize: v ?? 'auto' }));\n number('settings.reversalBoxes', all.pointAndFigure?.reversal ?? null, { step: '1', max: 10 },\n (v) => set('pointAndFigure', withValue(own().pointAndFigure, 'reversal', v)));\n break;\n case 'rangeBars':\n number('settings.barRange', typeof all.rangeBars?.range === 'number' ? all.rangeBars.range : null, SIZE_LIMITS,\n (v) => set('rangeBars', { range: v ?? 'auto' }));\n break;\n default:\n break;\n }\n for (const row of seriesRows(type)) section.appendChild(this.lookRow(row));\n this.bodyEl.appendChild(section);\n }\n\n private patch(partial: Partial<ChartSettingsState>): void {\n if (this.currentSettings) Object.assign(this.currentSettings, partial);\n this.callbacks.onChange(partial);\n }\n}\n\n/** Whether two settings say the same, whatever order their keys were set in, at any depth. */\nfunction sameSettings(a: ChartSettingsState, b: ChartSettingsState): boolean {\n return sameValue(a, b);\n}\n\nfunction sameValue(a: unknown, b: unknown): boolean {\n if (a === b) return true;\n if (typeof a !== 'object' || typeof b !== 'object' || a === null || b === null) return false;\n if (Array.isArray(a) !== Array.isArray(b)) return false;\n const x = a as Record<string, unknown>;\n const y = b as Record<string, unknown>;\n // A key set to undefined is as one not there, as JSON has it.\n const keys = new Set([...Object.keys(x), ...Object.keys(y)].filter((k) => x[k] !== undefined || y[k] !== undefined));\n for (const key of keys) if (!sameValue(x[key], y[key])) return false;\n return true;\n}\n\n/** `settings` with `key` set to `value`, or without it (back to its default) for null. */\nfunction withValue<T extends object, K extends string>(settings: T | undefined, key: K, value: number | null): T {\n const next = { ...(settings ?? {}) } as Record<string, unknown>;\n if (value === null) delete next[key];\n else next[key] = value;\n return next as T;\n}\n\nfunction asLineStyle(v: string): LineStyle {\n return v === 'dashed' || v === 'dotted' ? v : 'solid';\n}\n"],"mappings":"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"}