@tradecanvas/chart 2.4.0 → 2.6.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 (57) hide show
  1. package/dist/Chart.d.ts +41 -0
  2. package/dist/Chart.d.ts.map +1 -1
  3. package/dist/{LayoutSession-D7ztdedn.js → LayoutSession-D8TcwPy1.js} +510 -436
  4. package/dist/LayoutSession-D8TcwPy1.js.map +1 -0
  5. package/dist/LayoutSession-DDs1N5h2.cjs +2 -0
  6. package/dist/LayoutSession-DDs1N5h2.cjs.map +1 -0
  7. package/dist/{WidgetGoToDate-hcDtezRm.js → WidgetGoToDate-DnQxxvf1.js} +2 -2
  8. package/dist/{WidgetGoToDate-hcDtezRm.js.map → WidgetGoToDate-DnQxxvf1.js.map} +1 -1
  9. package/dist/{WidgetGoToDate-BNdngHlg.cjs → WidgetGoToDate-hNVxz-Zb.cjs} +2 -2
  10. package/dist/{WidgetGoToDate-BNdngHlg.cjs.map → WidgetGoToDate-hNVxz-Zb.cjs.map} +1 -1
  11. package/dist/{WidgetIndicatorPicker-DJQ5taqX.cjs → WidgetIndicatorPicker-DKxoB4Un.cjs} +2 -2
  12. package/dist/{WidgetIndicatorPicker-DJQ5taqX.cjs.map → WidgetIndicatorPicker-DKxoB4Un.cjs.map} +1 -1
  13. package/dist/{WidgetIndicatorPicker-BzV4Mm_W.js → WidgetIndicatorPicker-eK03W3LR.js} +2 -2
  14. package/dist/{WidgetIndicatorPicker-BzV4Mm_W.js.map → WidgetIndicatorPicker-eK03W3LR.js.map} +1 -1
  15. package/dist/{WidgetSymbolSearch-BOgfBOV6.cjs → WidgetSymbolSearch-C_yw9mLo.cjs} +2 -2
  16. package/dist/{WidgetSymbolSearch-BOgfBOV6.cjs.map → WidgetSymbolSearch-C_yw9mLo.cjs.map} +1 -1
  17. package/dist/{WidgetSymbolSearch-BgFNnrV3.js → WidgetSymbolSearch-Dnkh_y4h.js} +2 -2
  18. package/dist/{WidgetSymbolSearch-BgFNnrV3.js.map → WidgetSymbolSearch-Dnkh_y4h.js.map} +1 -1
  19. package/dist/index.cjs +1 -1
  20. package/dist/index.d.ts +2 -0
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +80 -80
  23. package/dist/widget/ChartWidget.d.ts +5 -0
  24. package/dist/widget/ChartWidget.d.ts.map +1 -1
  25. package/dist/widget/WidgetChartNav.d.ts +8 -0
  26. package/dist/widget/WidgetChartNav.d.ts.map +1 -1
  27. package/dist/widget/WidgetDrawingEdit.d.ts +13 -0
  28. package/dist/widget/WidgetDrawingEdit.d.ts.map +1 -1
  29. package/dist/widget/WidgetMarks.d.ts +60 -0
  30. package/dist/widget/WidgetMarks.d.ts.map +1 -0
  31. package/dist/widget/WidgetMenus.d.ts +2 -0
  32. package/dist/widget/WidgetMenus.d.ts.map +1 -1
  33. package/dist/widget/WidgetSelectionToolbar.d.ts +4 -0
  34. package/dist/widget/WidgetSelectionToolbar.d.ts.map +1 -1
  35. package/dist/widget/drawingMenu.d.ts +3 -1
  36. package/dist/widget/drawingMenu.d.ts.map +1 -1
  37. package/dist/widget/index.cjs +4 -3
  38. package/dist/widget/index.cjs.map +1 -1
  39. package/dist/widget/index.js +1245 -1059
  40. package/dist/widget/index.js.map +1 -1
  41. package/dist/widget/symbolInfoView.d.ts +9 -1
  42. package/dist/widget/symbolInfoView.d.ts.map +1 -1
  43. package/dist/widget/types.d.ts +8 -0
  44. package/dist/widget/types.d.ts.map +1 -1
  45. package/dist/widget/widgetFeatures.d.ts +1 -1
  46. package/dist/widget/widgetFeatures.d.ts.map +1 -1
  47. package/dist/widgetFeatures-DpBWfQ8D.cjs +3 -0
  48. package/dist/widgetFeatures-DpBWfQ8D.cjs.map +1 -0
  49. package/dist/{widgetFeatures-BM6u0u74.js → widgetFeatures-sU-9JOSB.js} +3 -2
  50. package/dist/widgetFeatures-sU-9JOSB.js.map +1 -0
  51. package/package.json +3 -3
  52. package/dist/LayoutSession-D7ztdedn.js.map +0 -1
  53. package/dist/LayoutSession-ScBZ-Eci.cjs +0 -2
  54. package/dist/LayoutSession-ScBZ-Eci.cjs.map +0 -1
  55. package/dist/widgetFeatures-BM6u0u74.js.map +0 -1
  56. package/dist/widgetFeatures-BZ8OVPN2.cjs +0 -3
  57. package/dist/widgetFeatures-BZ8OVPN2.cjs.map +0 -1
@@ -1,5 +1,5 @@
1
1
  import { t as e } from "./monthView-N8yDaTqZ.js";
2
- import { o as t } from "./widgetFeatures-BM6u0u74.js";
2
+ import { o as t } from "./widgetFeatures-sU-9JOSB.js";
3
3
  import { i as n, n as r } from "./focusTrap-Br9Zfje0.js";
4
4
  //#region src/widget/WidgetGoToDate.ts
5
5
  var i = ["date", "range"], a = 1, o = class {
@@ -188,4 +188,4 @@ var i = ["date", "range"], a = 1, o = class {
188
188
  //#endregion
189
189
  export { o as WidgetGoToDate };
190
190
 
191
- //# sourceMappingURL=WidgetGoToDate-hcDtezRm.js.map
191
+ //# sourceMappingURL=WidgetGoToDate-DnQxxvf1.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"WidgetGoToDate-hcDtezRm.js","names":[],"sources":["../src/widget/WidgetGoToDate.ts"],"sourcesContent":["import { createIcon } from './icons.js';\nimport { keepTabInside } from './focusTrap.js';\nimport { MonthView, type CalendarLabels } from './monthView.js';\nimport { markPart } from './widgetFeatures.js';\nimport type { WallTime } from './wallTime.js';\n\nexport interface GoToLabels {\n title: string;\n /** The tabs. */\n date: string;\n range: string;\n /** The range's ends. */\n from: string;\n to: string;\n time: string;\n submit: string;\n cancel: string;\n close: string;\n calendar: CalendarLabels;\n}\n\n/** Where to go: a moment to centre, or a span to show edge to edge. */\nexport type GoToTarget =\n | ({ kind: 'date' } & WallTime)\n | { kind: 'range'; from: WallTime; to: WallTime };\n\nexport interface GoToStart {\n /** The Date tab's moment. */\n date: WallTime;\n /** The Custom range tab's ends. */\n range: { from: WallTime; to: WallTime };\n /** Whether bars have a time of day (not on daily bars and longer). */\n withTime: boolean;\n /** The last day that can be picked, YYYY-MM-DD. */\n max?: string;\n /** Today in the time zone the dates are read in; the browser's when left out. */\n today?: string;\n}\n\ntype Tab = 'date' | 'range';\ntype Field = 'date' | 'time' | 'fromDate' | 'fromTime' | 'toDate' | 'toTime';\n\nconst TABS: readonly Tab[] = ['date', 'range'];\n\nlet nextGoToId = 1;\n\n/**\n * The Go to dialog. Date: a day and a time to centre the chart on. Custom\n * range: two of them to show edge to edge. The month under the fields fills\n * the date field in hand; after a range's start it moves on to its end.\n * Mounts in the widget's overlay layer; focus goes back to what opened it.\n */\nexport class WidgetGoToDate {\n private backdrop: HTMLDivElement | null = null;\n private fieldsEl: HTMLDivElement | null = null;\n private tabButtons: HTMLButtonElement[] = [];\n private month: MonthView | null = null;\n /** The date field the month fills. */\n private active: HTMLInputElement | null = null;\n private values: Record<Field, string> = { date: '', time: '', fromDate: '', fromTime: '', toDate: '', toTime: '' };\n private start: GoToStart | null = null;\n private tab: Tab = 'date';\n private returnFocus: HTMLElement | null = null;\n private readonly uid = nextGoToId++;\n\n constructor(\n private readonly layer: () => HTMLElement,\n private readonly labels: () => GoToLabels,\n private readonly locale: () => string,\n private readonly onSubmit: (target: GoToTarget) => void,\n ) {}\n\n isOpen(): boolean {\n return this.backdrop !== null;\n }\n\n open(start: GoToStart): void {\n this.close(false);\n this.returnFocus = document.activeElement instanceof HTMLElement ? document.activeElement : null;\n this.start = start;\n this.values = {\n date: start.date.date,\n time: start.date.time,\n fromDate: start.range.from.date,\n fromTime: start.range.from.time,\n toDate: start.range.to.date,\n toTime: start.range.to.time,\n };\n this.build();\n this.renderTab(true);\n }\n\n /** Close without going anywhere; focus goes back to what opened it unless `giveFocusBack` is false. */\n close(giveFocusBack = true): void {\n if (!this.backdrop) return;\n this.backdrop.remove();\n this.backdrop = this.fieldsEl = this.month = this.active = this.start = null;\n this.tabButtons = [];\n const back = this.returnFocus;\n this.returnFocus = null;\n if (giveFocusBack && back?.isConnected) back.focus();\n }\n\n destroy(): void {\n this.close(false);\n }\n\n private build(): void {\n const labels = this.labels();\n const backdrop = document.createElement('div');\n backdrop.className = 'tcw-modal-backdrop';\n // A press that starts in the dialog and ends on the backdrop doesn't close 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.close();\n pressedBackdrop = false;\n });\n\n const modal = document.createElement('div');\n modal.className = 'tcw-modal tcw-goto';\n markPart(modal, 'goToDate');\n modal.setAttribute('role', 'dialog');\n modal.setAttribute('aria-modal', 'true');\n modal.setAttribute('aria-labelledby', `tcw-goto-title-${this.uid}`);\n modal.tabIndex = -1;\n modal.addEventListener('keydown', (e) => {\n e.stopPropagation(); // typing here must not drive chart shortcuts\n if (e.key === 'Escape') {\n e.preventDefault();\n this.close();\n return;\n }\n keepTabInside(e, modal);\n });\n\n const header = document.createElement('div');\n header.className = 'tcw-modal-header';\n const title = document.createElement('h3');\n title.id = `tcw-goto-title-${this.uid}`;\n title.textContent = labels.title;\n const closeBtn = document.createElement('button');\n closeBtn.type = 'button';\n closeBtn.className = 'tcw-modal-close';\n closeBtn.setAttribute('aria-label', labels.close);\n closeBtn.innerHTML = createIcon('x', 16);\n closeBtn.addEventListener('click', () => this.close());\n header.append(title, closeBtn);\n\n const form = document.createElement('form');\n form.className = 'tcw-goto-form';\n form.noValidate = true;\n form.addEventListener('submit', (e) => {\n e.preventDefault();\n this.submit();\n });\n const fields = document.createElement('div');\n fields.className = 'tcw-goto-fields';\n fields.id = `tcw-goto-panel-${this.uid}`;\n fields.setAttribute('role', 'tabpanel');\n const month = new MonthView(labels.calendar, this.locale(), (value) => this.pickDay(value));\n month.element.classList.add('tcw-goto-calendar');\n form.append(fields, month.element, this.buildFooter(labels));\n\n modal.append(header, this.buildTabs(labels), form);\n backdrop.appendChild(modal);\n this.layer().appendChild(backdrop);\n this.backdrop = backdrop;\n this.fieldsEl = fields;\n this.month = month;\n }\n\n private buildTabs(labels: GoToLabels): HTMLDivElement {\n const tabs = document.createElement('div');\n tabs.className = 'tcw-modal-tabs';\n tabs.setAttribute('role', 'tablist');\n tabs.addEventListener('keydown', (e) => this.onTabKey(e));\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-goto-tab-${this.uid}-${tab}`;\n btn.dataset.tab = tab;\n btn.setAttribute('role', 'tab');\n btn.setAttribute('aria-controls', `tcw-goto-panel-${this.uid}`);\n btn.textContent = tab === 'date' ? labels.date : labels.range;\n btn.addEventListener('click', () => this.select(tab, false));\n return btn;\n });\n tabs.append(...this.tabButtons);\n return tabs;\n }\n\n private buildFooter(labels: GoToLabels): HTMLDivElement {\n const cancel = document.createElement('button');\n cancel.type = 'button';\n cancel.className = 'tcw-footer-cancel';\n cancel.dataset.act = 'cancel';\n cancel.textContent = labels.cancel;\n cancel.addEventListener('click', () => this.close());\n const go = document.createElement('button');\n go.type = 'submit';\n go.className = 'tcw-done-btn';\n go.dataset.act = 'go';\n go.textContent = labels.submit;\n const end = document.createElement('div');\n end.className = 'tcw-footer-end';\n end.append(cancel, go);\n const footer = document.createElement('div');\n footer.className = 'tcw-modal-footer';\n footer.appendChild(end);\n return footer;\n }\n\n private select(tab: Tab, focusTab: boolean): void {\n this.tab = tab;\n this.renderTab(!focusTab);\n if (focusTab) this.tabButtons.find((b) => b.dataset.tab === tab)?.focus();\n }\n\n /** The tab's fields, the month on its first date; `focusField` puts the keyboard there. */\n private renderTab(focusField: boolean): void {\n const fields = this.fieldsEl;\n const start = this.start;\n if (!fields || !start) return;\n for (const btn of this.tabButtons) {\n const on = btn.dataset.tab === this.tab;\n btn.setAttribute('aria-selected', String(on));\n btn.classList.toggle('tcw-active', on);\n btn.tabIndex = on ? 0 : -1;\n }\n fields.setAttribute('aria-labelledby', `tcw-goto-tab-${this.uid}-${this.tab}`);\n const labels = this.labels();\n fields.replaceChildren(...(this.tab === 'date'\n ? [this.row(null, 'date', 'time', labels)]\n : [this.row(labels.from, 'fromDate', 'fromTime', labels), this.row(labels.to, 'toDate', 'toTime', labels)]));\n const first = fields.querySelector<HTMLInputElement>('input[type=\"date\"]');\n this.setActive(first);\n if (focusField) first?.focus();\n }\n\n /** A date and, on intraday bars, a time, after the name of the range's end they set. */\n private row(name: string | null, dateField: Field, timeField: Field, labels: GoToLabels): HTMLDivElement {\n const row = document.createElement('div');\n row.className = 'tcw-goto-row';\n if (name) {\n const label = document.createElement('span');\n label.className = 'tcw-goto-label';\n label.textContent = name;\n row.appendChild(label);\n }\n const prefix = name ? `${name} · ` : '';\n const date = this.input('date', dateField, `${prefix}${labels.date}`);\n date.required = true;\n // The month below is this field's calendar.\n date.dataset.tcwCalendar = 'inline';\n if (this.start?.max) date.max = this.start.max;\n date.addEventListener('focus', () => this.setActive(date));\n date.addEventListener('input', () => {\n date.removeAttribute('aria-invalid');\n if (this.active === date) this.showMonth();\n });\n row.appendChild(date);\n if (this.start?.withTime) row.appendChild(this.input('time', timeField, `${prefix}${labels.time}`));\n return row;\n }\n\n private input(type: 'date' | 'time', field: Field, label: string): HTMLInputElement {\n const input = document.createElement('input');\n input.type = type;\n input.className = 'tcw-goto-input';\n input.dataset.field = field;\n input.value = this.values[field];\n input.setAttribute('aria-label', label);\n input.addEventListener('input', () => {\n this.values[field] = input.value;\n });\n return input;\n }\n\n private setActive(input: HTMLInputElement | null): void {\n this.active = input;\n this.showMonth();\n }\n\n private showMonth(): void {\n if (!this.active) return;\n this.month?.show(this.active.value, { max: this.start?.max, today: this.start?.today });\n }\n\n /** A day from the month: into the field in hand; a range's start then moves on to its end. */\n private pickDay(value: string): void {\n const input = this.active;\n if (!input) return;\n const fromGrid = this.month?.element.contains(document.activeElement) ?? false;\n input.value = value;\n input.dispatchEvent(new Event('input', { bubbles: true }));\n input.dispatchEvent(new Event('change', { bubbles: true }));\n const next = input.dataset.field === 'fromDate'\n ? this.fieldsEl?.querySelector<HTMLInputElement>('[data-field=\"toDate\"]')\n : null;\n if (next) {\n next.focus();\n this.setActive(next);\n return;\n }\n this.showMonth();\n if (fromGrid) this.month?.focusGrid();\n }\n\n private submit(): void {\n // The fields as they are now, whether or not they said they changed (autofill).\n const inputs = [...(this.fieldsEl?.querySelectorAll<HTMLInputElement>('[data-field]') ?? [])];\n for (const input of inputs) this.values[input.dataset.field as Field] = input.value;\n // A date not given (or not whole), or after the last day: marked, the keyboard on it, and nowhere to go.\n const max = this.start?.max;\n const wrong = inputs.find((input) => input.type === 'date' && (!input.value || (max !== undefined && input.value > max)));\n if (wrong) {\n wrong.setAttribute('aria-invalid', 'true');\n wrong.focus();\n return;\n }\n const v = this.values;\n const withTime = this.start?.withTime ?? false;\n const time = (t: string) => (withTime ? t : '');\n let target: GoToTarget;\n if (this.tab === 'date') {\n if (!v.date) return;\n target = { kind: 'date', date: v.date, time: time(v.time) };\n } else {\n if (!v.fromDate || !v.toDate) return;\n target = { kind: 'range', from: { date: v.fromDate, time: time(v.fromTime) }, to: { date: v.toDate, time: time(v.toTime) } };\n }\n this.close();\n this.onSubmit(target);\n }\n\n /** Arrow keys (the other way right to left), Home and End move between the tabs. */\n private onTabKey(e: KeyboardEvent): void {\n const at = TABS.indexOf(this.tab);\n const rtl = this.backdrop?.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) return;\n e.preventDefault();\n this.select(TABS[next], true);\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"WidgetGoToDate-DnQxxvf1.js","names":[],"sources":["../src/widget/WidgetGoToDate.ts"],"sourcesContent":["import { createIcon } from './icons.js';\nimport { keepTabInside } from './focusTrap.js';\nimport { MonthView, type CalendarLabels } from './monthView.js';\nimport { markPart } from './widgetFeatures.js';\nimport type { WallTime } from './wallTime.js';\n\nexport interface GoToLabels {\n title: string;\n /** The tabs. */\n date: string;\n range: string;\n /** The range's ends. */\n from: string;\n to: string;\n time: string;\n submit: string;\n cancel: string;\n close: string;\n calendar: CalendarLabels;\n}\n\n/** Where to go: a moment to centre, or a span to show edge to edge. */\nexport type GoToTarget =\n | ({ kind: 'date' } & WallTime)\n | { kind: 'range'; from: WallTime; to: WallTime };\n\nexport interface GoToStart {\n /** The Date tab's moment. */\n date: WallTime;\n /** The Custom range tab's ends. */\n range: { from: WallTime; to: WallTime };\n /** Whether bars have a time of day (not on daily bars and longer). */\n withTime: boolean;\n /** The last day that can be picked, YYYY-MM-DD. */\n max?: string;\n /** Today in the time zone the dates are read in; the browser's when left out. */\n today?: string;\n}\n\ntype Tab = 'date' | 'range';\ntype Field = 'date' | 'time' | 'fromDate' | 'fromTime' | 'toDate' | 'toTime';\n\nconst TABS: readonly Tab[] = ['date', 'range'];\n\nlet nextGoToId = 1;\n\n/**\n * The Go to dialog. Date: a day and a time to centre the chart on. Custom\n * range: two of them to show edge to edge. The month under the fields fills\n * the date field in hand; after a range's start it moves on to its end.\n * Mounts in the widget's overlay layer; focus goes back to what opened it.\n */\nexport class WidgetGoToDate {\n private backdrop: HTMLDivElement | null = null;\n private fieldsEl: HTMLDivElement | null = null;\n private tabButtons: HTMLButtonElement[] = [];\n private month: MonthView | null = null;\n /** The date field the month fills. */\n private active: HTMLInputElement | null = null;\n private values: Record<Field, string> = { date: '', time: '', fromDate: '', fromTime: '', toDate: '', toTime: '' };\n private start: GoToStart | null = null;\n private tab: Tab = 'date';\n private returnFocus: HTMLElement | null = null;\n private readonly uid = nextGoToId++;\n\n constructor(\n private readonly layer: () => HTMLElement,\n private readonly labels: () => GoToLabels,\n private readonly locale: () => string,\n private readonly onSubmit: (target: GoToTarget) => void,\n ) {}\n\n isOpen(): boolean {\n return this.backdrop !== null;\n }\n\n open(start: GoToStart): void {\n this.close(false);\n this.returnFocus = document.activeElement instanceof HTMLElement ? document.activeElement : null;\n this.start = start;\n this.values = {\n date: start.date.date,\n time: start.date.time,\n fromDate: start.range.from.date,\n fromTime: start.range.from.time,\n toDate: start.range.to.date,\n toTime: start.range.to.time,\n };\n this.build();\n this.renderTab(true);\n }\n\n /** Close without going anywhere; focus goes back to what opened it unless `giveFocusBack` is false. */\n close(giveFocusBack = true): void {\n if (!this.backdrop) return;\n this.backdrop.remove();\n this.backdrop = this.fieldsEl = this.month = this.active = this.start = null;\n this.tabButtons = [];\n const back = this.returnFocus;\n this.returnFocus = null;\n if (giveFocusBack && back?.isConnected) back.focus();\n }\n\n destroy(): void {\n this.close(false);\n }\n\n private build(): void {\n const labels = this.labels();\n const backdrop = document.createElement('div');\n backdrop.className = 'tcw-modal-backdrop';\n // A press that starts in the dialog and ends on the backdrop doesn't close 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.close();\n pressedBackdrop = false;\n });\n\n const modal = document.createElement('div');\n modal.className = 'tcw-modal tcw-goto';\n markPart(modal, 'goToDate');\n modal.setAttribute('role', 'dialog');\n modal.setAttribute('aria-modal', 'true');\n modal.setAttribute('aria-labelledby', `tcw-goto-title-${this.uid}`);\n modal.tabIndex = -1;\n modal.addEventListener('keydown', (e) => {\n e.stopPropagation(); // typing here must not drive chart shortcuts\n if (e.key === 'Escape') {\n e.preventDefault();\n this.close();\n return;\n }\n keepTabInside(e, modal);\n });\n\n const header = document.createElement('div');\n header.className = 'tcw-modal-header';\n const title = document.createElement('h3');\n title.id = `tcw-goto-title-${this.uid}`;\n title.textContent = labels.title;\n const closeBtn = document.createElement('button');\n closeBtn.type = 'button';\n closeBtn.className = 'tcw-modal-close';\n closeBtn.setAttribute('aria-label', labels.close);\n closeBtn.innerHTML = createIcon('x', 16);\n closeBtn.addEventListener('click', () => this.close());\n header.append(title, closeBtn);\n\n const form = document.createElement('form');\n form.className = 'tcw-goto-form';\n form.noValidate = true;\n form.addEventListener('submit', (e) => {\n e.preventDefault();\n this.submit();\n });\n const fields = document.createElement('div');\n fields.className = 'tcw-goto-fields';\n fields.id = `tcw-goto-panel-${this.uid}`;\n fields.setAttribute('role', 'tabpanel');\n const month = new MonthView(labels.calendar, this.locale(), (value) => this.pickDay(value));\n month.element.classList.add('tcw-goto-calendar');\n form.append(fields, month.element, this.buildFooter(labels));\n\n modal.append(header, this.buildTabs(labels), form);\n backdrop.appendChild(modal);\n this.layer().appendChild(backdrop);\n this.backdrop = backdrop;\n this.fieldsEl = fields;\n this.month = month;\n }\n\n private buildTabs(labels: GoToLabels): HTMLDivElement {\n const tabs = document.createElement('div');\n tabs.className = 'tcw-modal-tabs';\n tabs.setAttribute('role', 'tablist');\n tabs.addEventListener('keydown', (e) => this.onTabKey(e));\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-goto-tab-${this.uid}-${tab}`;\n btn.dataset.tab = tab;\n btn.setAttribute('role', 'tab');\n btn.setAttribute('aria-controls', `tcw-goto-panel-${this.uid}`);\n btn.textContent = tab === 'date' ? labels.date : labels.range;\n btn.addEventListener('click', () => this.select(tab, false));\n return btn;\n });\n tabs.append(...this.tabButtons);\n return tabs;\n }\n\n private buildFooter(labels: GoToLabels): HTMLDivElement {\n const cancel = document.createElement('button');\n cancel.type = 'button';\n cancel.className = 'tcw-footer-cancel';\n cancel.dataset.act = 'cancel';\n cancel.textContent = labels.cancel;\n cancel.addEventListener('click', () => this.close());\n const go = document.createElement('button');\n go.type = 'submit';\n go.className = 'tcw-done-btn';\n go.dataset.act = 'go';\n go.textContent = labels.submit;\n const end = document.createElement('div');\n end.className = 'tcw-footer-end';\n end.append(cancel, go);\n const footer = document.createElement('div');\n footer.className = 'tcw-modal-footer';\n footer.appendChild(end);\n return footer;\n }\n\n private select(tab: Tab, focusTab: boolean): void {\n this.tab = tab;\n this.renderTab(!focusTab);\n if (focusTab) this.tabButtons.find((b) => b.dataset.tab === tab)?.focus();\n }\n\n /** The tab's fields, the month on its first date; `focusField` puts the keyboard there. */\n private renderTab(focusField: boolean): void {\n const fields = this.fieldsEl;\n const start = this.start;\n if (!fields || !start) return;\n for (const btn of this.tabButtons) {\n const on = btn.dataset.tab === this.tab;\n btn.setAttribute('aria-selected', String(on));\n btn.classList.toggle('tcw-active', on);\n btn.tabIndex = on ? 0 : -1;\n }\n fields.setAttribute('aria-labelledby', `tcw-goto-tab-${this.uid}-${this.tab}`);\n const labels = this.labels();\n fields.replaceChildren(...(this.tab === 'date'\n ? [this.row(null, 'date', 'time', labels)]\n : [this.row(labels.from, 'fromDate', 'fromTime', labels), this.row(labels.to, 'toDate', 'toTime', labels)]));\n const first = fields.querySelector<HTMLInputElement>('input[type=\"date\"]');\n this.setActive(first);\n if (focusField) first?.focus();\n }\n\n /** A date and, on intraday bars, a time, after the name of the range's end they set. */\n private row(name: string | null, dateField: Field, timeField: Field, labels: GoToLabels): HTMLDivElement {\n const row = document.createElement('div');\n row.className = 'tcw-goto-row';\n if (name) {\n const label = document.createElement('span');\n label.className = 'tcw-goto-label';\n label.textContent = name;\n row.appendChild(label);\n }\n const prefix = name ? `${name} · ` : '';\n const date = this.input('date', dateField, `${prefix}${labels.date}`);\n date.required = true;\n // The month below is this field's calendar.\n date.dataset.tcwCalendar = 'inline';\n if (this.start?.max) date.max = this.start.max;\n date.addEventListener('focus', () => this.setActive(date));\n date.addEventListener('input', () => {\n date.removeAttribute('aria-invalid');\n if (this.active === date) this.showMonth();\n });\n row.appendChild(date);\n if (this.start?.withTime) row.appendChild(this.input('time', timeField, `${prefix}${labels.time}`));\n return row;\n }\n\n private input(type: 'date' | 'time', field: Field, label: string): HTMLInputElement {\n const input = document.createElement('input');\n input.type = type;\n input.className = 'tcw-goto-input';\n input.dataset.field = field;\n input.value = this.values[field];\n input.setAttribute('aria-label', label);\n input.addEventListener('input', () => {\n this.values[field] = input.value;\n });\n return input;\n }\n\n private setActive(input: HTMLInputElement | null): void {\n this.active = input;\n this.showMonth();\n }\n\n private showMonth(): void {\n if (!this.active) return;\n this.month?.show(this.active.value, { max: this.start?.max, today: this.start?.today });\n }\n\n /** A day from the month: into the field in hand; a range's start then moves on to its end. */\n private pickDay(value: string): void {\n const input = this.active;\n if (!input) return;\n const fromGrid = this.month?.element.contains(document.activeElement) ?? false;\n input.value = value;\n input.dispatchEvent(new Event('input', { bubbles: true }));\n input.dispatchEvent(new Event('change', { bubbles: true }));\n const next = input.dataset.field === 'fromDate'\n ? this.fieldsEl?.querySelector<HTMLInputElement>('[data-field=\"toDate\"]')\n : null;\n if (next) {\n next.focus();\n this.setActive(next);\n return;\n }\n this.showMonth();\n if (fromGrid) this.month?.focusGrid();\n }\n\n private submit(): void {\n // The fields as they are now, whether or not they said they changed (autofill).\n const inputs = [...(this.fieldsEl?.querySelectorAll<HTMLInputElement>('[data-field]') ?? [])];\n for (const input of inputs) this.values[input.dataset.field as Field] = input.value;\n // A date not given (or not whole), or after the last day: marked, the keyboard on it, and nowhere to go.\n const max = this.start?.max;\n const wrong = inputs.find((input) => input.type === 'date' && (!input.value || (max !== undefined && input.value > max)));\n if (wrong) {\n wrong.setAttribute('aria-invalid', 'true');\n wrong.focus();\n return;\n }\n const v = this.values;\n const withTime = this.start?.withTime ?? false;\n const time = (t: string) => (withTime ? t : '');\n let target: GoToTarget;\n if (this.tab === 'date') {\n if (!v.date) return;\n target = { kind: 'date', date: v.date, time: time(v.time) };\n } else {\n if (!v.fromDate || !v.toDate) return;\n target = { kind: 'range', from: { date: v.fromDate, time: time(v.fromTime) }, to: { date: v.toDate, time: time(v.toTime) } };\n }\n this.close();\n this.onSubmit(target);\n }\n\n /** Arrow keys (the other way right to left), Home and End move between the tabs. */\n private onTabKey(e: KeyboardEvent): void {\n const at = TABS.indexOf(this.tab);\n const rtl = this.backdrop?.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) return;\n e.preventDefault();\n this.select(TABS[next], true);\n }\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- const e=require(`./monthView-B-CieFDJ.cjs`),t=require(`./widgetFeatures-BZ8OVPN2.cjs`),n=require(`./focusTrap-DrhhmG0V.cjs`);var r=[`date`,`range`],i=1,a=class{backdrop=null;fieldsEl=null;tabButtons=[];month=null;active=null;values={date:``,time:``,fromDate:``,fromTime:``,toDate:``,toTime:``};start=null;tab=`date`;returnFocus=null;uid=i++;constructor(e,t,n,r){this.layer=e,this.labels=t,this.locale=n,this.onSubmit=r}isOpen(){return this.backdrop!==null}open(e){this.close(!1),this.returnFocus=document.activeElement instanceof HTMLElement?document.activeElement:null,this.start=e,this.values={date:e.date.date,time:e.date.time,fromDate:e.range.from.date,fromTime:e.range.from.time,toDate:e.range.to.date,toTime:e.range.to.time},this.build(),this.renderTab(!0)}close(e=!0){if(!this.backdrop)return;this.backdrop.remove(),this.backdrop=this.fieldsEl=this.month=this.active=this.start=null,this.tabButtons=[];let t=this.returnFocus;this.returnFocus=null,e&&t?.isConnected&&t.focus()}destroy(){this.close(!1)}build(){let r=this.labels(),i=document.createElement(`div`);i.className=`tcw-modal-backdrop`;let a=!1;i.addEventListener(`pointerdown`,e=>{a=e.target===i}),i.addEventListener(`click`,e=>{e.target===i&&a&&this.close(),a=!1});let o=document.createElement(`div`);o.className=`tcw-modal tcw-goto`,t.o(o,`goToDate`),o.setAttribute(`role`,`dialog`),o.setAttribute(`aria-modal`,`true`),o.setAttribute(`aria-labelledby`,`tcw-goto-title-${this.uid}`),o.tabIndex=-1,o.addEventListener(`keydown`,e=>{if(e.stopPropagation(),e.key===`Escape`){e.preventDefault(),this.close();return}n.n(e,o)});let s=document.createElement(`div`);s.className=`tcw-modal-header`;let c=document.createElement(`h3`);c.id=`tcw-goto-title-${this.uid}`,c.textContent=r.title;let l=document.createElement(`button`);l.type=`button`,l.className=`tcw-modal-close`,l.setAttribute(`aria-label`,r.close),l.innerHTML=n.i(`x`,16),l.addEventListener(`click`,()=>this.close()),s.append(c,l);let u=document.createElement(`form`);u.className=`tcw-goto-form`,u.noValidate=!0,u.addEventListener(`submit`,e=>{e.preventDefault(),this.submit()});let d=document.createElement(`div`);d.className=`tcw-goto-fields`,d.id=`tcw-goto-panel-${this.uid}`,d.setAttribute(`role`,`tabpanel`);let f=new e.t(r.calendar,this.locale(),e=>this.pickDay(e));f.element.classList.add(`tcw-goto-calendar`),u.append(d,f.element,this.buildFooter(r)),o.append(s,this.buildTabs(r),u),i.appendChild(o),this.layer().appendChild(i),this.backdrop=i,this.fieldsEl=d,this.month=f}buildTabs(e){let t=document.createElement(`div`);return t.className=`tcw-modal-tabs`,t.setAttribute(`role`,`tablist`),t.addEventListener(`keydown`,e=>this.onTabKey(e)),this.tabButtons=r.map(t=>{let n=document.createElement(`button`);return n.type=`button`,n.className=`tcw-modal-tab`,n.id=`tcw-goto-tab-${this.uid}-${t}`,n.dataset.tab=t,n.setAttribute(`role`,`tab`),n.setAttribute(`aria-controls`,`tcw-goto-panel-${this.uid}`),n.textContent=t===`date`?e.date:e.range,n.addEventListener(`click`,()=>this.select(t,!1)),n}),t.append(...this.tabButtons),t}buildFooter(e){let t=document.createElement(`button`);t.type=`button`,t.className=`tcw-footer-cancel`,t.dataset.act=`cancel`,t.textContent=e.cancel,t.addEventListener(`click`,()=>this.close());let n=document.createElement(`button`);n.type=`submit`,n.className=`tcw-done-btn`,n.dataset.act=`go`,n.textContent=e.submit;let r=document.createElement(`div`);r.className=`tcw-footer-end`,r.append(t,n);let i=document.createElement(`div`);return i.className=`tcw-modal-footer`,i.appendChild(r),i}select(e,t){this.tab=e,this.renderTab(!t),t&&this.tabButtons.find(t=>t.dataset.tab===e)?.focus()}renderTab(e){let t=this.fieldsEl,n=this.start;if(!t||!n)return;for(let e of this.tabButtons){let t=e.dataset.tab===this.tab;e.setAttribute(`aria-selected`,String(t)),e.classList.toggle(`tcw-active`,t),e.tabIndex=t?0:-1}t.setAttribute(`aria-labelledby`,`tcw-goto-tab-${this.uid}-${this.tab}`);let r=this.labels();t.replaceChildren(...this.tab===`date`?[this.row(null,`date`,`time`,r)]:[this.row(r.from,`fromDate`,`fromTime`,r),this.row(r.to,`toDate`,`toTime`,r)]);let i=t.querySelector(`input[type="date"]`);this.setActive(i),e&&i?.focus()}row(e,t,n,r){let i=document.createElement(`div`);if(i.className=`tcw-goto-row`,e){let t=document.createElement(`span`);t.className=`tcw-goto-label`,t.textContent=e,i.appendChild(t)}let a=e?`${e} · `:``,o=this.input(`date`,t,`${a}${r.date}`);return o.required=!0,o.dataset.tcwCalendar=`inline`,this.start?.max&&(o.max=this.start.max),o.addEventListener(`focus`,()=>this.setActive(o)),o.addEventListener(`input`,()=>{o.removeAttribute(`aria-invalid`),this.active===o&&this.showMonth()}),i.appendChild(o),this.start?.withTime&&i.appendChild(this.input(`time`,n,`${a}${r.time}`)),i}input(e,t,n){let r=document.createElement(`input`);return r.type=e,r.className=`tcw-goto-input`,r.dataset.field=t,r.value=this.values[t],r.setAttribute(`aria-label`,n),r.addEventListener(`input`,()=>{this.values[t]=r.value}),r}setActive(e){this.active=e,this.showMonth()}showMonth(){this.active&&this.month?.show(this.active.value,{max:this.start?.max,today:this.start?.today})}pickDay(e){let t=this.active;if(!t)return;let n=this.month?.element.contains(document.activeElement)??!1;t.value=e,t.dispatchEvent(new Event(`input`,{bubbles:!0})),t.dispatchEvent(new Event(`change`,{bubbles:!0}));let r=t.dataset.field===`fromDate`?this.fieldsEl?.querySelector(`[data-field="toDate"]`):null;if(r){r.focus(),this.setActive(r);return}this.showMonth(),n&&this.month?.focusGrid()}submit(){let e=[...this.fieldsEl?.querySelectorAll(`[data-field]`)??[]];for(let t of e)this.values[t.dataset.field]=t.value;let t=this.start?.max,n=e.find(e=>e.type===`date`&&(!e.value||t!==void 0&&e.value>t));if(n){n.setAttribute(`aria-invalid`,`true`),n.focus();return}let r=this.values,i=this.start?.withTime??!1,a=e=>i?e:``,o;if(this.tab===`date`){if(!r.date)return;o={kind:`date`,date:r.date,time:a(r.time)}}else{if(!r.fromDate||!r.toDate)return;o={kind:`range`,from:{date:r.fromDate,time:a(r.fromTime)},to:{date:r.toDate,time:a(r.toTime)}}}this.close(),this.onSubmit(o)}onTabKey(e){let t=r.indexOf(this.tab),[n,i]=this.backdrop?.closest(`[dir]`)?.getAttribute(`dir`)===`rtl`?[`ArrowLeft`,`ArrowRight`]:[`ArrowRight`,`ArrowLeft`],a=e.key===n?(t+1)%r.length:e.key===i?(t-1+r.length)%r.length:e.key===`Home`?0:e.key===`End`?r.length-1:-1;a<0||(e.preventDefault(),this.select(r[a],!0))}};exports.WidgetGoToDate=a;
2
- //# sourceMappingURL=WidgetGoToDate-BNdngHlg.cjs.map
1
+ const e=require(`./monthView-B-CieFDJ.cjs`),t=require(`./widgetFeatures-DpBWfQ8D.cjs`),n=require(`./focusTrap-DrhhmG0V.cjs`);var r=[`date`,`range`],i=1,a=class{backdrop=null;fieldsEl=null;tabButtons=[];month=null;active=null;values={date:``,time:``,fromDate:``,fromTime:``,toDate:``,toTime:``};start=null;tab=`date`;returnFocus=null;uid=i++;constructor(e,t,n,r){this.layer=e,this.labels=t,this.locale=n,this.onSubmit=r}isOpen(){return this.backdrop!==null}open(e){this.close(!1),this.returnFocus=document.activeElement instanceof HTMLElement?document.activeElement:null,this.start=e,this.values={date:e.date.date,time:e.date.time,fromDate:e.range.from.date,fromTime:e.range.from.time,toDate:e.range.to.date,toTime:e.range.to.time},this.build(),this.renderTab(!0)}close(e=!0){if(!this.backdrop)return;this.backdrop.remove(),this.backdrop=this.fieldsEl=this.month=this.active=this.start=null,this.tabButtons=[];let t=this.returnFocus;this.returnFocus=null,e&&t?.isConnected&&t.focus()}destroy(){this.close(!1)}build(){let r=this.labels(),i=document.createElement(`div`);i.className=`tcw-modal-backdrop`;let a=!1;i.addEventListener(`pointerdown`,e=>{a=e.target===i}),i.addEventListener(`click`,e=>{e.target===i&&a&&this.close(),a=!1});let o=document.createElement(`div`);o.className=`tcw-modal tcw-goto`,t.o(o,`goToDate`),o.setAttribute(`role`,`dialog`),o.setAttribute(`aria-modal`,`true`),o.setAttribute(`aria-labelledby`,`tcw-goto-title-${this.uid}`),o.tabIndex=-1,o.addEventListener(`keydown`,e=>{if(e.stopPropagation(),e.key===`Escape`){e.preventDefault(),this.close();return}n.n(e,o)});let s=document.createElement(`div`);s.className=`tcw-modal-header`;let c=document.createElement(`h3`);c.id=`tcw-goto-title-${this.uid}`,c.textContent=r.title;let l=document.createElement(`button`);l.type=`button`,l.className=`tcw-modal-close`,l.setAttribute(`aria-label`,r.close),l.innerHTML=n.i(`x`,16),l.addEventListener(`click`,()=>this.close()),s.append(c,l);let u=document.createElement(`form`);u.className=`tcw-goto-form`,u.noValidate=!0,u.addEventListener(`submit`,e=>{e.preventDefault(),this.submit()});let d=document.createElement(`div`);d.className=`tcw-goto-fields`,d.id=`tcw-goto-panel-${this.uid}`,d.setAttribute(`role`,`tabpanel`);let f=new e.t(r.calendar,this.locale(),e=>this.pickDay(e));f.element.classList.add(`tcw-goto-calendar`),u.append(d,f.element,this.buildFooter(r)),o.append(s,this.buildTabs(r),u),i.appendChild(o),this.layer().appendChild(i),this.backdrop=i,this.fieldsEl=d,this.month=f}buildTabs(e){let t=document.createElement(`div`);return t.className=`tcw-modal-tabs`,t.setAttribute(`role`,`tablist`),t.addEventListener(`keydown`,e=>this.onTabKey(e)),this.tabButtons=r.map(t=>{let n=document.createElement(`button`);return n.type=`button`,n.className=`tcw-modal-tab`,n.id=`tcw-goto-tab-${this.uid}-${t}`,n.dataset.tab=t,n.setAttribute(`role`,`tab`),n.setAttribute(`aria-controls`,`tcw-goto-panel-${this.uid}`),n.textContent=t===`date`?e.date:e.range,n.addEventListener(`click`,()=>this.select(t,!1)),n}),t.append(...this.tabButtons),t}buildFooter(e){let t=document.createElement(`button`);t.type=`button`,t.className=`tcw-footer-cancel`,t.dataset.act=`cancel`,t.textContent=e.cancel,t.addEventListener(`click`,()=>this.close());let n=document.createElement(`button`);n.type=`submit`,n.className=`tcw-done-btn`,n.dataset.act=`go`,n.textContent=e.submit;let r=document.createElement(`div`);r.className=`tcw-footer-end`,r.append(t,n);let i=document.createElement(`div`);return i.className=`tcw-modal-footer`,i.appendChild(r),i}select(e,t){this.tab=e,this.renderTab(!t),t&&this.tabButtons.find(t=>t.dataset.tab===e)?.focus()}renderTab(e){let t=this.fieldsEl,n=this.start;if(!t||!n)return;for(let e of this.tabButtons){let t=e.dataset.tab===this.tab;e.setAttribute(`aria-selected`,String(t)),e.classList.toggle(`tcw-active`,t),e.tabIndex=t?0:-1}t.setAttribute(`aria-labelledby`,`tcw-goto-tab-${this.uid}-${this.tab}`);let r=this.labels();t.replaceChildren(...this.tab===`date`?[this.row(null,`date`,`time`,r)]:[this.row(r.from,`fromDate`,`fromTime`,r),this.row(r.to,`toDate`,`toTime`,r)]);let i=t.querySelector(`input[type="date"]`);this.setActive(i),e&&i?.focus()}row(e,t,n,r){let i=document.createElement(`div`);if(i.className=`tcw-goto-row`,e){let t=document.createElement(`span`);t.className=`tcw-goto-label`,t.textContent=e,i.appendChild(t)}let a=e?`${e} · `:``,o=this.input(`date`,t,`${a}${r.date}`);return o.required=!0,o.dataset.tcwCalendar=`inline`,this.start?.max&&(o.max=this.start.max),o.addEventListener(`focus`,()=>this.setActive(o)),o.addEventListener(`input`,()=>{o.removeAttribute(`aria-invalid`),this.active===o&&this.showMonth()}),i.appendChild(o),this.start?.withTime&&i.appendChild(this.input(`time`,n,`${a}${r.time}`)),i}input(e,t,n){let r=document.createElement(`input`);return r.type=e,r.className=`tcw-goto-input`,r.dataset.field=t,r.value=this.values[t],r.setAttribute(`aria-label`,n),r.addEventListener(`input`,()=>{this.values[t]=r.value}),r}setActive(e){this.active=e,this.showMonth()}showMonth(){this.active&&this.month?.show(this.active.value,{max:this.start?.max,today:this.start?.today})}pickDay(e){let t=this.active;if(!t)return;let n=this.month?.element.contains(document.activeElement)??!1;t.value=e,t.dispatchEvent(new Event(`input`,{bubbles:!0})),t.dispatchEvent(new Event(`change`,{bubbles:!0}));let r=t.dataset.field===`fromDate`?this.fieldsEl?.querySelector(`[data-field="toDate"]`):null;if(r){r.focus(),this.setActive(r);return}this.showMonth(),n&&this.month?.focusGrid()}submit(){let e=[...this.fieldsEl?.querySelectorAll(`[data-field]`)??[]];for(let t of e)this.values[t.dataset.field]=t.value;let t=this.start?.max,n=e.find(e=>e.type===`date`&&(!e.value||t!==void 0&&e.value>t));if(n){n.setAttribute(`aria-invalid`,`true`),n.focus();return}let r=this.values,i=this.start?.withTime??!1,a=e=>i?e:``,o;if(this.tab===`date`){if(!r.date)return;o={kind:`date`,date:r.date,time:a(r.time)}}else{if(!r.fromDate||!r.toDate)return;o={kind:`range`,from:{date:r.fromDate,time:a(r.fromTime)},to:{date:r.toDate,time:a(r.toTime)}}}this.close(),this.onSubmit(o)}onTabKey(e){let t=r.indexOf(this.tab),[n,i]=this.backdrop?.closest(`[dir]`)?.getAttribute(`dir`)===`rtl`?[`ArrowLeft`,`ArrowRight`]:[`ArrowRight`,`ArrowLeft`],a=e.key===n?(t+1)%r.length:e.key===i?(t-1+r.length)%r.length:e.key===`Home`?0:e.key===`End`?r.length-1:-1;a<0||(e.preventDefault(),this.select(r[a],!0))}};exports.WidgetGoToDate=a;
2
+ //# sourceMappingURL=WidgetGoToDate-hNVxz-Zb.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"WidgetGoToDate-BNdngHlg.cjs","names":[],"sources":["../src/widget/WidgetGoToDate.ts"],"sourcesContent":["import { createIcon } from './icons.js';\nimport { keepTabInside } from './focusTrap.js';\nimport { MonthView, type CalendarLabels } from './monthView.js';\nimport { markPart } from './widgetFeatures.js';\nimport type { WallTime } from './wallTime.js';\n\nexport interface GoToLabels {\n title: string;\n /** The tabs. */\n date: string;\n range: string;\n /** The range's ends. */\n from: string;\n to: string;\n time: string;\n submit: string;\n cancel: string;\n close: string;\n calendar: CalendarLabels;\n}\n\n/** Where to go: a moment to centre, or a span to show edge to edge. */\nexport type GoToTarget =\n | ({ kind: 'date' } & WallTime)\n | { kind: 'range'; from: WallTime; to: WallTime };\n\nexport interface GoToStart {\n /** The Date tab's moment. */\n date: WallTime;\n /** The Custom range tab's ends. */\n range: { from: WallTime; to: WallTime };\n /** Whether bars have a time of day (not on daily bars and longer). */\n withTime: boolean;\n /** The last day that can be picked, YYYY-MM-DD. */\n max?: string;\n /** Today in the time zone the dates are read in; the browser's when left out. */\n today?: string;\n}\n\ntype Tab = 'date' | 'range';\ntype Field = 'date' | 'time' | 'fromDate' | 'fromTime' | 'toDate' | 'toTime';\n\nconst TABS: readonly Tab[] = ['date', 'range'];\n\nlet nextGoToId = 1;\n\n/**\n * The Go to dialog. Date: a day and a time to centre the chart on. Custom\n * range: two of them to show edge to edge. The month under the fields fills\n * the date field in hand; after a range's start it moves on to its end.\n * Mounts in the widget's overlay layer; focus goes back to what opened it.\n */\nexport class WidgetGoToDate {\n private backdrop: HTMLDivElement | null = null;\n private fieldsEl: HTMLDivElement | null = null;\n private tabButtons: HTMLButtonElement[] = [];\n private month: MonthView | null = null;\n /** The date field the month fills. */\n private active: HTMLInputElement | null = null;\n private values: Record<Field, string> = { date: '', time: '', fromDate: '', fromTime: '', toDate: '', toTime: '' };\n private start: GoToStart | null = null;\n private tab: Tab = 'date';\n private returnFocus: HTMLElement | null = null;\n private readonly uid = nextGoToId++;\n\n constructor(\n private readonly layer: () => HTMLElement,\n private readonly labels: () => GoToLabels,\n private readonly locale: () => string,\n private readonly onSubmit: (target: GoToTarget) => void,\n ) {}\n\n isOpen(): boolean {\n return this.backdrop !== null;\n }\n\n open(start: GoToStart): void {\n this.close(false);\n this.returnFocus = document.activeElement instanceof HTMLElement ? document.activeElement : null;\n this.start = start;\n this.values = {\n date: start.date.date,\n time: start.date.time,\n fromDate: start.range.from.date,\n fromTime: start.range.from.time,\n toDate: start.range.to.date,\n toTime: start.range.to.time,\n };\n this.build();\n this.renderTab(true);\n }\n\n /** Close without going anywhere; focus goes back to what opened it unless `giveFocusBack` is false. */\n close(giveFocusBack = true): void {\n if (!this.backdrop) return;\n this.backdrop.remove();\n this.backdrop = this.fieldsEl = this.month = this.active = this.start = null;\n this.tabButtons = [];\n const back = this.returnFocus;\n this.returnFocus = null;\n if (giveFocusBack && back?.isConnected) back.focus();\n }\n\n destroy(): void {\n this.close(false);\n }\n\n private build(): void {\n const labels = this.labels();\n const backdrop = document.createElement('div');\n backdrop.className = 'tcw-modal-backdrop';\n // A press that starts in the dialog and ends on the backdrop doesn't close 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.close();\n pressedBackdrop = false;\n });\n\n const modal = document.createElement('div');\n modal.className = 'tcw-modal tcw-goto';\n markPart(modal, 'goToDate');\n modal.setAttribute('role', 'dialog');\n modal.setAttribute('aria-modal', 'true');\n modal.setAttribute('aria-labelledby', `tcw-goto-title-${this.uid}`);\n modal.tabIndex = -1;\n modal.addEventListener('keydown', (e) => {\n e.stopPropagation(); // typing here must not drive chart shortcuts\n if (e.key === 'Escape') {\n e.preventDefault();\n this.close();\n return;\n }\n keepTabInside(e, modal);\n });\n\n const header = document.createElement('div');\n header.className = 'tcw-modal-header';\n const title = document.createElement('h3');\n title.id = `tcw-goto-title-${this.uid}`;\n title.textContent = labels.title;\n const closeBtn = document.createElement('button');\n closeBtn.type = 'button';\n closeBtn.className = 'tcw-modal-close';\n closeBtn.setAttribute('aria-label', labels.close);\n closeBtn.innerHTML = createIcon('x', 16);\n closeBtn.addEventListener('click', () => this.close());\n header.append(title, closeBtn);\n\n const form = document.createElement('form');\n form.className = 'tcw-goto-form';\n form.noValidate = true;\n form.addEventListener('submit', (e) => {\n e.preventDefault();\n this.submit();\n });\n const fields = document.createElement('div');\n fields.className = 'tcw-goto-fields';\n fields.id = `tcw-goto-panel-${this.uid}`;\n fields.setAttribute('role', 'tabpanel');\n const month = new MonthView(labels.calendar, this.locale(), (value) => this.pickDay(value));\n month.element.classList.add('tcw-goto-calendar');\n form.append(fields, month.element, this.buildFooter(labels));\n\n modal.append(header, this.buildTabs(labels), form);\n backdrop.appendChild(modal);\n this.layer().appendChild(backdrop);\n this.backdrop = backdrop;\n this.fieldsEl = fields;\n this.month = month;\n }\n\n private buildTabs(labels: GoToLabels): HTMLDivElement {\n const tabs = document.createElement('div');\n tabs.className = 'tcw-modal-tabs';\n tabs.setAttribute('role', 'tablist');\n tabs.addEventListener('keydown', (e) => this.onTabKey(e));\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-goto-tab-${this.uid}-${tab}`;\n btn.dataset.tab = tab;\n btn.setAttribute('role', 'tab');\n btn.setAttribute('aria-controls', `tcw-goto-panel-${this.uid}`);\n btn.textContent = tab === 'date' ? labels.date : labels.range;\n btn.addEventListener('click', () => this.select(tab, false));\n return btn;\n });\n tabs.append(...this.tabButtons);\n return tabs;\n }\n\n private buildFooter(labels: GoToLabels): HTMLDivElement {\n const cancel = document.createElement('button');\n cancel.type = 'button';\n cancel.className = 'tcw-footer-cancel';\n cancel.dataset.act = 'cancel';\n cancel.textContent = labels.cancel;\n cancel.addEventListener('click', () => this.close());\n const go = document.createElement('button');\n go.type = 'submit';\n go.className = 'tcw-done-btn';\n go.dataset.act = 'go';\n go.textContent = labels.submit;\n const end = document.createElement('div');\n end.className = 'tcw-footer-end';\n end.append(cancel, go);\n const footer = document.createElement('div');\n footer.className = 'tcw-modal-footer';\n footer.appendChild(end);\n return footer;\n }\n\n private select(tab: Tab, focusTab: boolean): void {\n this.tab = tab;\n this.renderTab(!focusTab);\n if (focusTab) this.tabButtons.find((b) => b.dataset.tab === tab)?.focus();\n }\n\n /** The tab's fields, the month on its first date; `focusField` puts the keyboard there. */\n private renderTab(focusField: boolean): void {\n const fields = this.fieldsEl;\n const start = this.start;\n if (!fields || !start) return;\n for (const btn of this.tabButtons) {\n const on = btn.dataset.tab === this.tab;\n btn.setAttribute('aria-selected', String(on));\n btn.classList.toggle('tcw-active', on);\n btn.tabIndex = on ? 0 : -1;\n }\n fields.setAttribute('aria-labelledby', `tcw-goto-tab-${this.uid}-${this.tab}`);\n const labels = this.labels();\n fields.replaceChildren(...(this.tab === 'date'\n ? [this.row(null, 'date', 'time', labels)]\n : [this.row(labels.from, 'fromDate', 'fromTime', labels), this.row(labels.to, 'toDate', 'toTime', labels)]));\n const first = fields.querySelector<HTMLInputElement>('input[type=\"date\"]');\n this.setActive(first);\n if (focusField) first?.focus();\n }\n\n /** A date and, on intraday bars, a time, after the name of the range's end they set. */\n private row(name: string | null, dateField: Field, timeField: Field, labels: GoToLabels): HTMLDivElement {\n const row = document.createElement('div');\n row.className = 'tcw-goto-row';\n if (name) {\n const label = document.createElement('span');\n label.className = 'tcw-goto-label';\n label.textContent = name;\n row.appendChild(label);\n }\n const prefix = name ? `${name} · ` : '';\n const date = this.input('date', dateField, `${prefix}${labels.date}`);\n date.required = true;\n // The month below is this field's calendar.\n date.dataset.tcwCalendar = 'inline';\n if (this.start?.max) date.max = this.start.max;\n date.addEventListener('focus', () => this.setActive(date));\n date.addEventListener('input', () => {\n date.removeAttribute('aria-invalid');\n if (this.active === date) this.showMonth();\n });\n row.appendChild(date);\n if (this.start?.withTime) row.appendChild(this.input('time', timeField, `${prefix}${labels.time}`));\n return row;\n }\n\n private input(type: 'date' | 'time', field: Field, label: string): HTMLInputElement {\n const input = document.createElement('input');\n input.type = type;\n input.className = 'tcw-goto-input';\n input.dataset.field = field;\n input.value = this.values[field];\n input.setAttribute('aria-label', label);\n input.addEventListener('input', () => {\n this.values[field] = input.value;\n });\n return input;\n }\n\n private setActive(input: HTMLInputElement | null): void {\n this.active = input;\n this.showMonth();\n }\n\n private showMonth(): void {\n if (!this.active) return;\n this.month?.show(this.active.value, { max: this.start?.max, today: this.start?.today });\n }\n\n /** A day from the month: into the field in hand; a range's start then moves on to its end. */\n private pickDay(value: string): void {\n const input = this.active;\n if (!input) return;\n const fromGrid = this.month?.element.contains(document.activeElement) ?? false;\n input.value = value;\n input.dispatchEvent(new Event('input', { bubbles: true }));\n input.dispatchEvent(new Event('change', { bubbles: true }));\n const next = input.dataset.field === 'fromDate'\n ? this.fieldsEl?.querySelector<HTMLInputElement>('[data-field=\"toDate\"]')\n : null;\n if (next) {\n next.focus();\n this.setActive(next);\n return;\n }\n this.showMonth();\n if (fromGrid) this.month?.focusGrid();\n }\n\n private submit(): void {\n // The fields as they are now, whether or not they said they changed (autofill).\n const inputs = [...(this.fieldsEl?.querySelectorAll<HTMLInputElement>('[data-field]') ?? [])];\n for (const input of inputs) this.values[input.dataset.field as Field] = input.value;\n // A date not given (or not whole), or after the last day: marked, the keyboard on it, and nowhere to go.\n const max = this.start?.max;\n const wrong = inputs.find((input) => input.type === 'date' && (!input.value || (max !== undefined && input.value > max)));\n if (wrong) {\n wrong.setAttribute('aria-invalid', 'true');\n wrong.focus();\n return;\n }\n const v = this.values;\n const withTime = this.start?.withTime ?? false;\n const time = (t: string) => (withTime ? t : '');\n let target: GoToTarget;\n if (this.tab === 'date') {\n if (!v.date) return;\n target = { kind: 'date', date: v.date, time: time(v.time) };\n } else {\n if (!v.fromDate || !v.toDate) return;\n target = { kind: 'range', from: { date: v.fromDate, time: time(v.fromTime) }, to: { date: v.toDate, time: time(v.toTime) } };\n }\n this.close();\n this.onSubmit(target);\n }\n\n /** Arrow keys (the other way right to left), Home and End move between the tabs. */\n private onTabKey(e: KeyboardEvent): void {\n const at = TABS.indexOf(this.tab);\n const rtl = this.backdrop?.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) return;\n e.preventDefault();\n this.select(TABS[next], true);\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"WidgetGoToDate-hNVxz-Zb.cjs","names":[],"sources":["../src/widget/WidgetGoToDate.ts"],"sourcesContent":["import { createIcon } from './icons.js';\nimport { keepTabInside } from './focusTrap.js';\nimport { MonthView, type CalendarLabels } from './monthView.js';\nimport { markPart } from './widgetFeatures.js';\nimport type { WallTime } from './wallTime.js';\n\nexport interface GoToLabels {\n title: string;\n /** The tabs. */\n date: string;\n range: string;\n /** The range's ends. */\n from: string;\n to: string;\n time: string;\n submit: string;\n cancel: string;\n close: string;\n calendar: CalendarLabels;\n}\n\n/** Where to go: a moment to centre, or a span to show edge to edge. */\nexport type GoToTarget =\n | ({ kind: 'date' } & WallTime)\n | { kind: 'range'; from: WallTime; to: WallTime };\n\nexport interface GoToStart {\n /** The Date tab's moment. */\n date: WallTime;\n /** The Custom range tab's ends. */\n range: { from: WallTime; to: WallTime };\n /** Whether bars have a time of day (not on daily bars and longer). */\n withTime: boolean;\n /** The last day that can be picked, YYYY-MM-DD. */\n max?: string;\n /** Today in the time zone the dates are read in; the browser's when left out. */\n today?: string;\n}\n\ntype Tab = 'date' | 'range';\ntype Field = 'date' | 'time' | 'fromDate' | 'fromTime' | 'toDate' | 'toTime';\n\nconst TABS: readonly Tab[] = ['date', 'range'];\n\nlet nextGoToId = 1;\n\n/**\n * The Go to dialog. Date: a day and a time to centre the chart on. Custom\n * range: two of them to show edge to edge. The month under the fields fills\n * the date field in hand; after a range's start it moves on to its end.\n * Mounts in the widget's overlay layer; focus goes back to what opened it.\n */\nexport class WidgetGoToDate {\n private backdrop: HTMLDivElement | null = null;\n private fieldsEl: HTMLDivElement | null = null;\n private tabButtons: HTMLButtonElement[] = [];\n private month: MonthView | null = null;\n /** The date field the month fills. */\n private active: HTMLInputElement | null = null;\n private values: Record<Field, string> = { date: '', time: '', fromDate: '', fromTime: '', toDate: '', toTime: '' };\n private start: GoToStart | null = null;\n private tab: Tab = 'date';\n private returnFocus: HTMLElement | null = null;\n private readonly uid = nextGoToId++;\n\n constructor(\n private readonly layer: () => HTMLElement,\n private readonly labels: () => GoToLabels,\n private readonly locale: () => string,\n private readonly onSubmit: (target: GoToTarget) => void,\n ) {}\n\n isOpen(): boolean {\n return this.backdrop !== null;\n }\n\n open(start: GoToStart): void {\n this.close(false);\n this.returnFocus = document.activeElement instanceof HTMLElement ? document.activeElement : null;\n this.start = start;\n this.values = {\n date: start.date.date,\n time: start.date.time,\n fromDate: start.range.from.date,\n fromTime: start.range.from.time,\n toDate: start.range.to.date,\n toTime: start.range.to.time,\n };\n this.build();\n this.renderTab(true);\n }\n\n /** Close without going anywhere; focus goes back to what opened it unless `giveFocusBack` is false. */\n close(giveFocusBack = true): void {\n if (!this.backdrop) return;\n this.backdrop.remove();\n this.backdrop = this.fieldsEl = this.month = this.active = this.start = null;\n this.tabButtons = [];\n const back = this.returnFocus;\n this.returnFocus = null;\n if (giveFocusBack && back?.isConnected) back.focus();\n }\n\n destroy(): void {\n this.close(false);\n }\n\n private build(): void {\n const labels = this.labels();\n const backdrop = document.createElement('div');\n backdrop.className = 'tcw-modal-backdrop';\n // A press that starts in the dialog and ends on the backdrop doesn't close 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.close();\n pressedBackdrop = false;\n });\n\n const modal = document.createElement('div');\n modal.className = 'tcw-modal tcw-goto';\n markPart(modal, 'goToDate');\n modal.setAttribute('role', 'dialog');\n modal.setAttribute('aria-modal', 'true');\n modal.setAttribute('aria-labelledby', `tcw-goto-title-${this.uid}`);\n modal.tabIndex = -1;\n modal.addEventListener('keydown', (e) => {\n e.stopPropagation(); // typing here must not drive chart shortcuts\n if (e.key === 'Escape') {\n e.preventDefault();\n this.close();\n return;\n }\n keepTabInside(e, modal);\n });\n\n const header = document.createElement('div');\n header.className = 'tcw-modal-header';\n const title = document.createElement('h3');\n title.id = `tcw-goto-title-${this.uid}`;\n title.textContent = labels.title;\n const closeBtn = document.createElement('button');\n closeBtn.type = 'button';\n closeBtn.className = 'tcw-modal-close';\n closeBtn.setAttribute('aria-label', labels.close);\n closeBtn.innerHTML = createIcon('x', 16);\n closeBtn.addEventListener('click', () => this.close());\n header.append(title, closeBtn);\n\n const form = document.createElement('form');\n form.className = 'tcw-goto-form';\n form.noValidate = true;\n form.addEventListener('submit', (e) => {\n e.preventDefault();\n this.submit();\n });\n const fields = document.createElement('div');\n fields.className = 'tcw-goto-fields';\n fields.id = `tcw-goto-panel-${this.uid}`;\n fields.setAttribute('role', 'tabpanel');\n const month = new MonthView(labels.calendar, this.locale(), (value) => this.pickDay(value));\n month.element.classList.add('tcw-goto-calendar');\n form.append(fields, month.element, this.buildFooter(labels));\n\n modal.append(header, this.buildTabs(labels), form);\n backdrop.appendChild(modal);\n this.layer().appendChild(backdrop);\n this.backdrop = backdrop;\n this.fieldsEl = fields;\n this.month = month;\n }\n\n private buildTabs(labels: GoToLabels): HTMLDivElement {\n const tabs = document.createElement('div');\n tabs.className = 'tcw-modal-tabs';\n tabs.setAttribute('role', 'tablist');\n tabs.addEventListener('keydown', (e) => this.onTabKey(e));\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-goto-tab-${this.uid}-${tab}`;\n btn.dataset.tab = tab;\n btn.setAttribute('role', 'tab');\n btn.setAttribute('aria-controls', `tcw-goto-panel-${this.uid}`);\n btn.textContent = tab === 'date' ? labels.date : labels.range;\n btn.addEventListener('click', () => this.select(tab, false));\n return btn;\n });\n tabs.append(...this.tabButtons);\n return tabs;\n }\n\n private buildFooter(labels: GoToLabels): HTMLDivElement {\n const cancel = document.createElement('button');\n cancel.type = 'button';\n cancel.className = 'tcw-footer-cancel';\n cancel.dataset.act = 'cancel';\n cancel.textContent = labels.cancel;\n cancel.addEventListener('click', () => this.close());\n const go = document.createElement('button');\n go.type = 'submit';\n go.className = 'tcw-done-btn';\n go.dataset.act = 'go';\n go.textContent = labels.submit;\n const end = document.createElement('div');\n end.className = 'tcw-footer-end';\n end.append(cancel, go);\n const footer = document.createElement('div');\n footer.className = 'tcw-modal-footer';\n footer.appendChild(end);\n return footer;\n }\n\n private select(tab: Tab, focusTab: boolean): void {\n this.tab = tab;\n this.renderTab(!focusTab);\n if (focusTab) this.tabButtons.find((b) => b.dataset.tab === tab)?.focus();\n }\n\n /** The tab's fields, the month on its first date; `focusField` puts the keyboard there. */\n private renderTab(focusField: boolean): void {\n const fields = this.fieldsEl;\n const start = this.start;\n if (!fields || !start) return;\n for (const btn of this.tabButtons) {\n const on = btn.dataset.tab === this.tab;\n btn.setAttribute('aria-selected', String(on));\n btn.classList.toggle('tcw-active', on);\n btn.tabIndex = on ? 0 : -1;\n }\n fields.setAttribute('aria-labelledby', `tcw-goto-tab-${this.uid}-${this.tab}`);\n const labels = this.labels();\n fields.replaceChildren(...(this.tab === 'date'\n ? [this.row(null, 'date', 'time', labels)]\n : [this.row(labels.from, 'fromDate', 'fromTime', labels), this.row(labels.to, 'toDate', 'toTime', labels)]));\n const first = fields.querySelector<HTMLInputElement>('input[type=\"date\"]');\n this.setActive(first);\n if (focusField) first?.focus();\n }\n\n /** A date and, on intraday bars, a time, after the name of the range's end they set. */\n private row(name: string | null, dateField: Field, timeField: Field, labels: GoToLabels): HTMLDivElement {\n const row = document.createElement('div');\n row.className = 'tcw-goto-row';\n if (name) {\n const label = document.createElement('span');\n label.className = 'tcw-goto-label';\n label.textContent = name;\n row.appendChild(label);\n }\n const prefix = name ? `${name} · ` : '';\n const date = this.input('date', dateField, `${prefix}${labels.date}`);\n date.required = true;\n // The month below is this field's calendar.\n date.dataset.tcwCalendar = 'inline';\n if (this.start?.max) date.max = this.start.max;\n date.addEventListener('focus', () => this.setActive(date));\n date.addEventListener('input', () => {\n date.removeAttribute('aria-invalid');\n if (this.active === date) this.showMonth();\n });\n row.appendChild(date);\n if (this.start?.withTime) row.appendChild(this.input('time', timeField, `${prefix}${labels.time}`));\n return row;\n }\n\n private input(type: 'date' | 'time', field: Field, label: string): HTMLInputElement {\n const input = document.createElement('input');\n input.type = type;\n input.className = 'tcw-goto-input';\n input.dataset.field = field;\n input.value = this.values[field];\n input.setAttribute('aria-label', label);\n input.addEventListener('input', () => {\n this.values[field] = input.value;\n });\n return input;\n }\n\n private setActive(input: HTMLInputElement | null): void {\n this.active = input;\n this.showMonth();\n }\n\n private showMonth(): void {\n if (!this.active) return;\n this.month?.show(this.active.value, { max: this.start?.max, today: this.start?.today });\n }\n\n /** A day from the month: into the field in hand; a range's start then moves on to its end. */\n private pickDay(value: string): void {\n const input = this.active;\n if (!input) return;\n const fromGrid = this.month?.element.contains(document.activeElement) ?? false;\n input.value = value;\n input.dispatchEvent(new Event('input', { bubbles: true }));\n input.dispatchEvent(new Event('change', { bubbles: true }));\n const next = input.dataset.field === 'fromDate'\n ? this.fieldsEl?.querySelector<HTMLInputElement>('[data-field=\"toDate\"]')\n : null;\n if (next) {\n next.focus();\n this.setActive(next);\n return;\n }\n this.showMonth();\n if (fromGrid) this.month?.focusGrid();\n }\n\n private submit(): void {\n // The fields as they are now, whether or not they said they changed (autofill).\n const inputs = [...(this.fieldsEl?.querySelectorAll<HTMLInputElement>('[data-field]') ?? [])];\n for (const input of inputs) this.values[input.dataset.field as Field] = input.value;\n // A date not given (or not whole), or after the last day: marked, the keyboard on it, and nowhere to go.\n const max = this.start?.max;\n const wrong = inputs.find((input) => input.type === 'date' && (!input.value || (max !== undefined && input.value > max)));\n if (wrong) {\n wrong.setAttribute('aria-invalid', 'true');\n wrong.focus();\n return;\n }\n const v = this.values;\n const withTime = this.start?.withTime ?? false;\n const time = (t: string) => (withTime ? t : '');\n let target: GoToTarget;\n if (this.tab === 'date') {\n if (!v.date) return;\n target = { kind: 'date', date: v.date, time: time(v.time) };\n } else {\n if (!v.fromDate || !v.toDate) return;\n target = { kind: 'range', from: { date: v.fromDate, time: time(v.fromTime) }, to: { date: v.toDate, time: time(v.toTime) } };\n }\n this.close();\n this.onSubmit(target);\n }\n\n /** Arrow keys (the other way right to left), Home and End move between the tabs. */\n private onTabKey(e: KeyboardEvent): void {\n const at = TABS.indexOf(this.tab);\n const rtl = this.backdrop?.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) return;\n e.preventDefault();\n this.select(TABS[next], true);\n }\n}\n"],"mappings":"6HA0CA,IAAM,EAAuB,CAAC,OAAQ,QAAQ,CAE1C,EAAa,EAQJ,EAAb,KAA4B,CAC1B,SAA0C,KAC1C,SAA0C,KAC1C,WAA0C,EAAE,CAC5C,MAAkC,KAElC,OAA0C,KAC1C,OAAwC,CAAE,KAAM,GAAI,KAAM,GAAI,SAAU,GAAI,SAAU,GAAI,OAAQ,GAAI,OAAQ,GAAI,CAClH,MAAkC,KAClC,IAAmB,OACnB,YAA0C,KAC1C,IAAuB,IAEvB,YACE,EACA,EACA,EACA,EACA,CAJiB,KAAA,MAAA,EACA,KAAA,OAAA,EACA,KAAA,OAAA,EACA,KAAA,SAAA,EAGnB,QAAkB,CAChB,OAAO,KAAK,WAAa,KAG3B,KAAK,EAAwB,CAC3B,KAAK,MAAM,GAAM,CACjB,KAAK,YAAc,SAAS,yBAAyB,YAAc,SAAS,cAAgB,KAC5F,KAAK,MAAQ,EACb,KAAK,OAAS,CACZ,KAAM,EAAM,KAAK,KACjB,KAAM,EAAM,KAAK,KACjB,SAAU,EAAM,MAAM,KAAK,KAC3B,SAAU,EAAM,MAAM,KAAK,KAC3B,OAAQ,EAAM,MAAM,GAAG,KACvB,OAAQ,EAAM,MAAM,GAAG,KACxB,CACD,KAAK,OAAO,CACZ,KAAK,UAAU,GAAK,CAItB,MAAM,EAAgB,GAAY,CAChC,GAAI,CAAC,KAAK,SAAU,OACpB,KAAK,SAAS,QAAQ,CACtB,KAAK,SAAW,KAAK,SAAW,KAAK,MAAQ,KAAK,OAAS,KAAK,MAAQ,KACxE,KAAK,WAAa,EAAE,CACpB,IAAM,EAAO,KAAK,YAClB,KAAK,YAAc,KACf,GAAiB,GAAM,aAAa,EAAK,OAAO,CAGtD,SAAgB,CACd,KAAK,MAAM,GAAM,CAGnB,OAAsB,CACpB,IAAM,EAAS,KAAK,QAAQ,CACtB,EAAW,SAAS,cAAc,MAAM,CAC9C,EAAS,UAAY,qBAErB,IAAI,EAAkB,GACtB,EAAS,iBAAiB,cAAgB,GAAM,CAC9C,EAAkB,EAAE,SAAW,GAC/B,CACF,EAAS,iBAAiB,QAAU,GAAM,CACpC,EAAE,SAAW,GAAY,GAAiB,KAAK,OAAO,CAC1D,EAAkB,IAClB,CAEF,IAAM,EAAQ,SAAS,cAAc,MAAM,CAC3C,EAAM,UAAY,qBAClB,EAAA,EAAS,EAAO,WAAW,CAC3B,EAAM,aAAa,OAAQ,SAAS,CACpC,EAAM,aAAa,aAAc,OAAO,CACxC,EAAM,aAAa,kBAAmB,kBAAkB,KAAK,MAAM,CACnE,EAAM,SAAW,GACjB,EAAM,iBAAiB,UAAY,GAAM,CAEvC,GADA,EAAE,iBAAiB,CACf,EAAE,MAAQ,SAAU,CACtB,EAAE,gBAAgB,CAClB,KAAK,OAAO,CACZ,OAEF,EAAA,EAAc,EAAG,EAAM,EACvB,CAEF,IAAM,EAAS,SAAS,cAAc,MAAM,CAC5C,EAAO,UAAY,mBACnB,IAAM,EAAQ,SAAS,cAAc,KAAK,CAC1C,EAAM,GAAK,kBAAkB,KAAK,MAClC,EAAM,YAAc,EAAO,MAC3B,IAAM,EAAW,SAAS,cAAc,SAAS,CACjD,EAAS,KAAO,SAChB,EAAS,UAAY,kBACrB,EAAS,aAAa,aAAc,EAAO,MAAM,CACjD,EAAS,UAAY,EAAA,EAAW,IAAK,GAAG,CACxC,EAAS,iBAAiB,YAAe,KAAK,OAAO,CAAC,CACtD,EAAO,OAAO,EAAO,EAAS,CAE9B,IAAM,EAAO,SAAS,cAAc,OAAO,CAC3C,EAAK,UAAY,gBACjB,EAAK,WAAa,GAClB,EAAK,iBAAiB,SAAW,GAAM,CACrC,EAAE,gBAAgB,CAClB,KAAK,QAAQ,EACb,CACF,IAAM,EAAS,SAAS,cAAc,MAAM,CAC5C,EAAO,UAAY,kBACnB,EAAO,GAAK,kBAAkB,KAAK,MACnC,EAAO,aAAa,OAAQ,WAAW,CACvC,IAAM,EAAQ,IAAI,EAAA,EAAU,EAAO,SAAU,KAAK,QAAQ,CAAG,GAAU,KAAK,QAAQ,EAAM,CAAC,CAC3F,EAAM,QAAQ,UAAU,IAAI,oBAAoB,CAChD,EAAK,OAAO,EAAQ,EAAM,QAAS,KAAK,YAAY,EAAO,CAAC,CAE5D,EAAM,OAAO,EAAQ,KAAK,UAAU,EAAO,CAAE,EAAK,CAClD,EAAS,YAAY,EAAM,CAC3B,KAAK,OAAO,CAAC,YAAY,EAAS,CAClC,KAAK,SAAW,EAChB,KAAK,SAAW,EAChB,KAAK,MAAQ,EAGf,UAAkB,EAAoC,CACpD,IAAM,EAAO,SAAS,cAAc,MAAM,CAiB1C,MAhBA,GAAK,UAAY,iBACjB,EAAK,aAAa,OAAQ,UAAU,CACpC,EAAK,iBAAiB,UAAY,GAAM,KAAK,SAAS,EAAE,CAAC,CACzD,KAAK,WAAa,EAAK,IAAK,GAAQ,CAClC,IAAM,EAAM,SAAS,cAAc,SAAS,CAS5C,MARA,GAAI,KAAO,SACX,EAAI,UAAY,gBAChB,EAAI,GAAK,gBAAgB,KAAK,IAAI,GAAG,IACrC,EAAI,QAAQ,IAAM,EAClB,EAAI,aAAa,OAAQ,MAAM,CAC/B,EAAI,aAAa,gBAAiB,kBAAkB,KAAK,MAAM,CAC/D,EAAI,YAAc,IAAQ,OAAS,EAAO,KAAO,EAAO,MACxD,EAAI,iBAAiB,YAAe,KAAK,OAAO,EAAK,GAAM,CAAC,CACrD,GACP,CACF,EAAK,OAAO,GAAG,KAAK,WAAW,CACxB,EAGT,YAAoB,EAAoC,CACtD,IAAM,EAAS,SAAS,cAAc,SAAS,CAC/C,EAAO,KAAO,SACd,EAAO,UAAY,oBACnB,EAAO,QAAQ,IAAM,SACrB,EAAO,YAAc,EAAO,OAC5B,EAAO,iBAAiB,YAAe,KAAK,OAAO,CAAC,CACpD,IAAM,EAAK,SAAS,cAAc,SAAS,CAC3C,EAAG,KAAO,SACV,EAAG,UAAY,eACf,EAAG,QAAQ,IAAM,KACjB,EAAG,YAAc,EAAO,OACxB,IAAM,EAAM,SAAS,cAAc,MAAM,CACzC,EAAI,UAAY,iBAChB,EAAI,OAAO,EAAQ,EAAG,CACtB,IAAM,EAAS,SAAS,cAAc,MAAM,CAG5C,MAFA,GAAO,UAAY,mBACnB,EAAO,YAAY,EAAI,CAChB,EAGT,OAAe,EAAU,EAAyB,CAChD,KAAK,IAAM,EACX,KAAK,UAAU,CAAC,EAAS,CACrB,GAAU,KAAK,WAAW,KAAM,GAAM,EAAE,QAAQ,MAAQ,EAAI,EAAE,OAAO,CAI3E,UAAkB,EAA2B,CAC3C,IAAM,EAAS,KAAK,SACd,EAAQ,KAAK,MACnB,GAAI,CAAC,GAAU,CAAC,EAAO,OACvB,IAAK,IAAM,KAAO,KAAK,WAAY,CACjC,IAAM,EAAK,EAAI,QAAQ,MAAQ,KAAK,IACpC,EAAI,aAAa,gBAAiB,OAAO,EAAG,CAAC,CAC7C,EAAI,UAAU,OAAO,aAAc,EAAG,CACtC,EAAI,SAAW,EAAK,EAAI,GAE1B,EAAO,aAAa,kBAAmB,gBAAgB,KAAK,IAAI,GAAG,KAAK,MAAM,CAC9E,IAAM,EAAS,KAAK,QAAQ,CAC5B,EAAO,gBAAgB,GAAI,KAAK,MAAQ,OACpC,CAAC,KAAK,IAAI,KAAM,OAAQ,OAAQ,EAAO,CAAC,CACxC,CAAC,KAAK,IAAI,EAAO,KAAM,WAAY,WAAY,EAAO,CAAE,KAAK,IAAI,EAAO,GAAI,SAAU,SAAU,EAAO,CAAC,CAAE,CAC9G,IAAM,EAAQ,EAAO,cAAgC,qBAAqB,CAC1E,KAAK,UAAU,EAAM,CACjB,GAAY,GAAO,OAAO,CAIhC,IAAY,EAAqB,EAAkB,EAAkB,EAAoC,CACvG,IAAM,EAAM,SAAS,cAAc,MAAM,CAEzC,GADA,EAAI,UAAY,eACZ,EAAM,CACR,IAAM,EAAQ,SAAS,cAAc,OAAO,CAC5C,EAAM,UAAY,iBAClB,EAAM,YAAc,EACpB,EAAI,YAAY,EAAM,CAExB,IAAM,EAAS,EAAO,GAAG,EAAK,KAAO,GAC/B,EAAO,KAAK,MAAM,OAAQ,EAAW,GAAG,IAAS,EAAO,OAAO,CAYrE,MAXA,GAAK,SAAW,GAEhB,EAAK,QAAQ,YAAc,SACvB,KAAK,OAAO,MAAK,EAAK,IAAM,KAAK,MAAM,KAC3C,EAAK,iBAAiB,YAAe,KAAK,UAAU,EAAK,CAAC,CAC1D,EAAK,iBAAiB,YAAe,CACnC,EAAK,gBAAgB,eAAe,CAChC,KAAK,SAAW,GAAM,KAAK,WAAW,EAC1C,CACF,EAAI,YAAY,EAAK,CACjB,KAAK,OAAO,UAAU,EAAI,YAAY,KAAK,MAAM,OAAQ,EAAW,GAAG,IAAS,EAAO,OAAO,CAAC,CAC5F,EAGT,MAAc,EAAuB,EAAc,EAAiC,CAClF,IAAM,EAAQ,SAAS,cAAc,QAAQ,CAS7C,MARA,GAAM,KAAO,EACb,EAAM,UAAY,iBAClB,EAAM,QAAQ,MAAQ,EACtB,EAAM,MAAQ,KAAK,OAAO,GAC1B,EAAM,aAAa,aAAc,EAAM,CACvC,EAAM,iBAAiB,YAAe,CACpC,KAAK,OAAO,GAAS,EAAM,OAC3B,CACK,EAGT,UAAkB,EAAsC,CACtD,KAAK,OAAS,EACd,KAAK,WAAW,CAGlB,WAA0B,CACnB,KAAK,QACV,KAAK,OAAO,KAAK,KAAK,OAAO,MAAO,CAAE,IAAK,KAAK,OAAO,IAAK,MAAO,KAAK,OAAO,MAAO,CAAC,CAIzF,QAAgB,EAAqB,CACnC,IAAM,EAAQ,KAAK,OACnB,GAAI,CAAC,EAAO,OACZ,IAAM,EAAW,KAAK,OAAO,QAAQ,SAAS,SAAS,cAAc,EAAI,GACzE,EAAM,MAAQ,EACd,EAAM,cAAc,IAAI,MAAM,QAAS,CAAE,QAAS,GAAM,CAAC,CAAC,CAC1D,EAAM,cAAc,IAAI,MAAM,SAAU,CAAE,QAAS,GAAM,CAAC,CAAC,CAC3D,IAAM,EAAO,EAAM,QAAQ,QAAU,WACjC,KAAK,UAAU,cAAgC,wBAAwB,CACvE,KACJ,GAAI,EAAM,CACR,EAAK,OAAO,CACZ,KAAK,UAAU,EAAK,CACpB,OAEF,KAAK,WAAW,CACZ,GAAU,KAAK,OAAO,WAAW,CAGvC,QAAuB,CAErB,IAAM,EAAS,CAAC,GAAI,KAAK,UAAU,iBAAmC,eAAe,EAAI,EAAE,CAAE,CAC7F,IAAK,IAAM,KAAS,EAAQ,KAAK,OAAO,EAAM,QAAQ,OAAkB,EAAM,MAE9E,IAAM,EAAM,KAAK,OAAO,IAClB,EAAQ,EAAO,KAAM,GAAU,EAAM,OAAS,SAAW,CAAC,EAAM,OAAU,IAAQ,IAAA,IAAa,EAAM,MAAQ,GAAM,CACzH,GAAI,EAAO,CACT,EAAM,aAAa,eAAgB,OAAO,CAC1C,EAAM,OAAO,CACb,OAEF,IAAM,EAAI,KAAK,OACT,EAAW,KAAK,OAAO,UAAY,GACnC,EAAQ,GAAe,EAAW,EAAI,GACxC,EACJ,GAAI,KAAK,MAAQ,OAAQ,CACvB,GAAI,CAAC,EAAE,KAAM,OACb,EAAS,CAAE,KAAM,OAAQ,KAAM,EAAE,KAAM,KAAM,EAAK,EAAE,KAAK,CAAE,KACtD,CACL,GAAI,CAAC,EAAE,UAAY,CAAC,EAAE,OAAQ,OAC9B,EAAS,CAAE,KAAM,QAAS,KAAM,CAAE,KAAM,EAAE,SAAU,KAAM,EAAK,EAAE,SAAS,CAAE,CAAE,GAAI,CAAE,KAAM,EAAE,OAAQ,KAAM,EAAK,EAAE,OAAO,CAAE,CAAE,CAE9H,KAAK,OAAO,CACZ,KAAK,SAAS,EAAO,CAIvB,SAAiB,EAAwB,CACvC,IAAM,EAAK,EAAK,QAAQ,KAAK,IAAI,CAE3B,CAAC,EAAS,GADJ,KAAK,UAAU,QAAQ,QAAQ,EAAE,aAAa,MAAM,GAAK,MACvC,CAAC,YAAa,aAAa,CAAG,CAAC,aAAc,YAAY,CACjF,EAAO,EAAE,MAAQ,GAAW,EAAK,GAAK,EAAK,OAC7C,EAAE,MAAQ,GAAQ,EAAK,EAAI,EAAK,QAAU,EAAK,OAC/C,EAAE,MAAQ,OAAS,EACnB,EAAE,MAAQ,MAAQ,EAAK,OAAS,EAChC,GACA,EAAO,IACX,EAAE,gBAAgB,CAClB,KAAK,OAAO,EAAK,GAAO,GAAK"}
@@ -1,2 +1,2 @@
1
- const e=require(`./widgetFeatures-BZ8OVPN2.cjs`),t=require(`./focusTrap-DrhhmG0V.cjs`),n=require(`./i18n-fgAwciRT.cjs`);let r=require(`@tradecanvas/core`);function i(e){return e.normalize(`NFD`).replace(/[̀-ͯ]/g,``).toLowerCase()}var a=/[\s/()%-]+/;function o(e,t){let n=i(e.shortName),r=i(e.name);return n===t||i(e.id)===t?0:n.startsWith(t)||r.startsWith(t)?1:[...r.split(a),...n.split(a)].some(e=>e.startsWith(t))?2:r.includes(t)||n.includes(t)?3:null}function s(e,t){let n=i(t.trim());return n?e.map(e=>({item:e,rank:o(e,n)})).filter(e=>e.rank!==null).sort((e,t)=>e.rank-t.rank||e.item.name.localeCompare(t.item.name)).map(e=>e.item):[]}var c=1,l=(e,t)=>e.name.localeCompare(t.name),u=class{backdrop=null;listEl=null;navEl=null;searchEl=null;liveEl=null;content={items:[],favorites:[],templates:[]};favorites=new Set;counts=new Map;view=`all`;lastView=null;query=``;returnFocus=null;uid=c++;constructor(e,t,n){this.layer=e,this.callbacks=t,this.t=n}open(e){this.backdrop&&this.close(!1),this.returnFocus=document.activeElement instanceof HTMLElement?document.activeElement:null,this.content=e,this.favorites=new Set(e.favorites),this.counts=e.counts??new Map,this.query=``,this.view=this.startView(),this.build(),this.searchEl?.focus()}close(e=!0){this.backdrop&&(this.backdrop.remove(),this.backdrop=this.listEl=this.navEl=this.searchEl=this.liveEl=null,this.callbacks.onClose?.(),e&&this.returnFocus?.isConnected&&this.returnFocus.focus(),this.returnFocus=null)}isOpen(){return this.backdrop!==null}setTemplates(e){if(this.content={...this.content,templates:[...e]},!this.listEl||this.view!==`templates`||this.query)return;let t=this.listEl.contains(document.activeElement);this.renderList(),t&&!this.listEl.contains(document.activeElement)&&this.listEl.querySelector(`[data-tpl-save]`)?.focus()}setCounts(e){this.counts=e,this.listEl?.querySelectorAll(`.tcw-ip-row[data-ind]`).forEach(e=>this.markCount(e))}destroy(){this.close(!1)}startView(){let e=this.views();return this.lastView&&e.includes(this.lastView)&&!(this.lastView===`favorites`&&this.favorites.size===0)?this.lastView:this.favorites.size>0?`favorites`:`all`}views(){let e=new Set(this.content.items.map(e=>e.category));return[`favorites`,...this.callbacks.templates&&this.content.templates?[`templates`]:[],`all`,...r.INDICATOR_CATEGORIES.filter(t=>e.has(t))]}viewLabel(e){return e===`favorites`?this.t(`indicatorPicker.favorites`):e===`templates`?this.t(`toolbar.indicators.templates`):e===`all`?this.t(`toolbar.indicators.all`):this.t(`indicatorCategory.${e}`)}build(){let e=document.createElement(`div`);e.className=`tcw-modal-backdrop`,e.addEventListener(`click`,t=>{t.target===e&&this.close()});let t=document.createElement(`div`);t.className=`tcw-modal tcw-ip`,t.setAttribute(`role`,`dialog`),t.setAttribute(`aria-modal`,`true`),t.setAttribute(`aria-labelledby`,`tcw-ip-title-${this.uid}`),t.tabIndex=-1,t.addEventListener(`keydown`,e=>this.onKeyDown(e,t));let n=document.createElement(`div`);n.className=`tcw-visually-hidden`,n.setAttribute(`aria-live`,`polite`),t.append(this.buildHeader(),this.buildSearch(),this.buildBody(),n),e.appendChild(t),this.layer().appendChild(e),this.backdrop=e,this.liveEl=n,this.renderNav(),this.renderList()}buildHeader(){let e=document.createElement(`div`);e.className=`tcw-modal-header`;let n=document.createElement(`h3`);n.id=`tcw-ip-title-${this.uid}`,n.textContent=this.t(`toolbar.indicators`);let r=document.createElement(`button`);return r.type=`button`,r.className=`tcw-modal-close`,r.setAttribute(`aria-label`,this.t(`indicatorSettings.close`)),r.innerHTML=t.i(`x`,16),r.addEventListener(`click`,()=>this.close()),e.append(n,r),e}buildSearch(){let e=document.createElement(`label`);e.className=`tcw-ip-search`;let n=document.createElement(`input`);return n.type=`search`,n.id=`tcw-ip-search-${this.uid}`,n.placeholder=this.t(`indicatorPicker.search`),n.setAttribute(`aria-label`,this.t(`indicatorPicker.search`)),n.autocomplete=`off`,n.spellcheck=!1,n.addEventListener(`input`,()=>{this.query=n.value,this.renderList()}),n.addEventListener(`keydown`,e=>this.onSearchKey(e)),e.innerHTML=t.i(`search`,16),e.appendChild(n),this.searchEl=n,e}buildBody(){let e=document.createElement(`div`);e.className=`tcw-ip-body`;let t=document.createElement(`div`);t.className=`tcw-ip-nav`,t.setAttribute(`role`,`tablist`),t.setAttribute(`aria-orientation`,`vertical`),t.setAttribute(`aria-label`,this.t(`indicatorPicker.groups`)),t.addEventListener(`keydown`,e=>this.onNavKey(e));let n=document.createElement(`div`);return n.className=`tcw-ip-list`,n.id=`tcw-ip-list-${this.uid}`,n.setAttribute(`role`,`tabpanel`),n.addEventListener(`keydown`,e=>this.onListKey(e)),n.addEventListener(`focusin`,e=>this.rove(e.target)),e.append(t,n),this.navEl=t,this.listEl=n,e}renderNav(){let n=this.navEl;n&&(n.replaceChildren(...this.views().map(n=>{let r=document.createElement(`button`);return r.type=`button`,r.className=`tcw-ip-tab`,r.id=`tcw-ip-tab-${this.uid}-${n}`,r.dataset.view=n,r.setAttribute(`role`,`tab`),r.setAttribute(`aria-controls`,`tcw-ip-list-${this.uid}`),r.innerHTML=`${n===`favorites`?t.i(`star`,14):n===`templates`?t.i(`layers`,14):``}<span></span>`,r.querySelector(`span`).textContent=this.viewLabel(n),n===`templates`&&e.o(r,`indicatorTemplates`),r.addEventListener(`click`,()=>this.select(n)),r})),this.markNav())}markNav(){this.navEl?.querySelectorAll(`.tcw-ip-tab`).forEach(e=>{let t=e.dataset.view===this.view;e.setAttribute(`aria-selected`,String(t)),e.classList.toggle(`tcw-active`,t),e.tabIndex=t?0:-1}),this.labelList()}labelList(){let e=this.query.trim()!==``;this.listEl?.setAttribute(`aria-labelledby`,e?`tcw-ip-search-${this.uid}`:`tcw-ip-tab-${this.uid}-${this.view}`)}select(e,t=!1){this.view=this.lastView=e,this.searchEl&&this.query&&(this.searchEl.value=this.query=``),this.markNav(),this.renderList(),t&&this.navEl?.querySelector(`[data-view="${e}"]`)?.focus()}renderList(){let e=this.listEl;if(!e)return;e.replaceChildren();let t=this.content.items;if(this.navEl?.classList.toggle(`tcw-ip-nav--searching`,this.query.trim()!==``),this.labelList(),this.query.trim()){let r=s(t,this.query);r.length===0?e.appendChild(this.note(n.r(this.t(`indicatorPicker.noMatch`),{query:this.query.trim()}))):e.append(...r.map(e=>this.row(e)))}else if(this.view===`templates`)this.renderTemplates(e);else if(this.view===`favorites`){let n=t.filter(e=>this.favorites.has(e.id)).sort(l);n.length===0?e.appendChild(this.note(this.t(`indicatorPicker.noFavorites`))):e.append(...n.map(e=>this.row(e)))}else if(this.view===`all`)for(let n of r.INDICATOR_CATEGORIES){let r=t.filter(e=>e.category===n).sort(l);if(r.length===0)continue;let i=document.createElement(`div`);i.className=`tcw-ip-heading`,i.textContent=this.viewLabel(n),e.append(i,...r.map(e=>this.row(e)))}else e.append(...t.filter(e=>e.category===this.view).sort(l).map(e=>this.row(e)));e.querySelector(`.tcw-ip-add`)?.setAttribute(`tabindex`,`0`)}row(e){let n=document.createElement(`div`);n.className=`tcw-ip-row`,n.dataset.ind=e.id;let r=document.createElement(`button`);r.type=`button`,r.className=`tcw-ip-add`,r.dataset.ind=e.id,r.tabIndex=-1;let i=document.createElement(`span`);i.className=`tcw-ip-name`,i.textContent=e.name,r.appendChild(i),e.shortName&&e.shortName!==e.name&&r.appendChild(this.tag(`tcw-ip-short`,e.shortName)),r.appendChild(this.tag(`tcw-ip-kind`,this.t(`indicatorType.${e.placement}`))),r.addEventListener(`click`,()=>this.add(e));let a=document.createElement(`button`);return a.type=`button`,a.className=`tcw-ip-aside tcw-ip-star`,a.tabIndex=-1,a.title=this.t(`indicatorPicker.favorite`),a.setAttribute(`aria-label`,`${this.t(`indicatorPicker.favorite`)}: ${e.name}`),a.setAttribute(`aria-pressed`,String(this.favorites.has(e.id))),a.innerHTML=t.i(`star`,14),a.addEventListener(`click`,()=>this.toggleFavorite(e.id,a)),n.append(r,a),this.markCount(n),n}renderTemplates(e){let r=document.createElement(`button`);r.type=`button`,r.className=`tcw-ip-add tcw-ip-save`,r.tabIndex=-1,r.dataset.tplSave=``,r.innerHTML=`${t.i(`save`,14)}<span class="tcw-ip-name"></span>`,r.querySelector(`span`).textContent=this.t(`templates.save`),r.addEventListener(`click`,()=>{this.close(),this.callbacks.templates?.onSave()}),e.appendChild(r);let i=this.content.templates??[];i.length===0&&e.appendChild(this.note(this.t(`indicatorPicker.noTemplates`)));for(let r of i){let i=document.createElement(`div`);i.className=`tcw-ip-row`;let a=document.createElement(`button`);a.type=`button`,a.className=`tcw-ip-add`,a.tabIndex=-1,a.dataset.tplApply=r,a.innerHTML=`${t.i(`layers`,14)}<span class="tcw-ip-name"></span>`,a.querySelector(`span`).textContent=r,a.addEventListener(`click`,()=>{this.close(),this.callbacks.templates?.onApply(r)});let o=document.createElement(`button`);o.type=`button`,o.className=`tcw-ip-aside tcw-ip-remove`,o.tabIndex=-1,o.dataset.tplDelete=r;let s=n.r(this.t(`templates.delete`),{name:r});o.title=s,o.setAttribute(`aria-label`,s),o.innerHTML=t.i(`x`,12),o.addEventListener(`click`,()=>this.callbacks.templates?.onDelete(r)),i.append(a,o),e.appendChild(i)}}add(e){if(this.callbacks.onAdd(e.id)===!1||!this.liveEl)return;let t=n.r(this.t(`indicatorPicker.added`),{name:e.name});this.liveEl.textContent=this.liveEl.textContent===t?`${t}\u00a0`:t}toggleFavorite(e,t){let n=!this.favorites.has(e);if(n?this.favorites.add(e):this.favorites.delete(e),this.callbacks.onFavorite(e,n),t.setAttribute(`aria-pressed`,String(n)),this.view!==`favorites`||this.query||n)return;let r=[...this.listEl?.querySelectorAll(`.tcw-ip-star`)??[]].indexOf(t),i=document.activeElement===t;if(this.renderList(),!i)return;let a=[...this.listEl?.querySelectorAll(`.tcw-ip-star`)??[]];(a[r]??a[a.length-1]??this.searchEl)?.focus()}markCount(e){let t=this.counts.get(e.dataset.ind??``)??0,r=e.querySelector(`.tcw-ip-add`),i=e.querySelector(`.tcw-ip-count`);if(t===0){i?.remove();return}i||(i=this.tag(`tcw-ip-count`,``),r?.appendChild(i)),i.textContent=String(t),i.title=n.r(this.t(`indicatorPicker.onChart`),{count:String(t)})}tag(e,t){let n=document.createElement(`span`);return n.className=e,n.textContent=t,n}note(e){let t=document.createElement(`div`);return t.className=`tcw-ip-empty`,t.textContent=e,t}rove(e){!(e instanceof HTMLElement)||!e.classList.contains(`tcw-ip-add`)||(this.listEl?.querySelectorAll(`.tcw-ip-add[tabindex="0"]`).forEach(t=>{t!==e&&(t.tabIndex=-1)}),e.tabIndex=0)}onKeyDown(e,n){if(e.key===`Escape`){e.preventDefault(),e.stopPropagation(),this.close();return}t.n(e,n)}onSearchKey(e){let t=this.listEl?.querySelector(`.tcw-ip-add`);e.key===`ArrowDown`&&t?(e.preventDefault(),t.focus()):e.key===`Enter`&&this.query.trim()&&t&&(e.preventDefault(),t.click())}onNavKey(e){let t=this.views(),n=t.indexOf(this.view),r=e.key===`ArrowDown`?Math.min(t.length-1,n+1):e.key===`ArrowUp`?Math.max(0,n-1):e.key===`Home`?0:e.key===`End`?t.length-1:-1;r<0||(e.preventDefault(),this.select(t[r],!0))}onListKey(e){let t=e.target,n=t.closest(`.tcw-ip-row, .tcw-ip-save`),r=[...this.listEl?.querySelectorAll(`.tcw-ip-add`)??[]],i=n?.classList.contains(`tcw-ip-save`)?n:n?.querySelector(`.tcw-ip-add`),a=i?r.indexOf(i):-1,o=null;e.key===`ArrowDown`?o=r[a+1]:e.key===`ArrowUp`?o=a>0?r[a-1]:this.searchEl:e.key===`Home`?o=r[0]:e.key===`End`?o=r[r.length-1]:e.key===`ArrowRight`&&t===i?o=n?.querySelector(`.tcw-ip-aside`):e.key===`ArrowLeft`&&t!==i&&(o=i),o&&(e.preventDefault(),o.focus())}};exports.WidgetIndicatorPicker=u;
2
- //# sourceMappingURL=WidgetIndicatorPicker-DJQ5taqX.cjs.map
1
+ const e=require(`./widgetFeatures-DpBWfQ8D.cjs`),t=require(`./focusTrap-DrhhmG0V.cjs`),n=require(`./i18n-fgAwciRT.cjs`);let r=require(`@tradecanvas/core`);function i(e){return e.normalize(`NFD`).replace(/[̀-ͯ]/g,``).toLowerCase()}var a=/[\s/()%-]+/;function o(e,t){let n=i(e.shortName),r=i(e.name);return n===t||i(e.id)===t?0:n.startsWith(t)||r.startsWith(t)?1:[...r.split(a),...n.split(a)].some(e=>e.startsWith(t))?2:r.includes(t)||n.includes(t)?3:null}function s(e,t){let n=i(t.trim());return n?e.map(e=>({item:e,rank:o(e,n)})).filter(e=>e.rank!==null).sort((e,t)=>e.rank-t.rank||e.item.name.localeCompare(t.item.name)).map(e=>e.item):[]}var c=1,l=(e,t)=>e.name.localeCompare(t.name),u=class{backdrop=null;listEl=null;navEl=null;searchEl=null;liveEl=null;content={items:[],favorites:[],templates:[]};favorites=new Set;counts=new Map;view=`all`;lastView=null;query=``;returnFocus=null;uid=c++;constructor(e,t,n){this.layer=e,this.callbacks=t,this.t=n}open(e){this.backdrop&&this.close(!1),this.returnFocus=document.activeElement instanceof HTMLElement?document.activeElement:null,this.content=e,this.favorites=new Set(e.favorites),this.counts=e.counts??new Map,this.query=``,this.view=this.startView(),this.build(),this.searchEl?.focus()}close(e=!0){this.backdrop&&(this.backdrop.remove(),this.backdrop=this.listEl=this.navEl=this.searchEl=this.liveEl=null,this.callbacks.onClose?.(),e&&this.returnFocus?.isConnected&&this.returnFocus.focus(),this.returnFocus=null)}isOpen(){return this.backdrop!==null}setTemplates(e){if(this.content={...this.content,templates:[...e]},!this.listEl||this.view!==`templates`||this.query)return;let t=this.listEl.contains(document.activeElement);this.renderList(),t&&!this.listEl.contains(document.activeElement)&&this.listEl.querySelector(`[data-tpl-save]`)?.focus()}setCounts(e){this.counts=e,this.listEl?.querySelectorAll(`.tcw-ip-row[data-ind]`).forEach(e=>this.markCount(e))}destroy(){this.close(!1)}startView(){let e=this.views();return this.lastView&&e.includes(this.lastView)&&!(this.lastView===`favorites`&&this.favorites.size===0)?this.lastView:this.favorites.size>0?`favorites`:`all`}views(){let e=new Set(this.content.items.map(e=>e.category));return[`favorites`,...this.callbacks.templates&&this.content.templates?[`templates`]:[],`all`,...r.INDICATOR_CATEGORIES.filter(t=>e.has(t))]}viewLabel(e){return e===`favorites`?this.t(`indicatorPicker.favorites`):e===`templates`?this.t(`toolbar.indicators.templates`):e===`all`?this.t(`toolbar.indicators.all`):this.t(`indicatorCategory.${e}`)}build(){let e=document.createElement(`div`);e.className=`tcw-modal-backdrop`,e.addEventListener(`click`,t=>{t.target===e&&this.close()});let t=document.createElement(`div`);t.className=`tcw-modal tcw-ip`,t.setAttribute(`role`,`dialog`),t.setAttribute(`aria-modal`,`true`),t.setAttribute(`aria-labelledby`,`tcw-ip-title-${this.uid}`),t.tabIndex=-1,t.addEventListener(`keydown`,e=>this.onKeyDown(e,t));let n=document.createElement(`div`);n.className=`tcw-visually-hidden`,n.setAttribute(`aria-live`,`polite`),t.append(this.buildHeader(),this.buildSearch(),this.buildBody(),n),e.appendChild(t),this.layer().appendChild(e),this.backdrop=e,this.liveEl=n,this.renderNav(),this.renderList()}buildHeader(){let e=document.createElement(`div`);e.className=`tcw-modal-header`;let n=document.createElement(`h3`);n.id=`tcw-ip-title-${this.uid}`,n.textContent=this.t(`toolbar.indicators`);let r=document.createElement(`button`);return r.type=`button`,r.className=`tcw-modal-close`,r.setAttribute(`aria-label`,this.t(`indicatorSettings.close`)),r.innerHTML=t.i(`x`,16),r.addEventListener(`click`,()=>this.close()),e.append(n,r),e}buildSearch(){let e=document.createElement(`label`);e.className=`tcw-ip-search`;let n=document.createElement(`input`);return n.type=`search`,n.id=`tcw-ip-search-${this.uid}`,n.placeholder=this.t(`indicatorPicker.search`),n.setAttribute(`aria-label`,this.t(`indicatorPicker.search`)),n.autocomplete=`off`,n.spellcheck=!1,n.addEventListener(`input`,()=>{this.query=n.value,this.renderList()}),n.addEventListener(`keydown`,e=>this.onSearchKey(e)),e.innerHTML=t.i(`search`,16),e.appendChild(n),this.searchEl=n,e}buildBody(){let e=document.createElement(`div`);e.className=`tcw-ip-body`;let t=document.createElement(`div`);t.className=`tcw-ip-nav`,t.setAttribute(`role`,`tablist`),t.setAttribute(`aria-orientation`,`vertical`),t.setAttribute(`aria-label`,this.t(`indicatorPicker.groups`)),t.addEventListener(`keydown`,e=>this.onNavKey(e));let n=document.createElement(`div`);return n.className=`tcw-ip-list`,n.id=`tcw-ip-list-${this.uid}`,n.setAttribute(`role`,`tabpanel`),n.addEventListener(`keydown`,e=>this.onListKey(e)),n.addEventListener(`focusin`,e=>this.rove(e.target)),e.append(t,n),this.navEl=t,this.listEl=n,e}renderNav(){let n=this.navEl;n&&(n.replaceChildren(...this.views().map(n=>{let r=document.createElement(`button`);return r.type=`button`,r.className=`tcw-ip-tab`,r.id=`tcw-ip-tab-${this.uid}-${n}`,r.dataset.view=n,r.setAttribute(`role`,`tab`),r.setAttribute(`aria-controls`,`tcw-ip-list-${this.uid}`),r.innerHTML=`${n===`favorites`?t.i(`star`,14):n===`templates`?t.i(`layers`,14):``}<span></span>`,r.querySelector(`span`).textContent=this.viewLabel(n),n===`templates`&&e.o(r,`indicatorTemplates`),r.addEventListener(`click`,()=>this.select(n)),r})),this.markNav())}markNav(){this.navEl?.querySelectorAll(`.tcw-ip-tab`).forEach(e=>{let t=e.dataset.view===this.view;e.setAttribute(`aria-selected`,String(t)),e.classList.toggle(`tcw-active`,t),e.tabIndex=t?0:-1}),this.labelList()}labelList(){let e=this.query.trim()!==``;this.listEl?.setAttribute(`aria-labelledby`,e?`tcw-ip-search-${this.uid}`:`tcw-ip-tab-${this.uid}-${this.view}`)}select(e,t=!1){this.view=this.lastView=e,this.searchEl&&this.query&&(this.searchEl.value=this.query=``),this.markNav(),this.renderList(),t&&this.navEl?.querySelector(`[data-view="${e}"]`)?.focus()}renderList(){let e=this.listEl;if(!e)return;e.replaceChildren();let t=this.content.items;if(this.navEl?.classList.toggle(`tcw-ip-nav--searching`,this.query.trim()!==``),this.labelList(),this.query.trim()){let r=s(t,this.query);r.length===0?e.appendChild(this.note(n.r(this.t(`indicatorPicker.noMatch`),{query:this.query.trim()}))):e.append(...r.map(e=>this.row(e)))}else if(this.view===`templates`)this.renderTemplates(e);else if(this.view===`favorites`){let n=t.filter(e=>this.favorites.has(e.id)).sort(l);n.length===0?e.appendChild(this.note(this.t(`indicatorPicker.noFavorites`))):e.append(...n.map(e=>this.row(e)))}else if(this.view===`all`)for(let n of r.INDICATOR_CATEGORIES){let r=t.filter(e=>e.category===n).sort(l);if(r.length===0)continue;let i=document.createElement(`div`);i.className=`tcw-ip-heading`,i.textContent=this.viewLabel(n),e.append(i,...r.map(e=>this.row(e)))}else e.append(...t.filter(e=>e.category===this.view).sort(l).map(e=>this.row(e)));e.querySelector(`.tcw-ip-add`)?.setAttribute(`tabindex`,`0`)}row(e){let n=document.createElement(`div`);n.className=`tcw-ip-row`,n.dataset.ind=e.id;let r=document.createElement(`button`);r.type=`button`,r.className=`tcw-ip-add`,r.dataset.ind=e.id,r.tabIndex=-1;let i=document.createElement(`span`);i.className=`tcw-ip-name`,i.textContent=e.name,r.appendChild(i),e.shortName&&e.shortName!==e.name&&r.appendChild(this.tag(`tcw-ip-short`,e.shortName)),r.appendChild(this.tag(`tcw-ip-kind`,this.t(`indicatorType.${e.placement}`))),r.addEventListener(`click`,()=>this.add(e));let a=document.createElement(`button`);return a.type=`button`,a.className=`tcw-ip-aside tcw-ip-star`,a.tabIndex=-1,a.title=this.t(`indicatorPicker.favorite`),a.setAttribute(`aria-label`,`${this.t(`indicatorPicker.favorite`)}: ${e.name}`),a.setAttribute(`aria-pressed`,String(this.favorites.has(e.id))),a.innerHTML=t.i(`star`,14),a.addEventListener(`click`,()=>this.toggleFavorite(e.id,a)),n.append(r,a),this.markCount(n),n}renderTemplates(e){let r=document.createElement(`button`);r.type=`button`,r.className=`tcw-ip-add tcw-ip-save`,r.tabIndex=-1,r.dataset.tplSave=``,r.innerHTML=`${t.i(`save`,14)}<span class="tcw-ip-name"></span>`,r.querySelector(`span`).textContent=this.t(`templates.save`),r.addEventListener(`click`,()=>{this.close(),this.callbacks.templates?.onSave()}),e.appendChild(r);let i=this.content.templates??[];i.length===0&&e.appendChild(this.note(this.t(`indicatorPicker.noTemplates`)));for(let r of i){let i=document.createElement(`div`);i.className=`tcw-ip-row`;let a=document.createElement(`button`);a.type=`button`,a.className=`tcw-ip-add`,a.tabIndex=-1,a.dataset.tplApply=r,a.innerHTML=`${t.i(`layers`,14)}<span class="tcw-ip-name"></span>`,a.querySelector(`span`).textContent=r,a.addEventListener(`click`,()=>{this.close(),this.callbacks.templates?.onApply(r)});let o=document.createElement(`button`);o.type=`button`,o.className=`tcw-ip-aside tcw-ip-remove`,o.tabIndex=-1,o.dataset.tplDelete=r;let s=n.r(this.t(`templates.delete`),{name:r});o.title=s,o.setAttribute(`aria-label`,s),o.innerHTML=t.i(`x`,12),o.addEventListener(`click`,()=>this.callbacks.templates?.onDelete(r)),i.append(a,o),e.appendChild(i)}}add(e){if(this.callbacks.onAdd(e.id)===!1||!this.liveEl)return;let t=n.r(this.t(`indicatorPicker.added`),{name:e.name});this.liveEl.textContent=this.liveEl.textContent===t?`${t}\u00a0`:t}toggleFavorite(e,t){let n=!this.favorites.has(e);if(n?this.favorites.add(e):this.favorites.delete(e),this.callbacks.onFavorite(e,n),t.setAttribute(`aria-pressed`,String(n)),this.view!==`favorites`||this.query||n)return;let r=[...this.listEl?.querySelectorAll(`.tcw-ip-star`)??[]].indexOf(t),i=document.activeElement===t;if(this.renderList(),!i)return;let a=[...this.listEl?.querySelectorAll(`.tcw-ip-star`)??[]];(a[r]??a[a.length-1]??this.searchEl)?.focus()}markCount(e){let t=this.counts.get(e.dataset.ind??``)??0,r=e.querySelector(`.tcw-ip-add`),i=e.querySelector(`.tcw-ip-count`);if(t===0){i?.remove();return}i||(i=this.tag(`tcw-ip-count`,``),r?.appendChild(i)),i.textContent=String(t),i.title=n.r(this.t(`indicatorPicker.onChart`),{count:String(t)})}tag(e,t){let n=document.createElement(`span`);return n.className=e,n.textContent=t,n}note(e){let t=document.createElement(`div`);return t.className=`tcw-ip-empty`,t.textContent=e,t}rove(e){!(e instanceof HTMLElement)||!e.classList.contains(`tcw-ip-add`)||(this.listEl?.querySelectorAll(`.tcw-ip-add[tabindex="0"]`).forEach(t=>{t!==e&&(t.tabIndex=-1)}),e.tabIndex=0)}onKeyDown(e,n){if(e.key===`Escape`){e.preventDefault(),e.stopPropagation(),this.close();return}t.n(e,n)}onSearchKey(e){let t=this.listEl?.querySelector(`.tcw-ip-add`);e.key===`ArrowDown`&&t?(e.preventDefault(),t.focus()):e.key===`Enter`&&this.query.trim()&&t&&(e.preventDefault(),t.click())}onNavKey(e){let t=this.views(),n=t.indexOf(this.view),r=e.key===`ArrowDown`?Math.min(t.length-1,n+1):e.key===`ArrowUp`?Math.max(0,n-1):e.key===`Home`?0:e.key===`End`?t.length-1:-1;r<0||(e.preventDefault(),this.select(t[r],!0))}onListKey(e){let t=e.target,n=t.closest(`.tcw-ip-row, .tcw-ip-save`),r=[...this.listEl?.querySelectorAll(`.tcw-ip-add`)??[]],i=n?.classList.contains(`tcw-ip-save`)?n:n?.querySelector(`.tcw-ip-add`),a=i?r.indexOf(i):-1,o=null;e.key===`ArrowDown`?o=r[a+1]:e.key===`ArrowUp`?o=a>0?r[a-1]:this.searchEl:e.key===`Home`?o=r[0]:e.key===`End`?o=r[r.length-1]:e.key===`ArrowRight`&&t===i?o=n?.querySelector(`.tcw-ip-aside`):e.key===`ArrowLeft`&&t!==i&&(o=i),o&&(e.preventDefault(),o.focus())}};exports.WidgetIndicatorPicker=u;
2
+ //# sourceMappingURL=WidgetIndicatorPicker-DKxoB4Un.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"WidgetIndicatorPicker-DJQ5taqX.cjs","names":[],"sources":["../src/widget/indicatorSearch.ts","../src/widget/WidgetIndicatorPicker.ts"],"sourcesContent":["/** What a search reads of an indicator. */\nexport interface SearchableIndicator {\n id: string;\n name: string;\n shortName: string;\n}\n\n/** Lower case without accents, so \"mômentum\" finds Momentum. */\nfunction fold(text: string): string {\n return text.normalize('NFD').replace(/[̀-ͯ]/g, '').toLowerCase();\n}\n\nconst WORD_BREAK = /[\\s/()%-]+/;\n\n/** How well an indicator matches `q` (folded): 0 best, null not at all. */\nfunction score(item: SearchableIndicator, q: string): number | null {\n const short = fold(item.shortName);\n const name = fold(item.name);\n if (short === q || fold(item.id) === q) return 0;\n if (short.startsWith(q) || name.startsWith(q)) return 1;\n if ([...name.split(WORD_BREAK), ...short.split(WORD_BREAK)].some((word) => word.startsWith(q))) return 2;\n if (name.includes(q) || short.includes(q)) return 3;\n return null;\n}\n\n/**\n * The indicators matching `query`, best first: the short name typed in full\n * (\"rsi\"), then names starting with it, then a word starting with it, then\n * any containing it. Ties go by name. Nothing typed finds nothing.\n */\nexport function searchIndicators<T extends SearchableIndicator>(items: readonly T[], query: string): T[] {\n const q = fold(query.trim());\n if (!q) return [];\n return items\n .map((item) => ({ item, rank: score(item, q) }))\n .filter((hit): hit is { item: T; rank: number } => hit.rank !== null)\n .sort((a, b) => a.rank - b.rank || a.item.name.localeCompare(b.item.name))\n .map((hit) => hit.item);\n}\n","import type { IndicatorCategory } from '@tradecanvas/commons';\nimport { INDICATOR_CATEGORIES } from '@tradecanvas/core';\nimport { createIcon } from './icons.js';\nimport { fill, type MessageKey, type Translator } from './i18n.js';\nimport { keepTabInside } from './focusTrap.js';\nimport { searchIndicators } from './indicatorSearch.js';\nimport { markPart } from './widgetFeatures.js';\n\n/** An indicator as the dialog lists it. */\nexport interface IndicatorPickerItem {\n id: string;\n name: string;\n shortName: string;\n placement: 'overlay' | 'panel';\n category: IndicatorCategory;\n}\n\nexport interface IndicatorPickerCallbacks {\n /** Add one; false when it wasn't added (nothing is announced then). */\n onAdd: (id: string) => boolean | void;\n onFavorite: (id: string, on: boolean) => void;\n /** The indicator templates; without them there is no Templates tab. */\n templates?: {\n onApply: (name: string) => void;\n onDelete: (name: string) => void;\n onSave: () => void;\n };\n onClose?: () => void;\n}\n\nexport interface IndicatorPickerContent {\n items: readonly IndicatorPickerItem[];\n favorites: readonly string[];\n /** The templates' names; null where they are switched off. */\n templates: readonly string[] | null;\n /** How many of each indicator are on the chart. */\n counts?: ReadonlyMap<string, number>;\n}\n\ntype View = 'favorites' | 'templates' | 'all' | IndicatorCategory;\n\nlet nextPickerId = 1;\n\nconst byName = (a: IndicatorPickerItem, b: IndicatorPickerItem) => a.name.localeCompare(b.name);\n\n/**\n * The indicators dialog: a search, the groups down the side (favourites,\n * templates, all, then each kind) and the list. A row adds its indicator and\n * the dialog stays open for the next; the star beside it keeps it in the\n * favourites. Mounts in the widget's overlay layer.\n */\nexport class WidgetIndicatorPicker {\n private backdrop: HTMLDivElement | null = null;\n private listEl: HTMLDivElement | null = null;\n private navEl: HTMLDivElement | null = null;\n private searchEl: HTMLInputElement | null = null;\n private liveEl: HTMLDivElement | null = null;\n private content: IndicatorPickerContent = { items: [], favorites: [], templates: [] };\n private favorites = new Set<string>();\n private counts: ReadonlyMap<string, number> = new Map();\n private view: View = 'all';\n /** The tab picked last, for the next opening. */\n private lastView: View | null = null;\n private query = '';\n private returnFocus: HTMLElement | null = null;\n private readonly uid = nextPickerId++;\n\n constructor(\n private readonly layer: () => HTMLElement,\n private readonly callbacks: IndicatorPickerCallbacks,\n private readonly t: Translator,\n ) {}\n\n open(content: IndicatorPickerContent): void {\n if (this.backdrop) this.close(false);\n this.returnFocus = document.activeElement instanceof HTMLElement ? document.activeElement : null;\n this.content = content;\n this.favorites = new Set(content.favorites);\n this.counts = content.counts ?? new Map();\n this.query = '';\n this.view = this.startView();\n this.build();\n this.searchEl?.focus();\n }\n\n close(giveFocusBack = true): void {\n if (!this.backdrop) return;\n this.backdrop.remove();\n this.backdrop = this.listEl = this.navEl = this.searchEl = this.liveEl = null;\n this.callbacks.onClose?.();\n if (giveFocusBack && this.returnFocus?.isConnected) this.returnFocus.focus();\n this.returnFocus = null;\n }\n\n isOpen(): boolean {\n return this.backdrop !== null;\n }\n\n /** The templates after one is saved or deleted; focus left on a deleted one goes to \"Save…\". */\n setTemplates(names: readonly string[]): void {\n this.content = { ...this.content, templates: [...names] };\n if (!this.listEl || this.view !== 'templates' || this.query) return;\n const hadFocus = this.listEl.contains(document.activeElement);\n this.renderList();\n if (hadFocus && !this.listEl.contains(document.activeElement)) {\n this.listEl.querySelector<HTMLButtonElement>('[data-tpl-save]')?.focus();\n }\n }\n\n /** How many of each indicator are on the chart, shown on their rows. */\n setCounts(counts: ReadonlyMap<string, number>): void {\n this.counts = counts;\n this.listEl?.querySelectorAll<HTMLElement>('.tcw-ip-row[data-ind]').forEach((row) => this.markCount(row));\n }\n\n destroy(): void {\n this.close(false);\n }\n\n private startView(): View {\n const views = this.views();\n if (this.lastView && views.includes(this.lastView) && !(this.lastView === 'favorites' && this.favorites.size === 0)) return this.lastView;\n return this.favorites.size > 0 ? 'favorites' : 'all';\n }\n\n /** The tabs down the side: favourites, templates, all, and each kind that has indicators. */\n private views(): View[] {\n const kinds = new Set(this.content.items.map((item) => item.category));\n return [\n 'favorites',\n ...(this.callbacks.templates && this.content.templates ? ['templates' as const] : []),\n 'all',\n ...INDICATOR_CATEGORIES.filter((category) => kinds.has(category)),\n ];\n }\n\n private viewLabel(view: View): string {\n if (view === 'favorites') return this.t('indicatorPicker.favorites');\n if (view === 'templates') return this.t('toolbar.indicators.templates');\n if (view === 'all') return this.t('toolbar.indicators.all');\n return this.t(`indicatorCategory.${view}` as MessageKey);\n }\n\n private build(): void {\n const backdrop = document.createElement('div');\n backdrop.className = 'tcw-modal-backdrop';\n backdrop.addEventListener('click', (e) => {\n if (e.target === backdrop) this.close();\n });\n const modal = document.createElement('div');\n modal.className = 'tcw-modal tcw-ip';\n modal.setAttribute('role', 'dialog');\n modal.setAttribute('aria-modal', 'true');\n modal.setAttribute('aria-labelledby', `tcw-ip-title-${this.uid}`);\n modal.tabIndex = -1;\n modal.addEventListener('keydown', (e) => this.onKeyDown(e, modal));\n\n const live = document.createElement('div');\n live.className = 'tcw-visually-hidden';\n live.setAttribute('aria-live', 'polite');\n modal.append(this.buildHeader(), this.buildSearch(), this.buildBody(), live);\n backdrop.appendChild(modal);\n this.layer().appendChild(backdrop);\n this.backdrop = backdrop;\n this.liveEl = live;\n this.renderNav();\n this.renderList();\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-ip-title-${this.uid}`;\n title.textContent = this.t('toolbar.indicators');\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.close());\n header.append(title, closeBtn);\n return header;\n }\n\n private buildSearch(): HTMLLabelElement {\n const row = document.createElement('label');\n row.className = 'tcw-ip-search';\n const search = document.createElement('input');\n search.type = 'search';\n search.id = `tcw-ip-search-${this.uid}`;\n search.placeholder = this.t('indicatorPicker.search');\n search.setAttribute('aria-label', this.t('indicatorPicker.search'));\n search.autocomplete = 'off';\n search.spellcheck = false;\n search.addEventListener('input', () => {\n this.query = search.value;\n this.renderList();\n });\n search.addEventListener('keydown', (e) => this.onSearchKey(e));\n row.innerHTML = createIcon('search', 16);\n row.appendChild(search);\n this.searchEl = search;\n return row;\n }\n\n /** The groups down the side, and the list beside them. */\n private buildBody(): HTMLDivElement {\n const body = document.createElement('div');\n body.className = 'tcw-ip-body';\n const nav = document.createElement('div');\n nav.className = 'tcw-ip-nav';\n nav.setAttribute('role', 'tablist');\n nav.setAttribute('aria-orientation', 'vertical');\n nav.setAttribute('aria-label', this.t('indicatorPicker.groups'));\n nav.addEventListener('keydown', (e) => this.onNavKey(e));\n const list = document.createElement('div');\n list.className = 'tcw-ip-list';\n list.id = `tcw-ip-list-${this.uid}`;\n list.setAttribute('role', 'tabpanel');\n list.addEventListener('keydown', (e) => this.onListKey(e));\n list.addEventListener('focusin', (e) => this.rove(e.target));\n body.append(nav, list);\n this.navEl = nav;\n this.listEl = list;\n return body;\n }\n\n private renderNav(): void {\n const nav = this.navEl;\n if (!nav) return;\n nav.replaceChildren(...this.views().map((view) => {\n const tab = document.createElement('button');\n tab.type = 'button';\n tab.className = 'tcw-ip-tab';\n tab.id = `tcw-ip-tab-${this.uid}-${view}`;\n tab.dataset.view = view;\n tab.setAttribute('role', 'tab');\n tab.setAttribute('aria-controls', `tcw-ip-list-${this.uid}`);\n const icon = view === 'favorites' ? createIcon('star', 14) : view === 'templates' ? createIcon('layers', 14) : '';\n tab.innerHTML = `${icon}<span></span>`;\n tab.querySelector('span')!.textContent = this.viewLabel(view);\n if (view === 'templates') markPart(tab, 'indicatorTemplates');\n tab.addEventListener('click', () => this.select(view));\n return tab;\n }));\n this.markNav();\n }\n\n private markNav(): void {\n this.navEl?.querySelectorAll<HTMLButtonElement>('.tcw-ip-tab').forEach((tab) => {\n const on = tab.dataset.view === this.view;\n tab.setAttribute('aria-selected', String(on));\n tab.classList.toggle('tcw-active', on);\n tab.tabIndex = on ? 0 : -1;\n });\n this.labelList();\n }\n\n /** The list is named by its tab, or by the search while it shows the matches. */\n private labelList(): void {\n const searching = this.query.trim() !== '';\n this.listEl?.setAttribute('aria-labelledby', searching ? `tcw-ip-search-${this.uid}` : `tcw-ip-tab-${this.uid}-${this.view}`);\n }\n\n private select(view: View, focusTab = false): void {\n this.view = this.lastView = view;\n if (this.searchEl && this.query) this.searchEl.value = this.query = '';\n this.markNav();\n this.renderList();\n if (focusTab) this.navEl?.querySelector<HTMLButtonElement>(`[data-view=\"${view}\"]`)?.focus();\n }\n\n private renderList(): void {\n const list = this.listEl;\n if (!list) return;\n list.replaceChildren();\n const items = this.content.items;\n // While searching, the results come from every group: the one picked steps back.\n this.navEl?.classList.toggle('tcw-ip-nav--searching', this.query.trim() !== '');\n this.labelList();\n if (this.query.trim()) {\n const hits = searchIndicators(items, this.query);\n if (hits.length === 0) list.appendChild(this.note(fill(this.t('indicatorPicker.noMatch'), { query: this.query.trim() })));\n else list.append(...hits.map((item) => this.row(item)));\n } else if (this.view === 'templates') {\n this.renderTemplates(list);\n } else if (this.view === 'favorites') {\n const favorites = items.filter((item) => this.favorites.has(item.id)).sort(byName);\n if (favorites.length === 0) list.appendChild(this.note(this.t('indicatorPicker.noFavorites')));\n else list.append(...favorites.map((item) => this.row(item)));\n } else if (this.view === 'all') {\n for (const category of INDICATOR_CATEGORIES) {\n const group = items.filter((item) => item.category === category).sort(byName);\n if (group.length === 0) continue;\n const heading = document.createElement('div');\n heading.className = 'tcw-ip-heading';\n heading.textContent = this.viewLabel(category);\n list.append(heading, ...group.map((item) => this.row(item)));\n }\n } else {\n list.append(...items.filter((item) => item.category === this.view).sort(byName).map((item) => this.row(item)));\n }\n list.querySelector<HTMLButtonElement>('.tcw-ip-add')?.setAttribute('tabindex', '0');\n }\n\n private row(item: IndicatorPickerItem): HTMLDivElement {\n const row = document.createElement('div');\n row.className = 'tcw-ip-row';\n row.dataset.ind = item.id;\n const add = document.createElement('button');\n add.type = 'button';\n add.className = 'tcw-ip-add';\n add.dataset.ind = item.id;\n add.tabIndex = -1;\n const name = document.createElement('span');\n name.className = 'tcw-ip-name';\n name.textContent = item.name;\n add.appendChild(name);\n if (item.shortName && item.shortName !== item.name) add.appendChild(this.tag('tcw-ip-short', item.shortName));\n add.appendChild(this.tag('tcw-ip-kind', this.t(`indicatorType.${item.placement}`)));\n add.addEventListener('click', () => this.add(item));\n const star = document.createElement('button');\n star.type = 'button';\n star.className = 'tcw-ip-aside tcw-ip-star';\n star.tabIndex = -1;\n star.title = this.t('indicatorPicker.favorite');\n star.setAttribute('aria-label', `${this.t('indicatorPicker.favorite')}: ${item.name}`);\n star.setAttribute('aria-pressed', String(this.favorites.has(item.id)));\n star.innerHTML = createIcon('star', 14);\n star.addEventListener('click', () => this.toggleFavorite(item.id, star));\n row.append(add, star);\n this.markCount(row);\n return row;\n }\n\n private renderTemplates(list: HTMLElement): void {\n const save = document.createElement('button');\n save.type = 'button';\n save.className = 'tcw-ip-add tcw-ip-save';\n save.tabIndex = -1;\n save.dataset.tplSave = '';\n save.innerHTML = `${createIcon('save', 14)}<span class=\"tcw-ip-name\"></span>`;\n save.querySelector('span')!.textContent = this.t('templates.save');\n save.addEventListener('click', () => {\n this.close();\n this.callbacks.templates?.onSave();\n });\n list.appendChild(save);\n const names = this.content.templates ?? [];\n if (names.length === 0) list.appendChild(this.note(this.t('indicatorPicker.noTemplates')));\n for (const name of names) {\n const row = document.createElement('div');\n row.className = 'tcw-ip-row';\n const apply = document.createElement('button');\n apply.type = 'button';\n apply.className = 'tcw-ip-add';\n apply.tabIndex = -1;\n apply.dataset.tplApply = name;\n apply.innerHTML = `${createIcon('layers', 14)}<span class=\"tcw-ip-name\"></span>`;\n apply.querySelector('span')!.textContent = name;\n apply.addEventListener('click', () => {\n this.close();\n this.callbacks.templates?.onApply(name);\n });\n const remove = document.createElement('button');\n remove.type = 'button';\n remove.className = 'tcw-ip-aside tcw-ip-remove';\n remove.tabIndex = -1;\n remove.dataset.tplDelete = name;\n const label = fill(this.t('templates.delete'), { name });\n remove.title = label;\n remove.setAttribute('aria-label', label);\n remove.innerHTML = createIcon('x', 12);\n remove.addEventListener('click', () => this.callbacks.templates?.onDelete(name));\n row.append(apply, remove);\n list.appendChild(row);\n }\n }\n\n private add(item: IndicatorPickerItem): void {\n if (this.callbacks.onAdd(item.id) === false || !this.liveEl) return;\n const text = fill(this.t('indicatorPicker.added'), { name: item.name });\n // The same words again (a second RSI) are only read out when they change: a space tells them apart.\n this.liveEl.textContent = this.liveEl.textContent === text ? `${text}\\u00a0` : text;\n }\n\n private toggleFavorite(id: string, star: HTMLButtonElement): void {\n const on = !this.favorites.has(id);\n if (on) this.favorites.add(id);\n else this.favorites.delete(id);\n this.callbacks.onFavorite(id, on);\n star.setAttribute('aria-pressed', String(on));\n if (this.view !== 'favorites' || this.query || on) return;\n // Gone from the favourites: the keyboard moves to the next row's star.\n const stars = [...(this.listEl?.querySelectorAll<HTMLButtonElement>('.tcw-ip-star') ?? [])];\n const at = stars.indexOf(star);\n const hadFocus = document.activeElement === star;\n this.renderList();\n if (!hadFocus) return;\n const left = [...(this.listEl?.querySelectorAll<HTMLButtonElement>('.tcw-ip-star') ?? [])];\n (left[at] ?? left[left.length - 1] ?? this.searchEl)?.focus();\n }\n\n private markCount(row: HTMLElement): void {\n const n = this.counts.get(row.dataset.ind ?? '') ?? 0;\n const add = row.querySelector('.tcw-ip-add');\n let badge = row.querySelector<HTMLElement>('.tcw-ip-count');\n if (n === 0) {\n badge?.remove();\n return;\n }\n if (!badge) {\n badge = this.tag('tcw-ip-count', '');\n add?.appendChild(badge);\n }\n badge.textContent = String(n);\n badge.title = fill(this.t('indicatorPicker.onChart'), { count: String(n) });\n }\n\n private tag(className: string, text: string): HTMLSpanElement {\n const span = document.createElement('span');\n span.className = className;\n span.textContent = text;\n return span;\n }\n\n private note(text: string): HTMLDivElement {\n const el = document.createElement('div');\n el.className = 'tcw-ip-empty';\n el.textContent = text;\n return el;\n }\n\n /** One row in the list takes Tab: the one focused last. */\n private rove(target: EventTarget | null): void {\n if (!(target instanceof HTMLElement) || !target.classList.contains('tcw-ip-add')) return;\n this.listEl?.querySelectorAll<HTMLElement>('.tcw-ip-add[tabindex=\"0\"]').forEach((el) => {\n if (el !== target) el.tabIndex = -1;\n });\n target.tabIndex = 0;\n }\n\n private onKeyDown(e: KeyboardEvent, modal: HTMLElement): void {\n if (e.key === 'Escape') {\n e.preventDefault();\n e.stopPropagation();\n this.close();\n return;\n }\n keepTabInside(e, modal);\n }\n\n /** Down goes into the list; Enter adds the best match. */\n private onSearchKey(e: KeyboardEvent): void {\n const first = this.listEl?.querySelector<HTMLButtonElement>('.tcw-ip-add');\n if (e.key === 'ArrowDown' && first) {\n e.preventDefault();\n first.focus();\n } else if (e.key === 'Enter' && this.query.trim() && first) {\n e.preventDefault();\n first.click();\n }\n }\n\n /** Up and down through the tabs (Home, End), picking as they go. */\n private onNavKey(e: KeyboardEvent): void {\n const views = this.views();\n const at = views.indexOf(this.view);\n const next = e.key === 'ArrowDown' ? Math.min(views.length - 1, at + 1)\n : e.key === 'ArrowUp' ? Math.max(0, at - 1)\n : e.key === 'Home' ? 0\n : e.key === 'End' ? views.length - 1\n : -1;\n if (next < 0) return;\n e.preventDefault();\n this.select(views[next], true);\n }\n\n /** Up and down through the rows (from the top, back to the search); right to a row's star, left back. */\n private onListKey(e: KeyboardEvent): void {\n const target = e.target as HTMLElement;\n const row = target.closest('.tcw-ip-row, .tcw-ip-save');\n const adds = [...(this.listEl?.querySelectorAll<HTMLButtonElement>('.tcw-ip-add') ?? [])];\n const main = row?.classList.contains('tcw-ip-save') ? row as HTMLButtonElement : row?.querySelector<HTMLButtonElement>('.tcw-ip-add');\n const at = main ? adds.indexOf(main) : -1;\n let to: HTMLElement | null | undefined = null;\n if (e.key === 'ArrowDown') to = adds[at + 1];\n else if (e.key === 'ArrowUp') to = at > 0 ? adds[at - 1] : this.searchEl;\n else if (e.key === 'Home') to = adds[0];\n else if (e.key === 'End') to = adds[adds.length - 1];\n else if (e.key === 'ArrowRight' && target === main) to = row?.querySelector<HTMLButtonElement>('.tcw-ip-aside');\n else if (e.key === 'ArrowLeft' && target !== main) to = main;\n if (!to) return;\n e.preventDefault();\n to.focus();\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"WidgetIndicatorPicker-DKxoB4Un.cjs","names":[],"sources":["../src/widget/indicatorSearch.ts","../src/widget/WidgetIndicatorPicker.ts"],"sourcesContent":["/** What a search reads of an indicator. */\nexport interface SearchableIndicator {\n id: string;\n name: string;\n shortName: string;\n}\n\n/** Lower case without accents, so \"mômentum\" finds Momentum. */\nfunction fold(text: string): string {\n return text.normalize('NFD').replace(/[̀-ͯ]/g, '').toLowerCase();\n}\n\nconst WORD_BREAK = /[\\s/()%-]+/;\n\n/** How well an indicator matches `q` (folded): 0 best, null not at all. */\nfunction score(item: SearchableIndicator, q: string): number | null {\n const short = fold(item.shortName);\n const name = fold(item.name);\n if (short === q || fold(item.id) === q) return 0;\n if (short.startsWith(q) || name.startsWith(q)) return 1;\n if ([...name.split(WORD_BREAK), ...short.split(WORD_BREAK)].some((word) => word.startsWith(q))) return 2;\n if (name.includes(q) || short.includes(q)) return 3;\n return null;\n}\n\n/**\n * The indicators matching `query`, best first: the short name typed in full\n * (\"rsi\"), then names starting with it, then a word starting with it, then\n * any containing it. Ties go by name. Nothing typed finds nothing.\n */\nexport function searchIndicators<T extends SearchableIndicator>(items: readonly T[], query: string): T[] {\n const q = fold(query.trim());\n if (!q) return [];\n return items\n .map((item) => ({ item, rank: score(item, q) }))\n .filter((hit): hit is { item: T; rank: number } => hit.rank !== null)\n .sort((a, b) => a.rank - b.rank || a.item.name.localeCompare(b.item.name))\n .map((hit) => hit.item);\n}\n","import type { IndicatorCategory } from '@tradecanvas/commons';\nimport { INDICATOR_CATEGORIES } from '@tradecanvas/core';\nimport { createIcon } from './icons.js';\nimport { fill, type MessageKey, type Translator } from './i18n.js';\nimport { keepTabInside } from './focusTrap.js';\nimport { searchIndicators } from './indicatorSearch.js';\nimport { markPart } from './widgetFeatures.js';\n\n/** An indicator as the dialog lists it. */\nexport interface IndicatorPickerItem {\n id: string;\n name: string;\n shortName: string;\n placement: 'overlay' | 'panel';\n category: IndicatorCategory;\n}\n\nexport interface IndicatorPickerCallbacks {\n /** Add one; false when it wasn't added (nothing is announced then). */\n onAdd: (id: string) => boolean | void;\n onFavorite: (id: string, on: boolean) => void;\n /** The indicator templates; without them there is no Templates tab. */\n templates?: {\n onApply: (name: string) => void;\n onDelete: (name: string) => void;\n onSave: () => void;\n };\n onClose?: () => void;\n}\n\nexport interface IndicatorPickerContent {\n items: readonly IndicatorPickerItem[];\n favorites: readonly string[];\n /** The templates' names; null where they are switched off. */\n templates: readonly string[] | null;\n /** How many of each indicator are on the chart. */\n counts?: ReadonlyMap<string, number>;\n}\n\ntype View = 'favorites' | 'templates' | 'all' | IndicatorCategory;\n\nlet nextPickerId = 1;\n\nconst byName = (a: IndicatorPickerItem, b: IndicatorPickerItem) => a.name.localeCompare(b.name);\n\n/**\n * The indicators dialog: a search, the groups down the side (favourites,\n * templates, all, then each kind) and the list. A row adds its indicator and\n * the dialog stays open for the next; the star beside it keeps it in the\n * favourites. Mounts in the widget's overlay layer.\n */\nexport class WidgetIndicatorPicker {\n private backdrop: HTMLDivElement | null = null;\n private listEl: HTMLDivElement | null = null;\n private navEl: HTMLDivElement | null = null;\n private searchEl: HTMLInputElement | null = null;\n private liveEl: HTMLDivElement | null = null;\n private content: IndicatorPickerContent = { items: [], favorites: [], templates: [] };\n private favorites = new Set<string>();\n private counts: ReadonlyMap<string, number> = new Map();\n private view: View = 'all';\n /** The tab picked last, for the next opening. */\n private lastView: View | null = null;\n private query = '';\n private returnFocus: HTMLElement | null = null;\n private readonly uid = nextPickerId++;\n\n constructor(\n private readonly layer: () => HTMLElement,\n private readonly callbacks: IndicatorPickerCallbacks,\n private readonly t: Translator,\n ) {}\n\n open(content: IndicatorPickerContent): void {\n if (this.backdrop) this.close(false);\n this.returnFocus = document.activeElement instanceof HTMLElement ? document.activeElement : null;\n this.content = content;\n this.favorites = new Set(content.favorites);\n this.counts = content.counts ?? new Map();\n this.query = '';\n this.view = this.startView();\n this.build();\n this.searchEl?.focus();\n }\n\n close(giveFocusBack = true): void {\n if (!this.backdrop) return;\n this.backdrop.remove();\n this.backdrop = this.listEl = this.navEl = this.searchEl = this.liveEl = null;\n this.callbacks.onClose?.();\n if (giveFocusBack && this.returnFocus?.isConnected) this.returnFocus.focus();\n this.returnFocus = null;\n }\n\n isOpen(): boolean {\n return this.backdrop !== null;\n }\n\n /** The templates after one is saved or deleted; focus left on a deleted one goes to \"Save…\". */\n setTemplates(names: readonly string[]): void {\n this.content = { ...this.content, templates: [...names] };\n if (!this.listEl || this.view !== 'templates' || this.query) return;\n const hadFocus = this.listEl.contains(document.activeElement);\n this.renderList();\n if (hadFocus && !this.listEl.contains(document.activeElement)) {\n this.listEl.querySelector<HTMLButtonElement>('[data-tpl-save]')?.focus();\n }\n }\n\n /** How many of each indicator are on the chart, shown on their rows. */\n setCounts(counts: ReadonlyMap<string, number>): void {\n this.counts = counts;\n this.listEl?.querySelectorAll<HTMLElement>('.tcw-ip-row[data-ind]').forEach((row) => this.markCount(row));\n }\n\n destroy(): void {\n this.close(false);\n }\n\n private startView(): View {\n const views = this.views();\n if (this.lastView && views.includes(this.lastView) && !(this.lastView === 'favorites' && this.favorites.size === 0)) return this.lastView;\n return this.favorites.size > 0 ? 'favorites' : 'all';\n }\n\n /** The tabs down the side: favourites, templates, all, and each kind that has indicators. */\n private views(): View[] {\n const kinds = new Set(this.content.items.map((item) => item.category));\n return [\n 'favorites',\n ...(this.callbacks.templates && this.content.templates ? ['templates' as const] : []),\n 'all',\n ...INDICATOR_CATEGORIES.filter((category) => kinds.has(category)),\n ];\n }\n\n private viewLabel(view: View): string {\n if (view === 'favorites') return this.t('indicatorPicker.favorites');\n if (view === 'templates') return this.t('toolbar.indicators.templates');\n if (view === 'all') return this.t('toolbar.indicators.all');\n return this.t(`indicatorCategory.${view}` as MessageKey);\n }\n\n private build(): void {\n const backdrop = document.createElement('div');\n backdrop.className = 'tcw-modal-backdrop';\n backdrop.addEventListener('click', (e) => {\n if (e.target === backdrop) this.close();\n });\n const modal = document.createElement('div');\n modal.className = 'tcw-modal tcw-ip';\n modal.setAttribute('role', 'dialog');\n modal.setAttribute('aria-modal', 'true');\n modal.setAttribute('aria-labelledby', `tcw-ip-title-${this.uid}`);\n modal.tabIndex = -1;\n modal.addEventListener('keydown', (e) => this.onKeyDown(e, modal));\n\n const live = document.createElement('div');\n live.className = 'tcw-visually-hidden';\n live.setAttribute('aria-live', 'polite');\n modal.append(this.buildHeader(), this.buildSearch(), this.buildBody(), live);\n backdrop.appendChild(modal);\n this.layer().appendChild(backdrop);\n this.backdrop = backdrop;\n this.liveEl = live;\n this.renderNav();\n this.renderList();\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-ip-title-${this.uid}`;\n title.textContent = this.t('toolbar.indicators');\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.close());\n header.append(title, closeBtn);\n return header;\n }\n\n private buildSearch(): HTMLLabelElement {\n const row = document.createElement('label');\n row.className = 'tcw-ip-search';\n const search = document.createElement('input');\n search.type = 'search';\n search.id = `tcw-ip-search-${this.uid}`;\n search.placeholder = this.t('indicatorPicker.search');\n search.setAttribute('aria-label', this.t('indicatorPicker.search'));\n search.autocomplete = 'off';\n search.spellcheck = false;\n search.addEventListener('input', () => {\n this.query = search.value;\n this.renderList();\n });\n search.addEventListener('keydown', (e) => this.onSearchKey(e));\n row.innerHTML = createIcon('search', 16);\n row.appendChild(search);\n this.searchEl = search;\n return row;\n }\n\n /** The groups down the side, and the list beside them. */\n private buildBody(): HTMLDivElement {\n const body = document.createElement('div');\n body.className = 'tcw-ip-body';\n const nav = document.createElement('div');\n nav.className = 'tcw-ip-nav';\n nav.setAttribute('role', 'tablist');\n nav.setAttribute('aria-orientation', 'vertical');\n nav.setAttribute('aria-label', this.t('indicatorPicker.groups'));\n nav.addEventListener('keydown', (e) => this.onNavKey(e));\n const list = document.createElement('div');\n list.className = 'tcw-ip-list';\n list.id = `tcw-ip-list-${this.uid}`;\n list.setAttribute('role', 'tabpanel');\n list.addEventListener('keydown', (e) => this.onListKey(e));\n list.addEventListener('focusin', (e) => this.rove(e.target));\n body.append(nav, list);\n this.navEl = nav;\n this.listEl = list;\n return body;\n }\n\n private renderNav(): void {\n const nav = this.navEl;\n if (!nav) return;\n nav.replaceChildren(...this.views().map((view) => {\n const tab = document.createElement('button');\n tab.type = 'button';\n tab.className = 'tcw-ip-tab';\n tab.id = `tcw-ip-tab-${this.uid}-${view}`;\n tab.dataset.view = view;\n tab.setAttribute('role', 'tab');\n tab.setAttribute('aria-controls', `tcw-ip-list-${this.uid}`);\n const icon = view === 'favorites' ? createIcon('star', 14) : view === 'templates' ? createIcon('layers', 14) : '';\n tab.innerHTML = `${icon}<span></span>`;\n tab.querySelector('span')!.textContent = this.viewLabel(view);\n if (view === 'templates') markPart(tab, 'indicatorTemplates');\n tab.addEventListener('click', () => this.select(view));\n return tab;\n }));\n this.markNav();\n }\n\n private markNav(): void {\n this.navEl?.querySelectorAll<HTMLButtonElement>('.tcw-ip-tab').forEach((tab) => {\n const on = tab.dataset.view === this.view;\n tab.setAttribute('aria-selected', String(on));\n tab.classList.toggle('tcw-active', on);\n tab.tabIndex = on ? 0 : -1;\n });\n this.labelList();\n }\n\n /** The list is named by its tab, or by the search while it shows the matches. */\n private labelList(): void {\n const searching = this.query.trim() !== '';\n this.listEl?.setAttribute('aria-labelledby', searching ? `tcw-ip-search-${this.uid}` : `tcw-ip-tab-${this.uid}-${this.view}`);\n }\n\n private select(view: View, focusTab = false): void {\n this.view = this.lastView = view;\n if (this.searchEl && this.query) this.searchEl.value = this.query = '';\n this.markNav();\n this.renderList();\n if (focusTab) this.navEl?.querySelector<HTMLButtonElement>(`[data-view=\"${view}\"]`)?.focus();\n }\n\n private renderList(): void {\n const list = this.listEl;\n if (!list) return;\n list.replaceChildren();\n const items = this.content.items;\n // While searching, the results come from every group: the one picked steps back.\n this.navEl?.classList.toggle('tcw-ip-nav--searching', this.query.trim() !== '');\n this.labelList();\n if (this.query.trim()) {\n const hits = searchIndicators(items, this.query);\n if (hits.length === 0) list.appendChild(this.note(fill(this.t('indicatorPicker.noMatch'), { query: this.query.trim() })));\n else list.append(...hits.map((item) => this.row(item)));\n } else if (this.view === 'templates') {\n this.renderTemplates(list);\n } else if (this.view === 'favorites') {\n const favorites = items.filter((item) => this.favorites.has(item.id)).sort(byName);\n if (favorites.length === 0) list.appendChild(this.note(this.t('indicatorPicker.noFavorites')));\n else list.append(...favorites.map((item) => this.row(item)));\n } else if (this.view === 'all') {\n for (const category of INDICATOR_CATEGORIES) {\n const group = items.filter((item) => item.category === category).sort(byName);\n if (group.length === 0) continue;\n const heading = document.createElement('div');\n heading.className = 'tcw-ip-heading';\n heading.textContent = this.viewLabel(category);\n list.append(heading, ...group.map((item) => this.row(item)));\n }\n } else {\n list.append(...items.filter((item) => item.category === this.view).sort(byName).map((item) => this.row(item)));\n }\n list.querySelector<HTMLButtonElement>('.tcw-ip-add')?.setAttribute('tabindex', '0');\n }\n\n private row(item: IndicatorPickerItem): HTMLDivElement {\n const row = document.createElement('div');\n row.className = 'tcw-ip-row';\n row.dataset.ind = item.id;\n const add = document.createElement('button');\n add.type = 'button';\n add.className = 'tcw-ip-add';\n add.dataset.ind = item.id;\n add.tabIndex = -1;\n const name = document.createElement('span');\n name.className = 'tcw-ip-name';\n name.textContent = item.name;\n add.appendChild(name);\n if (item.shortName && item.shortName !== item.name) add.appendChild(this.tag('tcw-ip-short', item.shortName));\n add.appendChild(this.tag('tcw-ip-kind', this.t(`indicatorType.${item.placement}`)));\n add.addEventListener('click', () => this.add(item));\n const star = document.createElement('button');\n star.type = 'button';\n star.className = 'tcw-ip-aside tcw-ip-star';\n star.tabIndex = -1;\n star.title = this.t('indicatorPicker.favorite');\n star.setAttribute('aria-label', `${this.t('indicatorPicker.favorite')}: ${item.name}`);\n star.setAttribute('aria-pressed', String(this.favorites.has(item.id)));\n star.innerHTML = createIcon('star', 14);\n star.addEventListener('click', () => this.toggleFavorite(item.id, star));\n row.append(add, star);\n this.markCount(row);\n return row;\n }\n\n private renderTemplates(list: HTMLElement): void {\n const save = document.createElement('button');\n save.type = 'button';\n save.className = 'tcw-ip-add tcw-ip-save';\n save.tabIndex = -1;\n save.dataset.tplSave = '';\n save.innerHTML = `${createIcon('save', 14)}<span class=\"tcw-ip-name\"></span>`;\n save.querySelector('span')!.textContent = this.t('templates.save');\n save.addEventListener('click', () => {\n this.close();\n this.callbacks.templates?.onSave();\n });\n list.appendChild(save);\n const names = this.content.templates ?? [];\n if (names.length === 0) list.appendChild(this.note(this.t('indicatorPicker.noTemplates')));\n for (const name of names) {\n const row = document.createElement('div');\n row.className = 'tcw-ip-row';\n const apply = document.createElement('button');\n apply.type = 'button';\n apply.className = 'tcw-ip-add';\n apply.tabIndex = -1;\n apply.dataset.tplApply = name;\n apply.innerHTML = `${createIcon('layers', 14)}<span class=\"tcw-ip-name\"></span>`;\n apply.querySelector('span')!.textContent = name;\n apply.addEventListener('click', () => {\n this.close();\n this.callbacks.templates?.onApply(name);\n });\n const remove = document.createElement('button');\n remove.type = 'button';\n remove.className = 'tcw-ip-aside tcw-ip-remove';\n remove.tabIndex = -1;\n remove.dataset.tplDelete = name;\n const label = fill(this.t('templates.delete'), { name });\n remove.title = label;\n remove.setAttribute('aria-label', label);\n remove.innerHTML = createIcon('x', 12);\n remove.addEventListener('click', () => this.callbacks.templates?.onDelete(name));\n row.append(apply, remove);\n list.appendChild(row);\n }\n }\n\n private add(item: IndicatorPickerItem): void {\n if (this.callbacks.onAdd(item.id) === false || !this.liveEl) return;\n const text = fill(this.t('indicatorPicker.added'), { name: item.name });\n // The same words again (a second RSI) are only read out when they change: a space tells them apart.\n this.liveEl.textContent = this.liveEl.textContent === text ? `${text}\\u00a0` : text;\n }\n\n private toggleFavorite(id: string, star: HTMLButtonElement): void {\n const on = !this.favorites.has(id);\n if (on) this.favorites.add(id);\n else this.favorites.delete(id);\n this.callbacks.onFavorite(id, on);\n star.setAttribute('aria-pressed', String(on));\n if (this.view !== 'favorites' || this.query || on) return;\n // Gone from the favourites: the keyboard moves to the next row's star.\n const stars = [...(this.listEl?.querySelectorAll<HTMLButtonElement>('.tcw-ip-star') ?? [])];\n const at = stars.indexOf(star);\n const hadFocus = document.activeElement === star;\n this.renderList();\n if (!hadFocus) return;\n const left = [...(this.listEl?.querySelectorAll<HTMLButtonElement>('.tcw-ip-star') ?? [])];\n (left[at] ?? left[left.length - 1] ?? this.searchEl)?.focus();\n }\n\n private markCount(row: HTMLElement): void {\n const n = this.counts.get(row.dataset.ind ?? '') ?? 0;\n const add = row.querySelector('.tcw-ip-add');\n let badge = row.querySelector<HTMLElement>('.tcw-ip-count');\n if (n === 0) {\n badge?.remove();\n return;\n }\n if (!badge) {\n badge = this.tag('tcw-ip-count', '');\n add?.appendChild(badge);\n }\n badge.textContent = String(n);\n badge.title = fill(this.t('indicatorPicker.onChart'), { count: String(n) });\n }\n\n private tag(className: string, text: string): HTMLSpanElement {\n const span = document.createElement('span');\n span.className = className;\n span.textContent = text;\n return span;\n }\n\n private note(text: string): HTMLDivElement {\n const el = document.createElement('div');\n el.className = 'tcw-ip-empty';\n el.textContent = text;\n return el;\n }\n\n /** One row in the list takes Tab: the one focused last. */\n private rove(target: EventTarget | null): void {\n if (!(target instanceof HTMLElement) || !target.classList.contains('tcw-ip-add')) return;\n this.listEl?.querySelectorAll<HTMLElement>('.tcw-ip-add[tabindex=\"0\"]').forEach((el) => {\n if (el !== target) el.tabIndex = -1;\n });\n target.tabIndex = 0;\n }\n\n private onKeyDown(e: KeyboardEvent, modal: HTMLElement): void {\n if (e.key === 'Escape') {\n e.preventDefault();\n e.stopPropagation();\n this.close();\n return;\n }\n keepTabInside(e, modal);\n }\n\n /** Down goes into the list; Enter adds the best match. */\n private onSearchKey(e: KeyboardEvent): void {\n const first = this.listEl?.querySelector<HTMLButtonElement>('.tcw-ip-add');\n if (e.key === 'ArrowDown' && first) {\n e.preventDefault();\n first.focus();\n } else if (e.key === 'Enter' && this.query.trim() && first) {\n e.preventDefault();\n first.click();\n }\n }\n\n /** Up and down through the tabs (Home, End), picking as they go. */\n private onNavKey(e: KeyboardEvent): void {\n const views = this.views();\n const at = views.indexOf(this.view);\n const next = e.key === 'ArrowDown' ? Math.min(views.length - 1, at + 1)\n : e.key === 'ArrowUp' ? Math.max(0, at - 1)\n : e.key === 'Home' ? 0\n : e.key === 'End' ? views.length - 1\n : -1;\n if (next < 0) return;\n e.preventDefault();\n this.select(views[next], true);\n }\n\n /** Up and down through the rows (from the top, back to the search); right to a row's star, left back. */\n private onListKey(e: KeyboardEvent): void {\n const target = e.target as HTMLElement;\n const row = target.closest('.tcw-ip-row, .tcw-ip-save');\n const adds = [...(this.listEl?.querySelectorAll<HTMLButtonElement>('.tcw-ip-add') ?? [])];\n const main = row?.classList.contains('tcw-ip-save') ? row as HTMLButtonElement : row?.querySelector<HTMLButtonElement>('.tcw-ip-add');\n const at = main ? adds.indexOf(main) : -1;\n let to: HTMLElement | null | undefined = null;\n if (e.key === 'ArrowDown') to = adds[at + 1];\n else if (e.key === 'ArrowUp') to = at > 0 ? adds[at - 1] : this.searchEl;\n else if (e.key === 'Home') to = adds[0];\n else if (e.key === 'End') to = adds[adds.length - 1];\n else if (e.key === 'ArrowRight' && target === main) to = row?.querySelector<HTMLButtonElement>('.tcw-ip-aside');\n else if (e.key === 'ArrowLeft' && target !== main) to = main;\n if (!to) return;\n e.preventDefault();\n to.focus();\n }\n}\n"],"mappings":"2JAQA,SAAS,EAAK,EAAsB,CAClC,OAAO,EAAK,UAAU,MAAM,CAAC,QAAQ,SAAU,GAAG,CAAC,aAAa,CAGlE,IAAM,EAAa,aAGnB,SAAS,EAAM,EAA2B,EAA0B,CAClE,IAAM,EAAQ,EAAK,EAAK,UAAU,CAC5B,EAAO,EAAK,EAAK,KAAK,CAK5B,OAJI,IAAU,GAAK,EAAK,EAAK,GAAG,GAAK,EAAU,EAC3C,EAAM,WAAW,EAAE,EAAI,EAAK,WAAW,EAAE,CAAS,EAClD,CAAC,GAAG,EAAK,MAAM,EAAW,CAAE,GAAG,EAAM,MAAM,EAAW,CAAC,CAAC,KAAM,GAAS,EAAK,WAAW,EAAE,CAAC,CAAS,EACnG,EAAK,SAAS,EAAE,EAAI,EAAM,SAAS,EAAE,CAAS,EAC3C,KAQT,SAAgB,EAAgD,EAAqB,EAAoB,CACvG,IAAM,EAAI,EAAK,EAAM,MAAM,CAAC,CAE5B,OADK,EACE,EACJ,IAAK,IAAU,CAAE,OAAM,KAAM,EAAM,EAAM,EAAE,CAAE,EAAE,CAC/C,OAAQ,GAA0C,EAAI,OAAS,KAAK,CACpE,MAAM,EAAG,IAAM,EAAE,KAAO,EAAE,MAAQ,EAAE,KAAK,KAAK,cAAc,EAAE,KAAK,KAAK,CAAC,CACzE,IAAK,GAAQ,EAAI,KAAK,CALV,EAAE,CCSnB,IAAI,EAAe,EAEb,GAAU,EAAwB,IAA2B,EAAE,KAAK,cAAc,EAAE,KAAK,CAQlF,EAAb,KAAmC,CACjC,SAA0C,KAC1C,OAAwC,KACxC,MAAuC,KACvC,SAA4C,KAC5C,OAAwC,KACxC,QAA0C,CAAE,MAAO,EAAE,CAAE,UAAW,EAAE,CAAE,UAAW,EAAE,CAAE,CACrF,UAAoB,IAAI,IACxB,OAA8C,IAAI,IAClD,KAAqB,MAErB,SAAgC,KAChC,MAAgB,GAChB,YAA0C,KAC1C,IAAuB,IAEvB,YACE,EACA,EACA,EACA,CAHiB,KAAA,MAAA,EACA,KAAA,UAAA,EACA,KAAA,EAAA,EAGnB,KAAK,EAAuC,CACtC,KAAK,UAAU,KAAK,MAAM,GAAM,CACpC,KAAK,YAAc,SAAS,yBAAyB,YAAc,SAAS,cAAgB,KAC5F,KAAK,QAAU,EACf,KAAK,UAAY,IAAI,IAAI,EAAQ,UAAU,CAC3C,KAAK,OAAS,EAAQ,QAAU,IAAI,IACpC,KAAK,MAAQ,GACb,KAAK,KAAO,KAAK,WAAW,CAC5B,KAAK,OAAO,CACZ,KAAK,UAAU,OAAO,CAGxB,MAAM,EAAgB,GAAY,CAC3B,KAAK,WACV,KAAK,SAAS,QAAQ,CACtB,KAAK,SAAW,KAAK,OAAS,KAAK,MAAQ,KAAK,SAAW,KAAK,OAAS,KACzE,KAAK,UAAU,WAAW,CACtB,GAAiB,KAAK,aAAa,aAAa,KAAK,YAAY,OAAO,CAC5E,KAAK,YAAc,MAGrB,QAAkB,CAChB,OAAO,KAAK,WAAa,KAI3B,aAAa,EAAgC,CAE3C,GADA,KAAK,QAAU,CAAE,GAAG,KAAK,QAAS,UAAW,CAAC,GAAG,EAAM,CAAE,CACrD,CAAC,KAAK,QAAU,KAAK,OAAS,aAAe,KAAK,MAAO,OAC7D,IAAM,EAAW,KAAK,OAAO,SAAS,SAAS,cAAc,CAC7D,KAAK,YAAY,CACb,GAAY,CAAC,KAAK,OAAO,SAAS,SAAS,cAAc,EAC3D,KAAK,OAAO,cAAiC,kBAAkB,EAAE,OAAO,CAK5E,UAAU,EAA2C,CACnD,KAAK,OAAS,EACd,KAAK,QAAQ,iBAA8B,wBAAwB,CAAC,QAAS,GAAQ,KAAK,UAAU,EAAI,CAAC,CAG3G,SAAgB,CACd,KAAK,MAAM,GAAM,CAGnB,WAA0B,CACxB,IAAM,EAAQ,KAAK,OAAO,CAE1B,OADI,KAAK,UAAY,EAAM,SAAS,KAAK,SAAS,EAAI,EAAE,KAAK,WAAa,aAAe,KAAK,UAAU,OAAS,GAAW,KAAK,SAC1H,KAAK,UAAU,KAAO,EAAI,YAAc,MAIjD,OAAwB,CACtB,IAAM,EAAQ,IAAI,IAAI,KAAK,QAAQ,MAAM,IAAK,GAAS,EAAK,SAAS,CAAC,CACtE,MAAO,CACL,YACA,GAAI,KAAK,UAAU,WAAa,KAAK,QAAQ,UAAY,CAAC,YAAqB,CAAG,EAAE,CACpF,MACA,GAAG,EAAA,qBAAqB,OAAQ,GAAa,EAAM,IAAI,EAAS,CAAC,CAClE,CAGH,UAAkB,EAAoB,CAIpC,OAHI,IAAS,YAAoB,KAAK,EAAE,4BAA4B,CAChE,IAAS,YAAoB,KAAK,EAAE,+BAA+B,CACnE,IAAS,MAAc,KAAK,EAAE,yBAAyB,CACpD,KAAK,EAAE,qBAAqB,IAAqB,CAG1D,OAAsB,CACpB,IAAM,EAAW,SAAS,cAAc,MAAM,CAC9C,EAAS,UAAY,qBACrB,EAAS,iBAAiB,QAAU,GAAM,CACpC,EAAE,SAAW,GAAU,KAAK,OAAO,EACvC,CACF,IAAM,EAAQ,SAAS,cAAc,MAAM,CAC3C,EAAM,UAAY,mBAClB,EAAM,aAAa,OAAQ,SAAS,CACpC,EAAM,aAAa,aAAc,OAAO,CACxC,EAAM,aAAa,kBAAmB,gBAAgB,KAAK,MAAM,CACjE,EAAM,SAAW,GACjB,EAAM,iBAAiB,UAAY,GAAM,KAAK,UAAU,EAAG,EAAM,CAAC,CAElE,IAAM,EAAO,SAAS,cAAc,MAAM,CAC1C,EAAK,UAAY,sBACjB,EAAK,aAAa,YAAa,SAAS,CACxC,EAAM,OAAO,KAAK,aAAa,CAAE,KAAK,aAAa,CAAE,KAAK,WAAW,CAAE,EAAK,CAC5E,EAAS,YAAY,EAAM,CAC3B,KAAK,OAAO,CAAC,YAAY,EAAS,CAClC,KAAK,SAAW,EAChB,KAAK,OAAS,EACd,KAAK,WAAW,CAChB,KAAK,YAAY,CAGnB,aAAsC,CACpC,IAAM,EAAS,SAAS,cAAc,MAAM,CAC5C,EAAO,UAAY,mBACnB,IAAM,EAAQ,SAAS,cAAc,KAAK,CAC1C,EAAM,GAAK,gBAAgB,KAAK,MAChC,EAAM,YAAc,KAAK,EAAE,qBAAqB,CAChD,IAAM,EAAW,SAAS,cAAc,SAAS,CAOjD,MANA,GAAS,KAAO,SAChB,EAAS,UAAY,kBACrB,EAAS,aAAa,aAAc,KAAK,EAAE,0BAA0B,CAAC,CACtE,EAAS,UAAY,EAAA,EAAW,IAAK,GAAG,CACxC,EAAS,iBAAiB,YAAe,KAAK,OAAO,CAAC,CACtD,EAAO,OAAO,EAAO,EAAS,CACvB,EAGT,aAAwC,CACtC,IAAM,EAAM,SAAS,cAAc,QAAQ,CAC3C,EAAI,UAAY,gBAChB,IAAM,EAAS,SAAS,cAAc,QAAQ,CAe9C,MAdA,GAAO,KAAO,SACd,EAAO,GAAK,iBAAiB,KAAK,MAClC,EAAO,YAAc,KAAK,EAAE,yBAAyB,CACrD,EAAO,aAAa,aAAc,KAAK,EAAE,yBAAyB,CAAC,CACnE,EAAO,aAAe,MACtB,EAAO,WAAa,GACpB,EAAO,iBAAiB,YAAe,CACrC,KAAK,MAAQ,EAAO,MACpB,KAAK,YAAY,EACjB,CACF,EAAO,iBAAiB,UAAY,GAAM,KAAK,YAAY,EAAE,CAAC,CAC9D,EAAI,UAAY,EAAA,EAAW,SAAU,GAAG,CACxC,EAAI,YAAY,EAAO,CACvB,KAAK,SAAW,EACT,EAIT,WAAoC,CAClC,IAAM,EAAO,SAAS,cAAc,MAAM,CAC1C,EAAK,UAAY,cACjB,IAAM,EAAM,SAAS,cAAc,MAAM,CACzC,EAAI,UAAY,aAChB,EAAI,aAAa,OAAQ,UAAU,CACnC,EAAI,aAAa,mBAAoB,WAAW,CAChD,EAAI,aAAa,aAAc,KAAK,EAAE,yBAAyB,CAAC,CAChE,EAAI,iBAAiB,UAAY,GAAM,KAAK,SAAS,EAAE,CAAC,CACxD,IAAM,EAAO,SAAS,cAAc,MAAM,CAS1C,MARA,GAAK,UAAY,cACjB,EAAK,GAAK,eAAe,KAAK,MAC9B,EAAK,aAAa,OAAQ,WAAW,CACrC,EAAK,iBAAiB,UAAY,GAAM,KAAK,UAAU,EAAE,CAAC,CAC1D,EAAK,iBAAiB,UAAY,GAAM,KAAK,KAAK,EAAE,OAAO,CAAC,CAC5D,EAAK,OAAO,EAAK,EAAK,CACtB,KAAK,MAAQ,EACb,KAAK,OAAS,EACP,EAGT,WAA0B,CACxB,IAAM,EAAM,KAAK,MACZ,IACL,EAAI,gBAAgB,GAAG,KAAK,OAAO,CAAC,IAAK,GAAS,CAChD,IAAM,EAAM,SAAS,cAAc,SAAS,CAY5C,MAXA,GAAI,KAAO,SACX,EAAI,UAAY,aAChB,EAAI,GAAK,cAAc,KAAK,IAAI,GAAG,IACnC,EAAI,QAAQ,KAAO,EACnB,EAAI,aAAa,OAAQ,MAAM,CAC/B,EAAI,aAAa,gBAAiB,eAAe,KAAK,MAAM,CAE5D,EAAI,UAAY,GADH,IAAS,YAAc,EAAA,EAAW,OAAQ,GAAG,CAAG,IAAS,YAAc,EAAA,EAAW,SAAU,GAAG,CAAG,GACvF,eACxB,EAAI,cAAc,OAAO,CAAE,YAAc,KAAK,UAAU,EAAK,CACzD,IAAS,aAAa,EAAA,EAAS,EAAK,qBAAqB,CAC7D,EAAI,iBAAiB,YAAe,KAAK,OAAO,EAAK,CAAC,CAC/C,GACP,CAAC,CACH,KAAK,SAAS,EAGhB,SAAwB,CACtB,KAAK,OAAO,iBAAoC,cAAc,CAAC,QAAS,GAAQ,CAC9E,IAAM,EAAK,EAAI,QAAQ,OAAS,KAAK,KACrC,EAAI,aAAa,gBAAiB,OAAO,EAAG,CAAC,CAC7C,EAAI,UAAU,OAAO,aAAc,EAAG,CACtC,EAAI,SAAW,EAAK,EAAI,IACxB,CACF,KAAK,WAAW,CAIlB,WAA0B,CACxB,IAAM,EAAY,KAAK,MAAM,MAAM,GAAK,GACxC,KAAK,QAAQ,aAAa,kBAAmB,EAAY,iBAAiB,KAAK,MAAQ,cAAc,KAAK,IAAI,GAAG,KAAK,OAAO,CAG/H,OAAe,EAAY,EAAW,GAAa,CACjD,KAAK,KAAO,KAAK,SAAW,EACxB,KAAK,UAAY,KAAK,QAAO,KAAK,SAAS,MAAQ,KAAK,MAAQ,IACpE,KAAK,SAAS,CACd,KAAK,YAAY,CACb,GAAU,KAAK,OAAO,cAAiC,eAAe,EAAK,IAAI,EAAE,OAAO,CAG9F,YAA2B,CACzB,IAAM,EAAO,KAAK,OAClB,GAAI,CAAC,EAAM,OACX,EAAK,iBAAiB,CACtB,IAAM,EAAQ,KAAK,QAAQ,MAI3B,GAFA,KAAK,OAAO,UAAU,OAAO,wBAAyB,KAAK,MAAM,MAAM,GAAK,GAAG,CAC/E,KAAK,WAAW,CACZ,KAAK,MAAM,MAAM,CAAE,CACrB,IAAM,EAAO,EAAiB,EAAO,KAAK,MAAM,CAC5C,EAAK,SAAW,EAAG,EAAK,YAAY,KAAK,KAAK,EAAA,EAAK,KAAK,EAAE,0BAA0B,CAAE,CAAE,MAAO,KAAK,MAAM,MAAM,CAAE,CAAC,CAAC,CAAC,CACpH,EAAK,OAAO,GAAG,EAAK,IAAK,GAAS,KAAK,IAAI,EAAK,CAAC,CAAC,SAC9C,KAAK,OAAS,YACvB,KAAK,gBAAgB,EAAK,SACjB,KAAK,OAAS,YAAa,CACpC,IAAM,EAAY,EAAM,OAAQ,GAAS,KAAK,UAAU,IAAI,EAAK,GAAG,CAAC,CAAC,KAAK,EAAO,CAC9E,EAAU,SAAW,EAAG,EAAK,YAAY,KAAK,KAAK,KAAK,EAAE,8BAA8B,CAAC,CAAC,CACzF,EAAK,OAAO,GAAG,EAAU,IAAK,GAAS,KAAK,IAAI,EAAK,CAAC,CAAC,SACnD,KAAK,OAAS,MACvB,IAAK,IAAM,KAAY,EAAA,qBAAsB,CAC3C,IAAM,EAAQ,EAAM,OAAQ,GAAS,EAAK,WAAa,EAAS,CAAC,KAAK,EAAO,CAC7E,GAAI,EAAM,SAAW,EAAG,SACxB,IAAM,EAAU,SAAS,cAAc,MAAM,CAC7C,EAAQ,UAAY,iBACpB,EAAQ,YAAc,KAAK,UAAU,EAAS,CAC9C,EAAK,OAAO,EAAS,GAAG,EAAM,IAAK,GAAS,KAAK,IAAI,EAAK,CAAC,CAAC,MAG9D,EAAK,OAAO,GAAG,EAAM,OAAQ,GAAS,EAAK,WAAa,KAAK,KAAK,CAAC,KAAK,EAAO,CAAC,IAAK,GAAS,KAAK,IAAI,EAAK,CAAC,CAAC,CAEhH,EAAK,cAAiC,cAAc,EAAE,aAAa,WAAY,IAAI,CAGrF,IAAY,EAA2C,CACrD,IAAM,EAAM,SAAS,cAAc,MAAM,CACzC,EAAI,UAAY,aAChB,EAAI,QAAQ,IAAM,EAAK,GACvB,IAAM,EAAM,SAAS,cAAc,SAAS,CAC5C,EAAI,KAAO,SACX,EAAI,UAAY,aAChB,EAAI,QAAQ,IAAM,EAAK,GACvB,EAAI,SAAW,GACf,IAAM,EAAO,SAAS,cAAc,OAAO,CAC3C,EAAK,UAAY,cACjB,EAAK,YAAc,EAAK,KACxB,EAAI,YAAY,EAAK,CACjB,EAAK,WAAa,EAAK,YAAc,EAAK,MAAM,EAAI,YAAY,KAAK,IAAI,eAAgB,EAAK,UAAU,CAAC,CAC7G,EAAI,YAAY,KAAK,IAAI,cAAe,KAAK,EAAE,iBAAiB,EAAK,YAAY,CAAC,CAAC,CACnF,EAAI,iBAAiB,YAAe,KAAK,IAAI,EAAK,CAAC,CACnD,IAAM,EAAO,SAAS,cAAc,SAAS,CAW7C,MAVA,GAAK,KAAO,SACZ,EAAK,UAAY,2BACjB,EAAK,SAAW,GAChB,EAAK,MAAQ,KAAK,EAAE,2BAA2B,CAC/C,EAAK,aAAa,aAAc,GAAG,KAAK,EAAE,2BAA2B,CAAC,IAAI,EAAK,OAAO,CACtF,EAAK,aAAa,eAAgB,OAAO,KAAK,UAAU,IAAI,EAAK,GAAG,CAAC,CAAC,CACtE,EAAK,UAAY,EAAA,EAAW,OAAQ,GAAG,CACvC,EAAK,iBAAiB,YAAe,KAAK,eAAe,EAAK,GAAI,EAAK,CAAC,CACxE,EAAI,OAAO,EAAK,EAAK,CACrB,KAAK,UAAU,EAAI,CACZ,EAGT,gBAAwB,EAAyB,CAC/C,IAAM,EAAO,SAAS,cAAc,SAAS,CAC7C,EAAK,KAAO,SACZ,EAAK,UAAY,yBACjB,EAAK,SAAW,GAChB,EAAK,QAAQ,QAAU,GACvB,EAAK,UAAY,GAAG,EAAA,EAAW,OAAQ,GAAG,CAAC,mCAC3C,EAAK,cAAc,OAAO,CAAE,YAAc,KAAK,EAAE,iBAAiB,CAClE,EAAK,iBAAiB,YAAe,CACnC,KAAK,OAAO,CACZ,KAAK,UAAU,WAAW,QAAQ,EAClC,CACF,EAAK,YAAY,EAAK,CACtB,IAAM,EAAQ,KAAK,QAAQ,WAAa,EAAE,CACtC,EAAM,SAAW,GAAG,EAAK,YAAY,KAAK,KAAK,KAAK,EAAE,8BAA8B,CAAC,CAAC,CAC1F,IAAK,IAAM,KAAQ,EAAO,CACxB,IAAM,EAAM,SAAS,cAAc,MAAM,CACzC,EAAI,UAAY,aAChB,IAAM,EAAQ,SAAS,cAAc,SAAS,CAC9C,EAAM,KAAO,SACb,EAAM,UAAY,aAClB,EAAM,SAAW,GACjB,EAAM,QAAQ,SAAW,EACzB,EAAM,UAAY,GAAG,EAAA,EAAW,SAAU,GAAG,CAAC,mCAC9C,EAAM,cAAc,OAAO,CAAE,YAAc,EAC3C,EAAM,iBAAiB,YAAe,CACpC,KAAK,OAAO,CACZ,KAAK,UAAU,WAAW,QAAQ,EAAK,EACvC,CACF,IAAM,EAAS,SAAS,cAAc,SAAS,CAC/C,EAAO,KAAO,SACd,EAAO,UAAY,6BACnB,EAAO,SAAW,GAClB,EAAO,QAAQ,UAAY,EAC3B,IAAM,EAAQ,EAAA,EAAK,KAAK,EAAE,mBAAmB,CAAE,CAAE,OAAM,CAAC,CACxD,EAAO,MAAQ,EACf,EAAO,aAAa,aAAc,EAAM,CACxC,EAAO,UAAY,EAAA,EAAW,IAAK,GAAG,CACtC,EAAO,iBAAiB,YAAe,KAAK,UAAU,WAAW,SAAS,EAAK,CAAC,CAChF,EAAI,OAAO,EAAO,EAAO,CACzB,EAAK,YAAY,EAAI,EAIzB,IAAY,EAAiC,CAC3C,GAAI,KAAK,UAAU,MAAM,EAAK,GAAG,GAAK,IAAS,CAAC,KAAK,OAAQ,OAC7D,IAAM,EAAO,EAAA,EAAK,KAAK,EAAE,wBAAwB,CAAE,CAAE,KAAM,EAAK,KAAM,CAAC,CAEvE,KAAK,OAAO,YAAc,KAAK,OAAO,cAAgB,EAAO,GAAG,EAAK,QAAU,EAGjF,eAAuB,EAAY,EAA+B,CAChE,IAAM,EAAK,CAAC,KAAK,UAAU,IAAI,EAAG,CAKlC,GAJI,EAAI,KAAK,UAAU,IAAI,EAAG,CACzB,KAAK,UAAU,OAAO,EAAG,CAC9B,KAAK,UAAU,WAAW,EAAI,EAAG,CACjC,EAAK,aAAa,eAAgB,OAAO,EAAG,CAAC,CACzC,KAAK,OAAS,aAAe,KAAK,OAAS,EAAI,OAGnD,IAAM,EAAK,CADI,GAAI,KAAK,QAAQ,iBAAoC,eAAe,EAAI,EAAE,CAC9E,CAAM,QAAQ,EAAK,CACxB,EAAW,SAAS,gBAAkB,EAE5C,GADA,KAAK,YAAY,CACb,CAAC,EAAU,OACf,IAAM,EAAO,CAAC,GAAI,KAAK,QAAQ,iBAAoC,eAAe,EAAI,EAAE,CAAE,EACzF,EAAK,IAAO,EAAK,EAAK,OAAS,IAAM,KAAK,WAAW,OAAO,CAG/D,UAAkB,EAAwB,CACxC,IAAM,EAAI,KAAK,OAAO,IAAI,EAAI,QAAQ,KAAO,GAAG,EAAI,EAC9C,EAAM,EAAI,cAAc,cAAc,CACxC,EAAQ,EAAI,cAA2B,gBAAgB,CAC3D,GAAI,IAAM,EAAG,CACX,GAAO,QAAQ,CACf,OAEG,IACH,EAAQ,KAAK,IAAI,eAAgB,GAAG,CACpC,GAAK,YAAY,EAAM,EAEzB,EAAM,YAAc,OAAO,EAAE,CAC7B,EAAM,MAAQ,EAAA,EAAK,KAAK,EAAE,0BAA0B,CAAE,CAAE,MAAO,OAAO,EAAE,CAAE,CAAC,CAG7E,IAAY,EAAmB,EAA+B,CAC5D,IAAM,EAAO,SAAS,cAAc,OAAO,CAG3C,MAFA,GAAK,UAAY,EACjB,EAAK,YAAc,EACZ,EAGT,KAAa,EAA8B,CACzC,IAAM,EAAK,SAAS,cAAc,MAAM,CAGxC,MAFA,GAAG,UAAY,eACf,EAAG,YAAc,EACV,EAIT,KAAa,EAAkC,CACzC,EAAE,aAAkB,cAAgB,CAAC,EAAO,UAAU,SAAS,aAAa,GAChF,KAAK,QAAQ,iBAA8B,4BAA4B,CAAC,QAAS,GAAO,CAClF,IAAO,IAAQ,EAAG,SAAW,KACjC,CACF,EAAO,SAAW,GAGpB,UAAkB,EAAkB,EAA0B,CAC5D,GAAI,EAAE,MAAQ,SAAU,CACtB,EAAE,gBAAgB,CAClB,EAAE,iBAAiB,CACnB,KAAK,OAAO,CACZ,OAEF,EAAA,EAAc,EAAG,EAAM,CAIzB,YAAoB,EAAwB,CAC1C,IAAM,EAAQ,KAAK,QAAQ,cAAiC,cAAc,CACtE,EAAE,MAAQ,aAAe,GAC3B,EAAE,gBAAgB,CAClB,EAAM,OAAO,EACJ,EAAE,MAAQ,SAAW,KAAK,MAAM,MAAM,EAAI,IACnD,EAAE,gBAAgB,CAClB,EAAM,OAAO,EAKjB,SAAiB,EAAwB,CACvC,IAAM,EAAQ,KAAK,OAAO,CACpB,EAAK,EAAM,QAAQ,KAAK,KAAK,CAC7B,EAAO,EAAE,MAAQ,YAAc,KAAK,IAAI,EAAM,OAAS,EAAG,EAAK,EAAE,CACnE,EAAE,MAAQ,UAAY,KAAK,IAAI,EAAG,EAAK,EAAE,CACzC,EAAE,MAAQ,OAAS,EACnB,EAAE,MAAQ,MAAQ,EAAM,OAAS,EACjC,GACA,EAAO,IACX,EAAE,gBAAgB,CAClB,KAAK,OAAO,EAAM,GAAO,GAAK,EAIhC,UAAkB,EAAwB,CACxC,IAAM,EAAS,EAAE,OACX,EAAM,EAAO,QAAQ,4BAA4B,CACjD,EAAO,CAAC,GAAI,KAAK,QAAQ,iBAAoC,cAAc,EAAI,EAAE,CAAE,CACnF,EAAO,GAAK,UAAU,SAAS,cAAc,CAAG,EAA2B,GAAK,cAAiC,cAAc,CAC/H,EAAK,EAAO,EAAK,QAAQ,EAAK,CAAG,GACnC,EAAqC,KACrC,EAAE,MAAQ,YAAa,EAAK,EAAK,EAAK,GACjC,EAAE,MAAQ,UAAW,EAAK,EAAK,EAAI,EAAK,EAAK,GAAK,KAAK,SACvD,EAAE,MAAQ,OAAQ,EAAK,EAAK,GAC5B,EAAE,MAAQ,MAAO,EAAK,EAAK,EAAK,OAAS,GACzC,EAAE,MAAQ,cAAgB,IAAW,EAAM,EAAK,GAAK,cAAiC,gBAAgB,CACtG,EAAE,MAAQ,aAAe,IAAW,IAAM,EAAK,GACnD,IACL,EAAE,gBAAgB,CAClB,EAAG,OAAO"}
@@ -1,4 +1,4 @@
1
- import { o as e } from "./widgetFeatures-BM6u0u74.js";
1
+ import { o as e } from "./widgetFeatures-sU-9JOSB.js";
2
2
  import { i as t, n } from "./focusTrap-Br9Zfje0.js";
3
3
  import { r } from "./i18n-DWLKoOBG.js";
4
4
  import { INDICATOR_CATEGORIES as i } from "@tradecanvas/core";
@@ -242,4 +242,4 @@ var l = 1, u = (e, t) => e.name.localeCompare(t.name), d = class {
242
242
  //#endregion
243
243
  export { d as WidgetIndicatorPicker };
244
244
 
245
- //# sourceMappingURL=WidgetIndicatorPicker-BzV4Mm_W.js.map
245
+ //# sourceMappingURL=WidgetIndicatorPicker-eK03W3LR.js.map