@tradecanvas/chart 1.72.0 → 1.72.1

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 (120) hide show
  1. package/dist/{LayoutSession-r7Rgc5Sw.cjs → LayoutSession-DO4bYkUi.cjs} +2 -2
  2. package/dist/{LayoutSession-r7Rgc5Sw.cjs.map → LayoutSession-DO4bYkUi.cjs.map} +1 -1
  3. package/dist/{LayoutSession-DjHF7tmL.js → LayoutSession-qRYG8GTD.js} +2 -2
  4. package/dist/{LayoutSession-DjHF7tmL.js.map → LayoutSession-qRYG8GTD.js.map} +1 -1
  5. package/dist/WidgetCommandPalette-CeCLHPy1.js +177 -0
  6. package/dist/WidgetCommandPalette-CeCLHPy1.js.map +1 -0
  7. package/dist/WidgetCommandPalette-ZZNbWwCZ.cjs +2 -0
  8. package/dist/WidgetCommandPalette-ZZNbWwCZ.cjs.map +1 -0
  9. package/dist/{WidgetDrawingSettings-MSpeHFhV.js → WidgetDrawingSettings-BeW8fX7r.js} +5 -5
  10. package/dist/{WidgetDrawingSettings-MSpeHFhV.js.map → WidgetDrawingSettings-BeW8fX7r.js.map} +1 -1
  11. package/dist/{WidgetDrawingSettings-B3KAH9Ag.cjs → WidgetDrawingSettings-Cyl2u_u8.cjs} +2 -2
  12. package/dist/{WidgetDrawingSettings-B3KAH9Ag.cjs.map → WidgetDrawingSettings-Cyl2u_u8.cjs.map} +1 -1
  13. package/dist/WidgetGoToDate-BNdngHlg.cjs +2 -0
  14. package/dist/{WidgetGoToDate-CPzfStIm.cjs.map → WidgetGoToDate-BNdngHlg.cjs.map} +1 -1
  15. package/dist/{WidgetGoToDate-R2teTLpJ.js → WidgetGoToDate-hcDtezRm.js} +6 -5
  16. package/dist/{WidgetGoToDate-R2teTLpJ.js.map → WidgetGoToDate-hcDtezRm.js.map} +1 -1
  17. package/dist/{WidgetHotkeySheet-BE0glkVA.js → WidgetHotkeySheet-1lRFSdvt.js} +3 -3
  18. package/dist/{WidgetHotkeySheet-BE0glkVA.js.map → WidgetHotkeySheet-1lRFSdvt.js.map} +1 -1
  19. package/dist/{WidgetHotkeySheet-pnsXreK6.cjs → WidgetHotkeySheet-y5LWKSG6.cjs} +2 -2
  20. package/dist/{WidgetHotkeySheet-pnsXreK6.cjs.map → WidgetHotkeySheet-y5LWKSG6.cjs.map} +1 -1
  21. package/dist/WidgetIndicatorPicker-CGvZ5kYd.js +245 -0
  22. package/dist/WidgetIndicatorPicker-CGvZ5kYd.js.map +1 -0
  23. package/dist/WidgetIndicatorPicker-DuKJFPRC.cjs +2 -0
  24. package/dist/WidgetIndicatorPicker-DuKJFPRC.cjs.map +1 -0
  25. package/dist/{WidgetIndicatorSettings-BJUjACzj.cjs → WidgetIndicatorSettings-C7vpco6e.cjs} +2 -2
  26. package/dist/{WidgetIndicatorSettings-BJUjACzj.cjs.map → WidgetIndicatorSettings-C7vpco6e.cjs.map} +1 -1
  27. package/dist/{WidgetIndicatorSettings-DgbsZk0h.js → WidgetIndicatorSettings-dDkqzzYo.js} +6 -6
  28. package/dist/{WidgetIndicatorSettings-DgbsZk0h.js.map → WidgetIndicatorSettings-dDkqzzYo.js.map} +1 -1
  29. package/dist/{WidgetSettings-Pgt3p6vv.cjs → WidgetSettings-D7Pq42zW.cjs} +2 -2
  30. package/dist/{WidgetSettings-Pgt3p6vv.cjs.map → WidgetSettings-D7Pq42zW.cjs.map} +1 -1
  31. package/dist/{WidgetSettings-D35fHGXL.js → WidgetSettings-eOo93L7Z.js} +4 -4
  32. package/dist/{WidgetSettings-D35fHGXL.js.map → WidgetSettings-eOo93L7Z.js.map} +1 -1
  33. package/dist/WidgetSymbolSearch-C4OR42nZ.cjs +2 -0
  34. package/dist/WidgetSymbolSearch-C4OR42nZ.cjs.map +1 -0
  35. package/dist/WidgetSymbolSearch-CvPpjIwa.js +580 -0
  36. package/dist/WidgetSymbolSearch-CvPpjIwa.js.map +1 -0
  37. package/dist/{focusTrap-Hm7AUTBw.js → focusTrap-Br9Zfje0.js} +1 -1
  38. package/dist/{focusTrap-Hm7AUTBw.js.map → focusTrap-Br9Zfje0.js.map} +1 -1
  39. package/dist/{focusTrap-B8OoGwHb.cjs → focusTrap-DrhhmG0V.cjs} +1 -1
  40. package/dist/{focusTrap-B8OoGwHb.cjs.map → focusTrap-DrhhmG0V.cjs.map} +1 -1
  41. package/dist/{i18n-DFR1Oukq.cjs → i18n-C2nQBZTY.cjs} +1 -1
  42. package/dist/{i18n-DFR1Oukq.cjs.map → i18n-C2nQBZTY.cjs.map} +1 -1
  43. package/dist/{i18n-CvnQcfor.js → i18n-C5v6Tzp1.js} +1 -1
  44. package/dist/{i18n-CvnQcfor.js.map → i18n-C5v6Tzp1.js.map} +1 -1
  45. package/dist/index.cjs +1 -1
  46. package/dist/index.js +1 -1
  47. package/dist/{indicatorInputWords-D19kY9NO.js → indicatorInputWords-Bd9Pt6qp.js} +2 -2
  48. package/dist/{indicatorInputWords-D19kY9NO.js.map → indicatorInputWords-Bd9Pt6qp.js.map} +1 -1
  49. package/dist/{indicatorInputWords-DzrKBU2I.cjs → indicatorInputWords-CTA4IcJR.cjs} +2 -2
  50. package/dist/{indicatorInputWords-DzrKBU2I.cjs.map → indicatorInputWords-CTA4IcJR.cjs.map} +1 -1
  51. package/dist/{indicatorSettingsTabs-BLEULMJJ.cjs → indicatorSettingsTabs-B0liE3ST.cjs} +2 -2
  52. package/dist/{indicatorSettingsTabs-BLEULMJJ.cjs.map → indicatorSettingsTabs-B0liE3ST.cjs.map} +1 -1
  53. package/dist/{indicatorSettingsTabs-Wxx2v_y4.js → indicatorSettingsTabs-XcgMdbLp.js} +3 -3
  54. package/dist/{indicatorSettingsTabs-Wxx2v_y4.js.map → indicatorSettingsTabs-XcgMdbLp.js.map} +1 -1
  55. package/dist/monthView-B-CieFDJ.cjs +2 -0
  56. package/dist/monthView-B-CieFDJ.cjs.map +1 -0
  57. package/dist/monthView-N8yDaTqZ.js +198 -0
  58. package/dist/monthView-N8yDaTqZ.js.map +1 -0
  59. package/dist/{platformKeys-BAPueZ1I.cjs → platformKeys-B82-j1Fs.cjs} +1 -1
  60. package/dist/{platformKeys-BAPueZ1I.cjs.map → platformKeys-B82-j1Fs.cjs.map} +1 -1
  61. package/dist/{platformKeys-D_x_6ZrV.js → platformKeys-Dd1qOCjF.js} +1 -1
  62. package/dist/{platformKeys-D_x_6ZrV.js.map → platformKeys-Dd1qOCjF.js.map} +1 -1
  63. package/dist/{settingsControls-ef4j_clQ.js → settingsControls-DIjzCFHI.js} +2 -2
  64. package/dist/{settingsControls-ef4j_clQ.js.map → settingsControls-DIjzCFHI.js.map} +1 -1
  65. package/dist/{settingsControls-jNn0If4b.cjs → settingsControls-Tn8_vJJv.cjs} +2 -2
  66. package/dist/{settingsControls-jNn0If4b.cjs.map → settingsControls-Tn8_vJJv.cjs.map} +1 -1
  67. package/dist/symbolCatalog-CA1fpz3s.js +90 -0
  68. package/dist/symbolCatalog-CA1fpz3s.js.map +1 -0
  69. package/dist/symbolCatalog-Dx1YnZgm.cjs +2 -0
  70. package/dist/symbolCatalog-Dx1YnZgm.cjs.map +1 -0
  71. package/dist/widget/ChartWidget.d.ts +67 -579
  72. package/dist/widget/ChartWidget.d.ts.map +1 -1
  73. package/dist/widget/WidgetAlerts.d.ts +35 -0
  74. package/dist/widget/WidgetAlerts.d.ts.map +1 -0
  75. package/dist/widget/WidgetChartSettings.d.ts +194 -0
  76. package/dist/widget/WidgetChartSettings.d.ts.map +1 -0
  77. package/dist/widget/WidgetCommands.d.ts +54 -0
  78. package/dist/widget/WidgetCommands.d.ts.map +1 -0
  79. package/dist/widget/WidgetCompares.d.ts +140 -0
  80. package/dist/widget/WidgetCompares.d.ts.map +1 -0
  81. package/dist/widget/WidgetDockArea.d.ts +89 -0
  82. package/dist/widget/WidgetDockArea.d.ts.map +1 -0
  83. package/dist/widget/WidgetDrawingEdit.d.ts +73 -0
  84. package/dist/widget/WidgetDrawingEdit.d.ts.map +1 -0
  85. package/dist/widget/WidgetIntervals.d.ts +66 -0
  86. package/dist/widget/WidgetIntervals.d.ts.map +1 -0
  87. package/dist/widget/WidgetLayouts.d.ts +85 -0
  88. package/dist/widget/WidgetLayouts.d.ts.map +1 -0
  89. package/dist/widget/WidgetLegendView.d.ts +54 -0
  90. package/dist/widget/WidgetLegendView.d.ts.map +1 -0
  91. package/dist/widget/WidgetMenus.d.ts +79 -0
  92. package/dist/widget/WidgetMenus.d.ts.map +1 -0
  93. package/dist/widget/WidgetReplay.d.ts +199 -0
  94. package/dist/widget/WidgetReplay.d.ts.map +1 -0
  95. package/dist/widget/WidgetTrading.d.ts +48 -0
  96. package/dist/widget/WidgetTrading.d.ts.map +1 -0
  97. package/dist/widget/WidgetWatchlists.d.ts +77 -0
  98. package/dist/widget/WidgetWatchlists.d.ts.map +1 -0
  99. package/dist/widget/index.cjs +21 -21
  100. package/dist/widget/index.cjs.map +1 -1
  101. package/dist/widget/index.js +6779 -7050
  102. package/dist/widget/index.js.map +1 -1
  103. package/dist/widget/keyTarget.d.ts +4 -0
  104. package/dist/widget/keyTarget.d.ts.map +1 -1
  105. package/dist/widget/locales/index.cjs +1 -1
  106. package/dist/widget/locales/index.js +1 -1
  107. package/dist/widget/pageEnv.d.ts +5 -0
  108. package/dist/widget/pageEnv.d.ts.map +1 -0
  109. package/dist/widget/widgetDialogs.d.ts +3 -0
  110. package/dist/widget/widgetDialogs.d.ts.map +1 -1
  111. package/dist/widgetFeatures-BM6u0u74.js +186 -0
  112. package/dist/widgetFeatures-BM6u0u74.js.map +1 -0
  113. package/dist/widgetFeatures-BZ8OVPN2.cjs +3 -0
  114. package/dist/widgetFeatures-BZ8OVPN2.cjs.map +1 -0
  115. package/package.json +3 -3
  116. package/dist/WidgetGoToDate-CPzfStIm.cjs +0 -2
  117. package/dist/widgetFeatures-BvkTbsQn.js +0 -378
  118. package/dist/widgetFeatures-BvkTbsQn.js.map +0 -1
  119. package/dist/widgetFeatures-Cy0fs3Nl.cjs +0 -3
  120. package/dist/widgetFeatures-Cy0fs3Nl.cjs.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"WidgetGoToDate-R2teTLpJ.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-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,5 +1,5 @@
1
- import { i as e, n as t } from "./focusTrap-Hm7AUTBw.js";
2
- import { n, t as r } from "./platformKeys-D_x_6ZrV.js";
1
+ import { i as e, n as t } from "./focusTrap-Br9Zfje0.js";
2
+ import { n, t as r } from "./platformKeys-Dd1qOCjF.js";
3
3
  //#region src/widget/WidgetHotkeySheet.ts
4
4
  var i = 229, a = {
5
5
  "Search & navigation": "hotkeys.group.searchNavigation",
@@ -413,4 +413,4 @@ var i = 229, a = {
413
413
  //#endregion
414
414
  export { l as WidgetHotkeySheet };
415
415
 
416
- //# sourceMappingURL=WidgetHotkeySheet-BE0glkVA.js.map
416
+ //# sourceMappingURL=WidgetHotkeySheet-1lRFSdvt.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"WidgetHotkeySheet-BE0glkVA.js","names":[],"sources":["../src/widget/WidgetHotkeySheet.ts"],"sourcesContent":["import type { MessageKey, Translator } from './i18n.js';\nimport type { WidgetFeature } from './widgetFeatures.js';\nimport { createIcon } from './icons.js';\nimport { keepTabInside } from './focusTrap.js';\nimport { MOD_KEY, searchForm } from './platformKeys.js';\n\n/**\n * Keyboard-shortcut reference sheet. Bound to `?` (and `Shift+/`). Surfaces\n * shortcuts so users discover the widget's hidden interaction model — a\n * premium-feel detail polished desktop tools all ship.\n */\nexport interface HotkeySheetCallbacks {\n onClose: () => void;\n}\n\n/** The key code a browser gives a key an input method takes. */\nconst IME_KEY_CODE = 229;\n\n// Group titles translate via this lookup; individual shortcut labels (and\n// the `keys` badges) stay English for now — see README \"Widget i18n\".\nconst GROUP_TITLE_KEYS: Record<string, MessageKey> = {\n 'Search & navigation': 'hotkeys.group.searchNavigation',\n 'Chart manipulation': 'hotkeys.group.chartManipulation',\n 'Touch (mobile / tablet)': 'hotkeys.group.touch',\n 'Keyboard': 'hotkeys.group.keyboard',\n 'Bar replay': 'toolbar.replay',\n 'Drawing': 'hotkeys.group.drawing',\n 'More': 'hotkeys.group.more',\n};\n\ninterface HotkeyEntry {\n keys: string[];\n label: string;\n /** The switch it goes with: left out of the sheet while that is off. */\n feature?: WidgetFeature;\n}\n\n/** A shortcut of the host's, shown as it was given. */\nexport interface HostHotkeyEntry {\n keys: string[];\n text: string;\n}\n\n/** What else the sheet shows: the switches that are on, and the host's shortcuts. */\nexport interface HotkeySheetOptions {\n isOn?: (feature: WidgetFeature) => boolean;\n extra?: readonly HostHotkeyEntry[];\n}\n\ninterface HotkeyGroup {\n title: string;\n entries: HotkeyEntry[];\n}\n\nconst mod = MOD_KEY;\n\nconst GROUPS: HotkeyGroup[] = [\n {\n title: 'Search & navigation',\n entries: [\n { keys: [mod, 'K'], label: 'hotkeys.commandPalette', feature: 'hotkeys.commandPalette' },\n { keys: [mod, 'P'], label: 'hotkeys.symbolSearch', feature: 'hotkeys.symbolSearch' },\n { keys: ['Alt', 'G'], label: 'hotkeys.goToDate', feature: 'hotkeys.goToDate' },\n { keys: ['Alt', 'I'], label: 'hotkeys.invertScale', feature: 'hotkeys.invertScale' },\n { keys: ['Alt', 'R'], label: 'chartMenu.resetView', feature: 'hotkeys.resetView' },\n { keys: [mod, 'S'], label: 'hotkeys.saveLayout', feature: 'hotkeys.save' },\n { keys: ['0–9'], label: 'hotkeys.typeInterval', feature: 'intervalTyping' },\n { keys: ['A–Z'], label: 'hotkeys.typeSymbol', feature: 'symbolTyping' },\n { keys: ['?'], label: 'hotkeys.showSheet', feature: 'hotkeys.help' },\n ],\n },\n {\n title: 'Chart manipulation',\n entries: [\n { keys: ['key.drag'], label: 'hotkeys.pan' },\n { keys: [mod, 'key.drag'], label: 'hotkeys.boxSelect' },\n { keys: [mod, 'key.click'], label: 'hotkeys.toggleSelect' },\n { keys: ['Shift', 'key.drag'], label: 'hotkeys.measure' },\n { keys: ['key.drag', 'key.priceAxis'], label: 'hotkeys.scalePrice' },\n { keys: ['key.drag', 'key.timeAxis'], label: 'hotkeys.zoomTime' },\n { keys: ['key.doubleClick', 'key.priceAxis'], label: 'hotkeys.autoScale' },\n { keys: ['key.doubleClick', 'key.timeAxis'], label: 'hotkeys.fit' },\n { keys: ['key.scroll'], label: 'hotkeys.zoomCursor' },\n ],\n },\n {\n title: 'Touch (mobile / tablet)',\n entries: [\n { keys: ['key.oneFinger', 'key.drag'], label: 'hotkeys.touchPan' },\n { keys: ['key.twoFingers', 'key.pinch'], label: 'hotkeys.pinch' },\n { keys: ['key.longPress'], label: 'hotkeys.longPress' },\n { keys: ['key.drag', 'key.axisStrip'], label: 'hotkeys.touchAxis' },\n ],\n },\n {\n title: 'Keyboard',\n entries: [\n { keys: ['←', '→'], label: 'hotkeys.panBar' },\n { keys: ['+', '−'], label: 'hotkeys.zoomInOut' },\n { keys: ['Home'], label: 'hotkeys.toStart' },\n { keys: ['End'], label: 'hotkeys.toEnd' },\n { keys: ['Space'], label: 'hotkeys.fit' },\n ],\n },\n {\n title: 'Bar replay',\n entries: [\n { keys: ['Shift', '←', '→'], label: 'hotkeys.replayStep', feature: 'replay' },\n // Outside a replay, Space fits the chart (above).\n { keys: ['Space'], label: 'hotkeys.replayPlay', feature: 'replay' },\n { keys: ['Esc'], label: 'hotkeys.replayCancel', feature: 'replay' },\n ],\n },\n {\n title: 'Drawing',\n entries: [\n { keys: [mod, 'Z'], label: 'hotkeys.undo' },\n { keys: [mod, 'Shift', 'Z'], label: 'hotkeys.redo' },\n { keys: ['Esc'], label: 'hotkeys.cancelDrawing' },\n { keys: [mod, 'C'], label: 'hotkeys.copy' },\n { keys: [mod, 'V'], label: 'hotkeys.paste' },\n { keys: ['key.rightClick'], label: 'hotkeys.drawingMenu' },\n { keys: ['key.doubleClick'], label: 'hotkeys.drawingSettings' },\n { keys: [mod, 'G'], label: 'hotkeys.group' },\n { keys: [mod, 'Shift', 'G'], label: 'hotkeys.ungroup' },\n { keys: [mod, ']', '['], label: 'hotkeys.orderStep' },\n { keys: [mod, 'Shift', ']', '['], label: 'hotkeys.orderEnd' },\n { keys: ['Enter'], label: 'hotkeys.finishPath' },\n { keys: [mod, 'key.drag'], label: 'hotkeys.copyDrag' },\n { keys: ['Shift'], label: 'hotkeys.keepAngle' },\n { keys: [mod], label: 'hotkeys.flipMagnet' },\n { keys: ['←', '→', '↑', '↓'], label: 'hotkeys.nudge' },\n { keys: ['Alt', 'T'], label: 'tool.trendLine', feature: 'hotkeys.tools' },\n { keys: ['Alt', 'H'], label: 'tool.horizontalLine', feature: 'hotkeys.tools' },\n { keys: ['Alt', 'J'], label: 'tool.horizontalRay', feature: 'hotkeys.tools' },\n { keys: ['Alt', 'V'], label: 'tool.verticalLine', feature: 'hotkeys.tools' },\n { keys: ['Alt', 'C'], label: 'tool.crossLine', feature: 'hotkeys.tools' },\n { keys: ['Alt', 'F'], label: 'tool.fibRetracement', feature: 'hotkeys.tools' },\n ],\n },\n];\n\nlet sheetSeq = 0;\n\n/**\n * The shortcut sheet: what each key does, by group, searchable by the words\n * or by the keys (\"alt+g\"). A gesture (drag, double-click) is written as\n * words, a key as a key. The keys switched off are left out; the host's\n * shortcuts come last, in place of a built-in one on the same keys.\n */\nexport class WidgetHotkeySheet {\n private backdrop: HTMLDivElement | null = null;\n private bodyEl: HTMLDivElement | null = null;\n private searchEl: HTMLInputElement | null = null;\n /** Says \"No shortcuts match\" to a screen reader. */\n private liveEl: HTMLDivElement | null = null;\n private groups: { title: string; entries: (HotkeyEntry | HostHotkeyEntry)[] }[] = [];\n private returnFocus: HTMLElement | null = null;\n private readonly uid = ++sheetSeq;\n\n /** Where the overlay mounts (the widget's portal: themed, and inside it when fullscreen). */\n constructor(\n private readonly callbacks: HotkeySheetCallbacks,\n private readonly t: Translator,\n private readonly host: () => HTMLElement = () => document.body,\n ) {}\n\n open(options: HotkeySheetOptions = {}): void {\n if (this.backdrop) return;\n this.returnFocus = document.activeElement instanceof HTMLElement ? document.activeElement : null;\n const isOn = options.isOn ?? (() => true);\n // The widget's own, less those switched off (a whole group goes when all its keys do).\n // A host's shortcut on the same keys replaces the built-in one.\n const replaced = new Set((options.extra ?? []).map((e) => e.keys.join('+').toLowerCase()));\n this.groups = GROUPS\n .map((group) => ({\n title: group.title,\n entries: group.entries.filter((e) => (!e.feature || isOn(e.feature)) && !replaced.has(e.keys.join('+').toLowerCase())) as (HotkeyEntry | HostHotkeyEntry)[],\n }))\n .filter((group) => group.entries.length > 0);\n if (options.extra && options.extra.length > 0) this.groups.push({ title: 'More', entries: [...options.extra] });\n this.build(isOn('hotkeys.help'));\n this.render();\n this.searchEl?.focus();\n }\n\n close(): void {\n if (!this.backdrop) return;\n this.backdrop.remove();\n this.backdrop = this.bodyEl = this.searchEl = this.liveEl = null;\n this.callbacks.onClose();\n const back = this.returnFocus;\n this.returnFocus = null;\n if (back?.isConnected) back.focus();\n }\n\n isOpen(): boolean {\n return this.backdrop !== null;\n }\n\n destroy(): void {\n this.close();\n }\n\n /** The sheet; `withFoot` says how to open it again (while its key is on). */\n private build(withFoot: boolean): void {\n const backdrop = document.createElement('div');\n backdrop.className = 'tcw-modal-backdrop';\n // A press that starts in the sheet 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-hotkey-sheet';\n modal.setAttribute('role', 'dialog');\n modal.setAttribute('aria-modal', 'true');\n modal.setAttribute('aria-labelledby', `tcw-hotkey-title-${this.uid}`);\n modal.tabIndex = -1;\n modal.addEventListener('keydown', (e) => {\n e.stopPropagation(); // keys here must not drive chart shortcuts\n // An input method's Escape ends what it composes; the sheet stays.\n if (e.key === 'Escape' && !e.isComposing && e.keyCode !== IME_KEY_CODE) {\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-hotkey-title-${this.uid}`;\n title.textContent = this.t('hotkeys.title');\n const closeBtn = document.createElement('button');\n closeBtn.type = 'button';\n closeBtn.className = 'tcw-modal-close';\n closeBtn.setAttribute('aria-label', this.t('hotkeys.close'));\n closeBtn.innerHTML = createIcon('x', 16);\n closeBtn.addEventListener('click', () => this.close());\n header.append(title, closeBtn);\n\n const search = document.createElement('label');\n search.className = 'tcw-ss-search tcw-hotkey-search';\n search.innerHTML = createIcon('search', 16);\n const input = document.createElement('input');\n input.type = 'search';\n input.placeholder = this.t('hotkeys.search');\n input.setAttribute('aria-label', this.t('hotkeys.search'));\n input.autocomplete = 'off';\n input.spellcheck = false;\n input.addEventListener('input', () => this.render());\n search.appendChild(input);\n\n const body = document.createElement('div');\n body.className = 'tcw-hotkey-body';\n\n const live = document.createElement('div');\n live.className = 'tcw-hotkey-live tcw-visually-hidden';\n live.setAttribute('aria-live', 'polite');\n\n modal.append(header, search, body, live);\n if (withFoot) {\n const foot = document.createElement('div');\n foot.className = 'tcw-hotkey-foot';\n foot.textContent = this.t('hotkeys.footer');\n modal.appendChild(foot);\n }\n backdrop.appendChild(modal);\n this.host().appendChild(backdrop);\n this.backdrop = backdrop;\n this.bodyEl = body;\n this.searchEl = input;\n this.liveEl = live;\n }\n\n /** The groups and their shortcuts that match what is typed. */\n private render(): void {\n const body = this.bodyEl;\n if (!body) return;\n const query = searchForm((this.searchEl?.value ?? '').trim());\n const sections: HTMLElement[] = [];\n for (const group of this.groups) {\n const entries = query ? group.entries.filter((e) => this.matches(e, query)) : group.entries;\n if (entries.length === 0) continue;\n const section = document.createElement('section');\n section.className = 'tcw-hotkey-group';\n const heading = document.createElement('div');\n heading.className = 'tcw-hotkey-heading';\n const titleKey = GROUP_TITLE_KEYS[group.title];\n heading.textContent = titleKey ? this.t(titleKey) : group.title;\n section.append(heading, ...entries.map((entry) => this.row(entry)));\n sections.push(section);\n }\n if (this.liveEl) this.liveEl.textContent = sections.length === 0 ? this.t('hotkeys.noMatch') : '';\n if (sections.length === 0) {\n const empty = document.createElement('div');\n empty.className = 'tcw-hotkey-empty';\n empty.textContent = this.t('hotkeys.noMatch');\n body.replaceChildren(empty);\n return;\n }\n // In columns inside the box that scrolls, as tall as they are: columns on a box of a fixed height\n // lay what doesn't fit in more columns to the side, out of sight and out of reach of the scroll.\n const columns = document.createElement('div');\n columns.className = 'tcw-hotkey-columns';\n columns.append(...sections);\n body.replaceChildren(columns);\n }\n\n private label(entry: HotkeyEntry | HostHotkeyEntry): string {\n return 'text' in entry ? entry.text : this.t(entry.label as MessageKey);\n }\n\n /** A key's words: its own, or a gesture's in the widget's language. */\n private keyText(key: string): string {\n return key.startsWith('key.') ? this.t(key as MessageKey) : key;\n }\n\n /** What it does, or its keys written as \"alt+g\", holds the query. */\n private matches(entry: HotkeyEntry | HostHotkeyEntry, query: string): boolean {\n if (searchForm(this.label(entry)).includes(query)) return true;\n return searchForm(entry.keys.map((k) => this.keyText(k)).join('+')).includes(query);\n }\n\n /** What it does, then its keys: a key as a key, a gesture as words. */\n private row(entry: HotkeyEntry | HostHotkeyEntry): HTMLDivElement {\n const row = document.createElement('div');\n row.className = 'tcw-hotkey-row';\n const label = document.createElement('span');\n label.className = 'tcw-hotkey-label';\n label.textContent = this.label(entry);\n const keys = document.createElement('span');\n keys.className = 'tcw-hotkey-keys';\n // A combination reads left to right in any widget: Ctrl + K, never K + Ctrl.\n keys.dir = 'ltr';\n entry.keys.forEach((key, i) => {\n if (i > 0) {\n const plus = document.createElement('span');\n plus.className = 'tcw-hotkey-sep';\n plus.textContent = '+';\n keys.appendChild(plus);\n }\n const gesture = key.startsWith('key.');\n const el = document.createElement(gesture ? 'span' : 'kbd');\n el.className = gesture ? 'tcw-hotkey-gesture' : 'tcw-hotkey-key';\n el.textContent = this.keyText(key);\n keys.appendChild(el);\n });\n row.append(label, keys);\n return row;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"WidgetHotkeySheet-1lRFSdvt.js","names":[],"sources":["../src/widget/WidgetHotkeySheet.ts"],"sourcesContent":["import type { MessageKey, Translator } from './i18n.js';\nimport type { WidgetFeature } from './widgetFeatures.js';\nimport { createIcon } from './icons.js';\nimport { keepTabInside } from './focusTrap.js';\nimport { MOD_KEY, searchForm } from './platformKeys.js';\n\n/**\n * Keyboard-shortcut reference sheet. Bound to `?` (and `Shift+/`). Surfaces\n * shortcuts so users discover the widget's hidden interaction model — a\n * premium-feel detail polished desktop tools all ship.\n */\nexport interface HotkeySheetCallbacks {\n onClose: () => void;\n}\n\n/** The key code a browser gives a key an input method takes. */\nconst IME_KEY_CODE = 229;\n\n// Group titles translate via this lookup; individual shortcut labels (and\n// the `keys` badges) stay English for now — see README \"Widget i18n\".\nconst GROUP_TITLE_KEYS: Record<string, MessageKey> = {\n 'Search & navigation': 'hotkeys.group.searchNavigation',\n 'Chart manipulation': 'hotkeys.group.chartManipulation',\n 'Touch (mobile / tablet)': 'hotkeys.group.touch',\n 'Keyboard': 'hotkeys.group.keyboard',\n 'Bar replay': 'toolbar.replay',\n 'Drawing': 'hotkeys.group.drawing',\n 'More': 'hotkeys.group.more',\n};\n\ninterface HotkeyEntry {\n keys: string[];\n label: string;\n /** The switch it goes with: left out of the sheet while that is off. */\n feature?: WidgetFeature;\n}\n\n/** A shortcut of the host's, shown as it was given. */\nexport interface HostHotkeyEntry {\n keys: string[];\n text: string;\n}\n\n/** What else the sheet shows: the switches that are on, and the host's shortcuts. */\nexport interface HotkeySheetOptions {\n isOn?: (feature: WidgetFeature) => boolean;\n extra?: readonly HostHotkeyEntry[];\n}\n\ninterface HotkeyGroup {\n title: string;\n entries: HotkeyEntry[];\n}\n\nconst mod = MOD_KEY;\n\nconst GROUPS: HotkeyGroup[] = [\n {\n title: 'Search & navigation',\n entries: [\n { keys: [mod, 'K'], label: 'hotkeys.commandPalette', feature: 'hotkeys.commandPalette' },\n { keys: [mod, 'P'], label: 'hotkeys.symbolSearch', feature: 'hotkeys.symbolSearch' },\n { keys: ['Alt', 'G'], label: 'hotkeys.goToDate', feature: 'hotkeys.goToDate' },\n { keys: ['Alt', 'I'], label: 'hotkeys.invertScale', feature: 'hotkeys.invertScale' },\n { keys: ['Alt', 'R'], label: 'chartMenu.resetView', feature: 'hotkeys.resetView' },\n { keys: [mod, 'S'], label: 'hotkeys.saveLayout', feature: 'hotkeys.save' },\n { keys: ['0–9'], label: 'hotkeys.typeInterval', feature: 'intervalTyping' },\n { keys: ['A–Z'], label: 'hotkeys.typeSymbol', feature: 'symbolTyping' },\n { keys: ['?'], label: 'hotkeys.showSheet', feature: 'hotkeys.help' },\n ],\n },\n {\n title: 'Chart manipulation',\n entries: [\n { keys: ['key.drag'], label: 'hotkeys.pan' },\n { keys: [mod, 'key.drag'], label: 'hotkeys.boxSelect' },\n { keys: [mod, 'key.click'], label: 'hotkeys.toggleSelect' },\n { keys: ['Shift', 'key.drag'], label: 'hotkeys.measure' },\n { keys: ['key.drag', 'key.priceAxis'], label: 'hotkeys.scalePrice' },\n { keys: ['key.drag', 'key.timeAxis'], label: 'hotkeys.zoomTime' },\n { keys: ['key.doubleClick', 'key.priceAxis'], label: 'hotkeys.autoScale' },\n { keys: ['key.doubleClick', 'key.timeAxis'], label: 'hotkeys.fit' },\n { keys: ['key.scroll'], label: 'hotkeys.zoomCursor' },\n ],\n },\n {\n title: 'Touch (mobile / tablet)',\n entries: [\n { keys: ['key.oneFinger', 'key.drag'], label: 'hotkeys.touchPan' },\n { keys: ['key.twoFingers', 'key.pinch'], label: 'hotkeys.pinch' },\n { keys: ['key.longPress'], label: 'hotkeys.longPress' },\n { keys: ['key.drag', 'key.axisStrip'], label: 'hotkeys.touchAxis' },\n ],\n },\n {\n title: 'Keyboard',\n entries: [\n { keys: ['←', '→'], label: 'hotkeys.panBar' },\n { keys: ['+', '−'], label: 'hotkeys.zoomInOut' },\n { keys: ['Home'], label: 'hotkeys.toStart' },\n { keys: ['End'], label: 'hotkeys.toEnd' },\n { keys: ['Space'], label: 'hotkeys.fit' },\n ],\n },\n {\n title: 'Bar replay',\n entries: [\n { keys: ['Shift', '←', '→'], label: 'hotkeys.replayStep', feature: 'replay' },\n // Outside a replay, Space fits the chart (above).\n { keys: ['Space'], label: 'hotkeys.replayPlay', feature: 'replay' },\n { keys: ['Esc'], label: 'hotkeys.replayCancel', feature: 'replay' },\n ],\n },\n {\n title: 'Drawing',\n entries: [\n { keys: [mod, 'Z'], label: 'hotkeys.undo' },\n { keys: [mod, 'Shift', 'Z'], label: 'hotkeys.redo' },\n { keys: ['Esc'], label: 'hotkeys.cancelDrawing' },\n { keys: [mod, 'C'], label: 'hotkeys.copy' },\n { keys: [mod, 'V'], label: 'hotkeys.paste' },\n { keys: ['key.rightClick'], label: 'hotkeys.drawingMenu' },\n { keys: ['key.doubleClick'], label: 'hotkeys.drawingSettings' },\n { keys: [mod, 'G'], label: 'hotkeys.group' },\n { keys: [mod, 'Shift', 'G'], label: 'hotkeys.ungroup' },\n { keys: [mod, ']', '['], label: 'hotkeys.orderStep' },\n { keys: [mod, 'Shift', ']', '['], label: 'hotkeys.orderEnd' },\n { keys: ['Enter'], label: 'hotkeys.finishPath' },\n { keys: [mod, 'key.drag'], label: 'hotkeys.copyDrag' },\n { keys: ['Shift'], label: 'hotkeys.keepAngle' },\n { keys: [mod], label: 'hotkeys.flipMagnet' },\n { keys: ['←', '→', '↑', '↓'], label: 'hotkeys.nudge' },\n { keys: ['Alt', 'T'], label: 'tool.trendLine', feature: 'hotkeys.tools' },\n { keys: ['Alt', 'H'], label: 'tool.horizontalLine', feature: 'hotkeys.tools' },\n { keys: ['Alt', 'J'], label: 'tool.horizontalRay', feature: 'hotkeys.tools' },\n { keys: ['Alt', 'V'], label: 'tool.verticalLine', feature: 'hotkeys.tools' },\n { keys: ['Alt', 'C'], label: 'tool.crossLine', feature: 'hotkeys.tools' },\n { keys: ['Alt', 'F'], label: 'tool.fibRetracement', feature: 'hotkeys.tools' },\n ],\n },\n];\n\nlet sheetSeq = 0;\n\n/**\n * The shortcut sheet: what each key does, by group, searchable by the words\n * or by the keys (\"alt+g\"). A gesture (drag, double-click) is written as\n * words, a key as a key. The keys switched off are left out; the host's\n * shortcuts come last, in place of a built-in one on the same keys.\n */\nexport class WidgetHotkeySheet {\n private backdrop: HTMLDivElement | null = null;\n private bodyEl: HTMLDivElement | null = null;\n private searchEl: HTMLInputElement | null = null;\n /** Says \"No shortcuts match\" to a screen reader. */\n private liveEl: HTMLDivElement | null = null;\n private groups: { title: string; entries: (HotkeyEntry | HostHotkeyEntry)[] }[] = [];\n private returnFocus: HTMLElement | null = null;\n private readonly uid = ++sheetSeq;\n\n /** Where the overlay mounts (the widget's portal: themed, and inside it when fullscreen). */\n constructor(\n private readonly callbacks: HotkeySheetCallbacks,\n private readonly t: Translator,\n private readonly host: () => HTMLElement = () => document.body,\n ) {}\n\n open(options: HotkeySheetOptions = {}): void {\n if (this.backdrop) return;\n this.returnFocus = document.activeElement instanceof HTMLElement ? document.activeElement : null;\n const isOn = options.isOn ?? (() => true);\n // The widget's own, less those switched off (a whole group goes when all its keys do).\n // A host's shortcut on the same keys replaces the built-in one.\n const replaced = new Set((options.extra ?? []).map((e) => e.keys.join('+').toLowerCase()));\n this.groups = GROUPS\n .map((group) => ({\n title: group.title,\n entries: group.entries.filter((e) => (!e.feature || isOn(e.feature)) && !replaced.has(e.keys.join('+').toLowerCase())) as (HotkeyEntry | HostHotkeyEntry)[],\n }))\n .filter((group) => group.entries.length > 0);\n if (options.extra && options.extra.length > 0) this.groups.push({ title: 'More', entries: [...options.extra] });\n this.build(isOn('hotkeys.help'));\n this.render();\n this.searchEl?.focus();\n }\n\n close(): void {\n if (!this.backdrop) return;\n this.backdrop.remove();\n this.backdrop = this.bodyEl = this.searchEl = this.liveEl = null;\n this.callbacks.onClose();\n const back = this.returnFocus;\n this.returnFocus = null;\n if (back?.isConnected) back.focus();\n }\n\n isOpen(): boolean {\n return this.backdrop !== null;\n }\n\n destroy(): void {\n this.close();\n }\n\n /** The sheet; `withFoot` says how to open it again (while its key is on). */\n private build(withFoot: boolean): void {\n const backdrop = document.createElement('div');\n backdrop.className = 'tcw-modal-backdrop';\n // A press that starts in the sheet 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-hotkey-sheet';\n modal.setAttribute('role', 'dialog');\n modal.setAttribute('aria-modal', 'true');\n modal.setAttribute('aria-labelledby', `tcw-hotkey-title-${this.uid}`);\n modal.tabIndex = -1;\n modal.addEventListener('keydown', (e) => {\n e.stopPropagation(); // keys here must not drive chart shortcuts\n // An input method's Escape ends what it composes; the sheet stays.\n if (e.key === 'Escape' && !e.isComposing && e.keyCode !== IME_KEY_CODE) {\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-hotkey-title-${this.uid}`;\n title.textContent = this.t('hotkeys.title');\n const closeBtn = document.createElement('button');\n closeBtn.type = 'button';\n closeBtn.className = 'tcw-modal-close';\n closeBtn.setAttribute('aria-label', this.t('hotkeys.close'));\n closeBtn.innerHTML = createIcon('x', 16);\n closeBtn.addEventListener('click', () => this.close());\n header.append(title, closeBtn);\n\n const search = document.createElement('label');\n search.className = 'tcw-ss-search tcw-hotkey-search';\n search.innerHTML = createIcon('search', 16);\n const input = document.createElement('input');\n input.type = 'search';\n input.placeholder = this.t('hotkeys.search');\n input.setAttribute('aria-label', this.t('hotkeys.search'));\n input.autocomplete = 'off';\n input.spellcheck = false;\n input.addEventListener('input', () => this.render());\n search.appendChild(input);\n\n const body = document.createElement('div');\n body.className = 'tcw-hotkey-body';\n\n const live = document.createElement('div');\n live.className = 'tcw-hotkey-live tcw-visually-hidden';\n live.setAttribute('aria-live', 'polite');\n\n modal.append(header, search, body, live);\n if (withFoot) {\n const foot = document.createElement('div');\n foot.className = 'tcw-hotkey-foot';\n foot.textContent = this.t('hotkeys.footer');\n modal.appendChild(foot);\n }\n backdrop.appendChild(modal);\n this.host().appendChild(backdrop);\n this.backdrop = backdrop;\n this.bodyEl = body;\n this.searchEl = input;\n this.liveEl = live;\n }\n\n /** The groups and their shortcuts that match what is typed. */\n private render(): void {\n const body = this.bodyEl;\n if (!body) return;\n const query = searchForm((this.searchEl?.value ?? '').trim());\n const sections: HTMLElement[] = [];\n for (const group of this.groups) {\n const entries = query ? group.entries.filter((e) => this.matches(e, query)) : group.entries;\n if (entries.length === 0) continue;\n const section = document.createElement('section');\n section.className = 'tcw-hotkey-group';\n const heading = document.createElement('div');\n heading.className = 'tcw-hotkey-heading';\n const titleKey = GROUP_TITLE_KEYS[group.title];\n heading.textContent = titleKey ? this.t(titleKey) : group.title;\n section.append(heading, ...entries.map((entry) => this.row(entry)));\n sections.push(section);\n }\n if (this.liveEl) this.liveEl.textContent = sections.length === 0 ? this.t('hotkeys.noMatch') : '';\n if (sections.length === 0) {\n const empty = document.createElement('div');\n empty.className = 'tcw-hotkey-empty';\n empty.textContent = this.t('hotkeys.noMatch');\n body.replaceChildren(empty);\n return;\n }\n // In columns inside the box that scrolls, as tall as they are: columns on a box of a fixed height\n // lay what doesn't fit in more columns to the side, out of sight and out of reach of the scroll.\n const columns = document.createElement('div');\n columns.className = 'tcw-hotkey-columns';\n columns.append(...sections);\n body.replaceChildren(columns);\n }\n\n private label(entry: HotkeyEntry | HostHotkeyEntry): string {\n return 'text' in entry ? entry.text : this.t(entry.label as MessageKey);\n }\n\n /** A key's words: its own, or a gesture's in the widget's language. */\n private keyText(key: string): string {\n return key.startsWith('key.') ? this.t(key as MessageKey) : key;\n }\n\n /** What it does, or its keys written as \"alt+g\", holds the query. */\n private matches(entry: HotkeyEntry | HostHotkeyEntry, query: string): boolean {\n if (searchForm(this.label(entry)).includes(query)) return true;\n return searchForm(entry.keys.map((k) => this.keyText(k)).join('+')).includes(query);\n }\n\n /** What it does, then its keys: a key as a key, a gesture as words. */\n private row(entry: HotkeyEntry | HostHotkeyEntry): HTMLDivElement {\n const row = document.createElement('div');\n row.className = 'tcw-hotkey-row';\n const label = document.createElement('span');\n label.className = 'tcw-hotkey-label';\n label.textContent = this.label(entry);\n const keys = document.createElement('span');\n keys.className = 'tcw-hotkey-keys';\n // A combination reads left to right in any widget: Ctrl + K, never K + Ctrl.\n keys.dir = 'ltr';\n entry.keys.forEach((key, i) => {\n if (i > 0) {\n const plus = document.createElement('span');\n plus.className = 'tcw-hotkey-sep';\n plus.textContent = '+';\n keys.appendChild(plus);\n }\n const gesture = key.startsWith('key.');\n const el = document.createElement(gesture ? 'span' : 'kbd');\n el.className = gesture ? 'tcw-hotkey-gesture' : 'tcw-hotkey-key';\n el.textContent = this.keyText(key);\n keys.appendChild(el);\n });\n row.append(label, keys);\n return row;\n }\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- const e=require(`./focusTrap-B8OoGwHb.cjs`),t=require(`./platformKeys-BAPueZ1I.cjs`);var n=229,r={"Search & navigation":`hotkeys.group.searchNavigation`,"Chart manipulation":`hotkeys.group.chartManipulation`,"Touch (mobile / tablet)":`hotkeys.group.touch`,Keyboard:`hotkeys.group.keyboard`,"Bar replay":`toolbar.replay`,Drawing:`hotkeys.group.drawing`,More:`hotkeys.group.more`},i=t.t,a=[{title:`Search & navigation`,entries:[{keys:[i,`K`],label:`hotkeys.commandPalette`,feature:`hotkeys.commandPalette`},{keys:[i,`P`],label:`hotkeys.symbolSearch`,feature:`hotkeys.symbolSearch`},{keys:[`Alt`,`G`],label:`hotkeys.goToDate`,feature:`hotkeys.goToDate`},{keys:[`Alt`,`I`],label:`hotkeys.invertScale`,feature:`hotkeys.invertScale`},{keys:[`Alt`,`R`],label:`chartMenu.resetView`,feature:`hotkeys.resetView`},{keys:[i,`S`],label:`hotkeys.saveLayout`,feature:`hotkeys.save`},{keys:[`0–9`],label:`hotkeys.typeInterval`,feature:`intervalTyping`},{keys:[`A–Z`],label:`hotkeys.typeSymbol`,feature:`symbolTyping`},{keys:[`?`],label:`hotkeys.showSheet`,feature:`hotkeys.help`}]},{title:`Chart manipulation`,entries:[{keys:[`key.drag`],label:`hotkeys.pan`},{keys:[i,`key.drag`],label:`hotkeys.boxSelect`},{keys:[i,`key.click`],label:`hotkeys.toggleSelect`},{keys:[`Shift`,`key.drag`],label:`hotkeys.measure`},{keys:[`key.drag`,`key.priceAxis`],label:`hotkeys.scalePrice`},{keys:[`key.drag`,`key.timeAxis`],label:`hotkeys.zoomTime`},{keys:[`key.doubleClick`,`key.priceAxis`],label:`hotkeys.autoScale`},{keys:[`key.doubleClick`,`key.timeAxis`],label:`hotkeys.fit`},{keys:[`key.scroll`],label:`hotkeys.zoomCursor`}]},{title:`Touch (mobile / tablet)`,entries:[{keys:[`key.oneFinger`,`key.drag`],label:`hotkeys.touchPan`},{keys:[`key.twoFingers`,`key.pinch`],label:`hotkeys.pinch`},{keys:[`key.longPress`],label:`hotkeys.longPress`},{keys:[`key.drag`,`key.axisStrip`],label:`hotkeys.touchAxis`}]},{title:`Keyboard`,entries:[{keys:[`←`,`→`],label:`hotkeys.panBar`},{keys:[`+`,`−`],label:`hotkeys.zoomInOut`},{keys:[`Home`],label:`hotkeys.toStart`},{keys:[`End`],label:`hotkeys.toEnd`},{keys:[`Space`],label:`hotkeys.fit`}]},{title:`Bar replay`,entries:[{keys:[`Shift`,`←`,`→`],label:`hotkeys.replayStep`,feature:`replay`},{keys:[`Space`],label:`hotkeys.replayPlay`,feature:`replay`},{keys:[`Esc`],label:`hotkeys.replayCancel`,feature:`replay`}]},{title:`Drawing`,entries:[{keys:[i,`Z`],label:`hotkeys.undo`},{keys:[i,`Shift`,`Z`],label:`hotkeys.redo`},{keys:[`Esc`],label:`hotkeys.cancelDrawing`},{keys:[i,`C`],label:`hotkeys.copy`},{keys:[i,`V`],label:`hotkeys.paste`},{keys:[`key.rightClick`],label:`hotkeys.drawingMenu`},{keys:[`key.doubleClick`],label:`hotkeys.drawingSettings`},{keys:[i,`G`],label:`hotkeys.group`},{keys:[i,`Shift`,`G`],label:`hotkeys.ungroup`},{keys:[i,`]`,`[`],label:`hotkeys.orderStep`},{keys:[i,`Shift`,`]`,`[`],label:`hotkeys.orderEnd`},{keys:[`Enter`],label:`hotkeys.finishPath`},{keys:[i,`key.drag`],label:`hotkeys.copyDrag`},{keys:[`Shift`],label:`hotkeys.keepAngle`},{keys:[i],label:`hotkeys.flipMagnet`},{keys:[`←`,`→`,`↑`,`↓`],label:`hotkeys.nudge`},{keys:[`Alt`,`T`],label:`tool.trendLine`,feature:`hotkeys.tools`},{keys:[`Alt`,`H`],label:`tool.horizontalLine`,feature:`hotkeys.tools`},{keys:[`Alt`,`J`],label:`tool.horizontalRay`,feature:`hotkeys.tools`},{keys:[`Alt`,`V`],label:`tool.verticalLine`,feature:`hotkeys.tools`},{keys:[`Alt`,`C`],label:`tool.crossLine`,feature:`hotkeys.tools`},{keys:[`Alt`,`F`],label:`tool.fibRetracement`,feature:`hotkeys.tools`}]}],o=0,s=class{backdrop=null;bodyEl=null;searchEl=null;liveEl=null;groups=[];returnFocus=null;uid=++o;constructor(e,t,n=()=>document.body){this.callbacks=e,this.t=t,this.host=n}open(e={}){if(this.backdrop)return;this.returnFocus=document.activeElement instanceof HTMLElement?document.activeElement:null;let t=e.isOn??(()=>!0),n=new Set((e.extra??[]).map(e=>e.keys.join(`+`).toLowerCase()));this.groups=a.map(e=>({title:e.title,entries:e.entries.filter(e=>(!e.feature||t(e.feature))&&!n.has(e.keys.join(`+`).toLowerCase()))})).filter(e=>e.entries.length>0),e.extra&&e.extra.length>0&&this.groups.push({title:`More`,entries:[...e.extra]}),this.build(t(`hotkeys.help`)),this.render(),this.searchEl?.focus()}close(){if(!this.backdrop)return;this.backdrop.remove(),this.backdrop=this.bodyEl=this.searchEl=this.liveEl=null,this.callbacks.onClose();let e=this.returnFocus;this.returnFocus=null,e?.isConnected&&e.focus()}isOpen(){return this.backdrop!==null}destroy(){this.close()}build(t){let r=document.createElement(`div`);r.className=`tcw-modal-backdrop`;let i=!1;r.addEventListener(`pointerdown`,e=>{i=e.target===r}),r.addEventListener(`click`,e=>{e.target===r&&i&&this.close(),i=!1});let a=document.createElement(`div`);a.className=`tcw-modal tcw-hotkey-sheet`,a.setAttribute(`role`,`dialog`),a.setAttribute(`aria-modal`,`true`),a.setAttribute(`aria-labelledby`,`tcw-hotkey-title-${this.uid}`),a.tabIndex=-1,a.addEventListener(`keydown`,t=>{if(t.stopPropagation(),t.key===`Escape`&&!t.isComposing&&t.keyCode!==n){t.preventDefault(),this.close();return}e.n(t,a)});let o=document.createElement(`div`);o.className=`tcw-modal-header`;let s=document.createElement(`h3`);s.id=`tcw-hotkey-title-${this.uid}`,s.textContent=this.t(`hotkeys.title`);let c=document.createElement(`button`);c.type=`button`,c.className=`tcw-modal-close`,c.setAttribute(`aria-label`,this.t(`hotkeys.close`)),c.innerHTML=e.i(`x`,16),c.addEventListener(`click`,()=>this.close()),o.append(s,c);let l=document.createElement(`label`);l.className=`tcw-ss-search tcw-hotkey-search`,l.innerHTML=e.i(`search`,16);let u=document.createElement(`input`);u.type=`search`,u.placeholder=this.t(`hotkeys.search`),u.setAttribute(`aria-label`,this.t(`hotkeys.search`)),u.autocomplete=`off`,u.spellcheck=!1,u.addEventListener(`input`,()=>this.render()),l.appendChild(u);let d=document.createElement(`div`);d.className=`tcw-hotkey-body`;let f=document.createElement(`div`);if(f.className=`tcw-hotkey-live tcw-visually-hidden`,f.setAttribute(`aria-live`,`polite`),a.append(o,l,d,f),t){let e=document.createElement(`div`);e.className=`tcw-hotkey-foot`,e.textContent=this.t(`hotkeys.footer`),a.appendChild(e)}r.appendChild(a),this.host().appendChild(r),this.backdrop=r,this.bodyEl=d,this.searchEl=u,this.liveEl=f}render(){let e=this.bodyEl;if(!e)return;let n=t.n((this.searchEl?.value??``).trim()),i=[];for(let e of this.groups){let t=n?e.entries.filter(e=>this.matches(e,n)):e.entries;if(t.length===0)continue;let a=document.createElement(`section`);a.className=`tcw-hotkey-group`;let o=document.createElement(`div`);o.className=`tcw-hotkey-heading`;let s=r[e.title];o.textContent=s?this.t(s):e.title,a.append(o,...t.map(e=>this.row(e))),i.push(a)}if(this.liveEl&&(this.liveEl.textContent=i.length===0?this.t(`hotkeys.noMatch`):``),i.length===0){let t=document.createElement(`div`);t.className=`tcw-hotkey-empty`,t.textContent=this.t(`hotkeys.noMatch`),e.replaceChildren(t);return}let a=document.createElement(`div`);a.className=`tcw-hotkey-columns`,a.append(...i),e.replaceChildren(a)}label(e){return`text`in e?e.text:this.t(e.label)}keyText(e){return e.startsWith(`key.`)?this.t(e):e}matches(e,n){return t.n(this.label(e)).includes(n)?!0:t.n(e.keys.map(e=>this.keyText(e)).join(`+`)).includes(n)}row(e){let t=document.createElement(`div`);t.className=`tcw-hotkey-row`;let n=document.createElement(`span`);n.className=`tcw-hotkey-label`,n.textContent=this.label(e);let r=document.createElement(`span`);return r.className=`tcw-hotkey-keys`,r.dir=`ltr`,e.keys.forEach((e,t)=>{if(t>0){let e=document.createElement(`span`);e.className=`tcw-hotkey-sep`,e.textContent=`+`,r.appendChild(e)}let n=e.startsWith(`key.`),i=document.createElement(n?`span`:`kbd`);i.className=n?`tcw-hotkey-gesture`:`tcw-hotkey-key`,i.textContent=this.keyText(e),r.appendChild(i)}),t.append(n,r),t}};exports.WidgetHotkeySheet=s;
2
- //# sourceMappingURL=WidgetHotkeySheet-pnsXreK6.cjs.map
1
+ const e=require(`./focusTrap-DrhhmG0V.cjs`),t=require(`./platformKeys-B82-j1Fs.cjs`);var n=229,r={"Search & navigation":`hotkeys.group.searchNavigation`,"Chart manipulation":`hotkeys.group.chartManipulation`,"Touch (mobile / tablet)":`hotkeys.group.touch`,Keyboard:`hotkeys.group.keyboard`,"Bar replay":`toolbar.replay`,Drawing:`hotkeys.group.drawing`,More:`hotkeys.group.more`},i=t.t,a=[{title:`Search & navigation`,entries:[{keys:[i,`K`],label:`hotkeys.commandPalette`,feature:`hotkeys.commandPalette`},{keys:[i,`P`],label:`hotkeys.symbolSearch`,feature:`hotkeys.symbolSearch`},{keys:[`Alt`,`G`],label:`hotkeys.goToDate`,feature:`hotkeys.goToDate`},{keys:[`Alt`,`I`],label:`hotkeys.invertScale`,feature:`hotkeys.invertScale`},{keys:[`Alt`,`R`],label:`chartMenu.resetView`,feature:`hotkeys.resetView`},{keys:[i,`S`],label:`hotkeys.saveLayout`,feature:`hotkeys.save`},{keys:[`0–9`],label:`hotkeys.typeInterval`,feature:`intervalTyping`},{keys:[`A–Z`],label:`hotkeys.typeSymbol`,feature:`symbolTyping`},{keys:[`?`],label:`hotkeys.showSheet`,feature:`hotkeys.help`}]},{title:`Chart manipulation`,entries:[{keys:[`key.drag`],label:`hotkeys.pan`},{keys:[i,`key.drag`],label:`hotkeys.boxSelect`},{keys:[i,`key.click`],label:`hotkeys.toggleSelect`},{keys:[`Shift`,`key.drag`],label:`hotkeys.measure`},{keys:[`key.drag`,`key.priceAxis`],label:`hotkeys.scalePrice`},{keys:[`key.drag`,`key.timeAxis`],label:`hotkeys.zoomTime`},{keys:[`key.doubleClick`,`key.priceAxis`],label:`hotkeys.autoScale`},{keys:[`key.doubleClick`,`key.timeAxis`],label:`hotkeys.fit`},{keys:[`key.scroll`],label:`hotkeys.zoomCursor`}]},{title:`Touch (mobile / tablet)`,entries:[{keys:[`key.oneFinger`,`key.drag`],label:`hotkeys.touchPan`},{keys:[`key.twoFingers`,`key.pinch`],label:`hotkeys.pinch`},{keys:[`key.longPress`],label:`hotkeys.longPress`},{keys:[`key.drag`,`key.axisStrip`],label:`hotkeys.touchAxis`}]},{title:`Keyboard`,entries:[{keys:[`←`,`→`],label:`hotkeys.panBar`},{keys:[`+`,`−`],label:`hotkeys.zoomInOut`},{keys:[`Home`],label:`hotkeys.toStart`},{keys:[`End`],label:`hotkeys.toEnd`},{keys:[`Space`],label:`hotkeys.fit`}]},{title:`Bar replay`,entries:[{keys:[`Shift`,`←`,`→`],label:`hotkeys.replayStep`,feature:`replay`},{keys:[`Space`],label:`hotkeys.replayPlay`,feature:`replay`},{keys:[`Esc`],label:`hotkeys.replayCancel`,feature:`replay`}]},{title:`Drawing`,entries:[{keys:[i,`Z`],label:`hotkeys.undo`},{keys:[i,`Shift`,`Z`],label:`hotkeys.redo`},{keys:[`Esc`],label:`hotkeys.cancelDrawing`},{keys:[i,`C`],label:`hotkeys.copy`},{keys:[i,`V`],label:`hotkeys.paste`},{keys:[`key.rightClick`],label:`hotkeys.drawingMenu`},{keys:[`key.doubleClick`],label:`hotkeys.drawingSettings`},{keys:[i,`G`],label:`hotkeys.group`},{keys:[i,`Shift`,`G`],label:`hotkeys.ungroup`},{keys:[i,`]`,`[`],label:`hotkeys.orderStep`},{keys:[i,`Shift`,`]`,`[`],label:`hotkeys.orderEnd`},{keys:[`Enter`],label:`hotkeys.finishPath`},{keys:[i,`key.drag`],label:`hotkeys.copyDrag`},{keys:[`Shift`],label:`hotkeys.keepAngle`},{keys:[i],label:`hotkeys.flipMagnet`},{keys:[`←`,`→`,`↑`,`↓`],label:`hotkeys.nudge`},{keys:[`Alt`,`T`],label:`tool.trendLine`,feature:`hotkeys.tools`},{keys:[`Alt`,`H`],label:`tool.horizontalLine`,feature:`hotkeys.tools`},{keys:[`Alt`,`J`],label:`tool.horizontalRay`,feature:`hotkeys.tools`},{keys:[`Alt`,`V`],label:`tool.verticalLine`,feature:`hotkeys.tools`},{keys:[`Alt`,`C`],label:`tool.crossLine`,feature:`hotkeys.tools`},{keys:[`Alt`,`F`],label:`tool.fibRetracement`,feature:`hotkeys.tools`}]}],o=0,s=class{backdrop=null;bodyEl=null;searchEl=null;liveEl=null;groups=[];returnFocus=null;uid=++o;constructor(e,t,n=()=>document.body){this.callbacks=e,this.t=t,this.host=n}open(e={}){if(this.backdrop)return;this.returnFocus=document.activeElement instanceof HTMLElement?document.activeElement:null;let t=e.isOn??(()=>!0),n=new Set((e.extra??[]).map(e=>e.keys.join(`+`).toLowerCase()));this.groups=a.map(e=>({title:e.title,entries:e.entries.filter(e=>(!e.feature||t(e.feature))&&!n.has(e.keys.join(`+`).toLowerCase()))})).filter(e=>e.entries.length>0),e.extra&&e.extra.length>0&&this.groups.push({title:`More`,entries:[...e.extra]}),this.build(t(`hotkeys.help`)),this.render(),this.searchEl?.focus()}close(){if(!this.backdrop)return;this.backdrop.remove(),this.backdrop=this.bodyEl=this.searchEl=this.liveEl=null,this.callbacks.onClose();let e=this.returnFocus;this.returnFocus=null,e?.isConnected&&e.focus()}isOpen(){return this.backdrop!==null}destroy(){this.close()}build(t){let r=document.createElement(`div`);r.className=`tcw-modal-backdrop`;let i=!1;r.addEventListener(`pointerdown`,e=>{i=e.target===r}),r.addEventListener(`click`,e=>{e.target===r&&i&&this.close(),i=!1});let a=document.createElement(`div`);a.className=`tcw-modal tcw-hotkey-sheet`,a.setAttribute(`role`,`dialog`),a.setAttribute(`aria-modal`,`true`),a.setAttribute(`aria-labelledby`,`tcw-hotkey-title-${this.uid}`),a.tabIndex=-1,a.addEventListener(`keydown`,t=>{if(t.stopPropagation(),t.key===`Escape`&&!t.isComposing&&t.keyCode!==n){t.preventDefault(),this.close();return}e.n(t,a)});let o=document.createElement(`div`);o.className=`tcw-modal-header`;let s=document.createElement(`h3`);s.id=`tcw-hotkey-title-${this.uid}`,s.textContent=this.t(`hotkeys.title`);let c=document.createElement(`button`);c.type=`button`,c.className=`tcw-modal-close`,c.setAttribute(`aria-label`,this.t(`hotkeys.close`)),c.innerHTML=e.i(`x`,16),c.addEventListener(`click`,()=>this.close()),o.append(s,c);let l=document.createElement(`label`);l.className=`tcw-ss-search tcw-hotkey-search`,l.innerHTML=e.i(`search`,16);let u=document.createElement(`input`);u.type=`search`,u.placeholder=this.t(`hotkeys.search`),u.setAttribute(`aria-label`,this.t(`hotkeys.search`)),u.autocomplete=`off`,u.spellcheck=!1,u.addEventListener(`input`,()=>this.render()),l.appendChild(u);let d=document.createElement(`div`);d.className=`tcw-hotkey-body`;let f=document.createElement(`div`);if(f.className=`tcw-hotkey-live tcw-visually-hidden`,f.setAttribute(`aria-live`,`polite`),a.append(o,l,d,f),t){let e=document.createElement(`div`);e.className=`tcw-hotkey-foot`,e.textContent=this.t(`hotkeys.footer`),a.appendChild(e)}r.appendChild(a),this.host().appendChild(r),this.backdrop=r,this.bodyEl=d,this.searchEl=u,this.liveEl=f}render(){let e=this.bodyEl;if(!e)return;let n=t.n((this.searchEl?.value??``).trim()),i=[];for(let e of this.groups){let t=n?e.entries.filter(e=>this.matches(e,n)):e.entries;if(t.length===0)continue;let a=document.createElement(`section`);a.className=`tcw-hotkey-group`;let o=document.createElement(`div`);o.className=`tcw-hotkey-heading`;let s=r[e.title];o.textContent=s?this.t(s):e.title,a.append(o,...t.map(e=>this.row(e))),i.push(a)}if(this.liveEl&&(this.liveEl.textContent=i.length===0?this.t(`hotkeys.noMatch`):``),i.length===0){let t=document.createElement(`div`);t.className=`tcw-hotkey-empty`,t.textContent=this.t(`hotkeys.noMatch`),e.replaceChildren(t);return}let a=document.createElement(`div`);a.className=`tcw-hotkey-columns`,a.append(...i),e.replaceChildren(a)}label(e){return`text`in e?e.text:this.t(e.label)}keyText(e){return e.startsWith(`key.`)?this.t(e):e}matches(e,n){return t.n(this.label(e)).includes(n)?!0:t.n(e.keys.map(e=>this.keyText(e)).join(`+`)).includes(n)}row(e){let t=document.createElement(`div`);t.className=`tcw-hotkey-row`;let n=document.createElement(`span`);n.className=`tcw-hotkey-label`,n.textContent=this.label(e);let r=document.createElement(`span`);return r.className=`tcw-hotkey-keys`,r.dir=`ltr`,e.keys.forEach((e,t)=>{if(t>0){let e=document.createElement(`span`);e.className=`tcw-hotkey-sep`,e.textContent=`+`,r.appendChild(e)}let n=e.startsWith(`key.`),i=document.createElement(n?`span`:`kbd`);i.className=n?`tcw-hotkey-gesture`:`tcw-hotkey-key`,i.textContent=this.keyText(e),r.appendChild(i)}),t.append(n,r),t}};exports.WidgetHotkeySheet=s;
2
+ //# sourceMappingURL=WidgetHotkeySheet-y5LWKSG6.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"WidgetHotkeySheet-pnsXreK6.cjs","names":[],"sources":["../src/widget/WidgetHotkeySheet.ts"],"sourcesContent":["import type { MessageKey, Translator } from './i18n.js';\nimport type { WidgetFeature } from './widgetFeatures.js';\nimport { createIcon } from './icons.js';\nimport { keepTabInside } from './focusTrap.js';\nimport { MOD_KEY, searchForm } from './platformKeys.js';\n\n/**\n * Keyboard-shortcut reference sheet. Bound to `?` (and `Shift+/`). Surfaces\n * shortcuts so users discover the widget's hidden interaction model — a\n * premium-feel detail polished desktop tools all ship.\n */\nexport interface HotkeySheetCallbacks {\n onClose: () => void;\n}\n\n/** The key code a browser gives a key an input method takes. */\nconst IME_KEY_CODE = 229;\n\n// Group titles translate via this lookup; individual shortcut labels (and\n// the `keys` badges) stay English for now — see README \"Widget i18n\".\nconst GROUP_TITLE_KEYS: Record<string, MessageKey> = {\n 'Search & navigation': 'hotkeys.group.searchNavigation',\n 'Chart manipulation': 'hotkeys.group.chartManipulation',\n 'Touch (mobile / tablet)': 'hotkeys.group.touch',\n 'Keyboard': 'hotkeys.group.keyboard',\n 'Bar replay': 'toolbar.replay',\n 'Drawing': 'hotkeys.group.drawing',\n 'More': 'hotkeys.group.more',\n};\n\ninterface HotkeyEntry {\n keys: string[];\n label: string;\n /** The switch it goes with: left out of the sheet while that is off. */\n feature?: WidgetFeature;\n}\n\n/** A shortcut of the host's, shown as it was given. */\nexport interface HostHotkeyEntry {\n keys: string[];\n text: string;\n}\n\n/** What else the sheet shows: the switches that are on, and the host's shortcuts. */\nexport interface HotkeySheetOptions {\n isOn?: (feature: WidgetFeature) => boolean;\n extra?: readonly HostHotkeyEntry[];\n}\n\ninterface HotkeyGroup {\n title: string;\n entries: HotkeyEntry[];\n}\n\nconst mod = MOD_KEY;\n\nconst GROUPS: HotkeyGroup[] = [\n {\n title: 'Search & navigation',\n entries: [\n { keys: [mod, 'K'], label: 'hotkeys.commandPalette', feature: 'hotkeys.commandPalette' },\n { keys: [mod, 'P'], label: 'hotkeys.symbolSearch', feature: 'hotkeys.symbolSearch' },\n { keys: ['Alt', 'G'], label: 'hotkeys.goToDate', feature: 'hotkeys.goToDate' },\n { keys: ['Alt', 'I'], label: 'hotkeys.invertScale', feature: 'hotkeys.invertScale' },\n { keys: ['Alt', 'R'], label: 'chartMenu.resetView', feature: 'hotkeys.resetView' },\n { keys: [mod, 'S'], label: 'hotkeys.saveLayout', feature: 'hotkeys.save' },\n { keys: ['0–9'], label: 'hotkeys.typeInterval', feature: 'intervalTyping' },\n { keys: ['A–Z'], label: 'hotkeys.typeSymbol', feature: 'symbolTyping' },\n { keys: ['?'], label: 'hotkeys.showSheet', feature: 'hotkeys.help' },\n ],\n },\n {\n title: 'Chart manipulation',\n entries: [\n { keys: ['key.drag'], label: 'hotkeys.pan' },\n { keys: [mod, 'key.drag'], label: 'hotkeys.boxSelect' },\n { keys: [mod, 'key.click'], label: 'hotkeys.toggleSelect' },\n { keys: ['Shift', 'key.drag'], label: 'hotkeys.measure' },\n { keys: ['key.drag', 'key.priceAxis'], label: 'hotkeys.scalePrice' },\n { keys: ['key.drag', 'key.timeAxis'], label: 'hotkeys.zoomTime' },\n { keys: ['key.doubleClick', 'key.priceAxis'], label: 'hotkeys.autoScale' },\n { keys: ['key.doubleClick', 'key.timeAxis'], label: 'hotkeys.fit' },\n { keys: ['key.scroll'], label: 'hotkeys.zoomCursor' },\n ],\n },\n {\n title: 'Touch (mobile / tablet)',\n entries: [\n { keys: ['key.oneFinger', 'key.drag'], label: 'hotkeys.touchPan' },\n { keys: ['key.twoFingers', 'key.pinch'], label: 'hotkeys.pinch' },\n { keys: ['key.longPress'], label: 'hotkeys.longPress' },\n { keys: ['key.drag', 'key.axisStrip'], label: 'hotkeys.touchAxis' },\n ],\n },\n {\n title: 'Keyboard',\n entries: [\n { keys: ['←', '→'], label: 'hotkeys.panBar' },\n { keys: ['+', '−'], label: 'hotkeys.zoomInOut' },\n { keys: ['Home'], label: 'hotkeys.toStart' },\n { keys: ['End'], label: 'hotkeys.toEnd' },\n { keys: ['Space'], label: 'hotkeys.fit' },\n ],\n },\n {\n title: 'Bar replay',\n entries: [\n { keys: ['Shift', '←', '→'], label: 'hotkeys.replayStep', feature: 'replay' },\n // Outside a replay, Space fits the chart (above).\n { keys: ['Space'], label: 'hotkeys.replayPlay', feature: 'replay' },\n { keys: ['Esc'], label: 'hotkeys.replayCancel', feature: 'replay' },\n ],\n },\n {\n title: 'Drawing',\n entries: [\n { keys: [mod, 'Z'], label: 'hotkeys.undo' },\n { keys: [mod, 'Shift', 'Z'], label: 'hotkeys.redo' },\n { keys: ['Esc'], label: 'hotkeys.cancelDrawing' },\n { keys: [mod, 'C'], label: 'hotkeys.copy' },\n { keys: [mod, 'V'], label: 'hotkeys.paste' },\n { keys: ['key.rightClick'], label: 'hotkeys.drawingMenu' },\n { keys: ['key.doubleClick'], label: 'hotkeys.drawingSettings' },\n { keys: [mod, 'G'], label: 'hotkeys.group' },\n { keys: [mod, 'Shift', 'G'], label: 'hotkeys.ungroup' },\n { keys: [mod, ']', '['], label: 'hotkeys.orderStep' },\n { keys: [mod, 'Shift', ']', '['], label: 'hotkeys.orderEnd' },\n { keys: ['Enter'], label: 'hotkeys.finishPath' },\n { keys: [mod, 'key.drag'], label: 'hotkeys.copyDrag' },\n { keys: ['Shift'], label: 'hotkeys.keepAngle' },\n { keys: [mod], label: 'hotkeys.flipMagnet' },\n { keys: ['←', '→', '↑', '↓'], label: 'hotkeys.nudge' },\n { keys: ['Alt', 'T'], label: 'tool.trendLine', feature: 'hotkeys.tools' },\n { keys: ['Alt', 'H'], label: 'tool.horizontalLine', feature: 'hotkeys.tools' },\n { keys: ['Alt', 'J'], label: 'tool.horizontalRay', feature: 'hotkeys.tools' },\n { keys: ['Alt', 'V'], label: 'tool.verticalLine', feature: 'hotkeys.tools' },\n { keys: ['Alt', 'C'], label: 'tool.crossLine', feature: 'hotkeys.tools' },\n { keys: ['Alt', 'F'], label: 'tool.fibRetracement', feature: 'hotkeys.tools' },\n ],\n },\n];\n\nlet sheetSeq = 0;\n\n/**\n * The shortcut sheet: what each key does, by group, searchable by the words\n * or by the keys (\"alt+g\"). A gesture (drag, double-click) is written as\n * words, a key as a key. The keys switched off are left out; the host's\n * shortcuts come last, in place of a built-in one on the same keys.\n */\nexport class WidgetHotkeySheet {\n private backdrop: HTMLDivElement | null = null;\n private bodyEl: HTMLDivElement | null = null;\n private searchEl: HTMLInputElement | null = null;\n /** Says \"No shortcuts match\" to a screen reader. */\n private liveEl: HTMLDivElement | null = null;\n private groups: { title: string; entries: (HotkeyEntry | HostHotkeyEntry)[] }[] = [];\n private returnFocus: HTMLElement | null = null;\n private readonly uid = ++sheetSeq;\n\n /** Where the overlay mounts (the widget's portal: themed, and inside it when fullscreen). */\n constructor(\n private readonly callbacks: HotkeySheetCallbacks,\n private readonly t: Translator,\n private readonly host: () => HTMLElement = () => document.body,\n ) {}\n\n open(options: HotkeySheetOptions = {}): void {\n if (this.backdrop) return;\n this.returnFocus = document.activeElement instanceof HTMLElement ? document.activeElement : null;\n const isOn = options.isOn ?? (() => true);\n // The widget's own, less those switched off (a whole group goes when all its keys do).\n // A host's shortcut on the same keys replaces the built-in one.\n const replaced = new Set((options.extra ?? []).map((e) => e.keys.join('+').toLowerCase()));\n this.groups = GROUPS\n .map((group) => ({\n title: group.title,\n entries: group.entries.filter((e) => (!e.feature || isOn(e.feature)) && !replaced.has(e.keys.join('+').toLowerCase())) as (HotkeyEntry | HostHotkeyEntry)[],\n }))\n .filter((group) => group.entries.length > 0);\n if (options.extra && options.extra.length > 0) this.groups.push({ title: 'More', entries: [...options.extra] });\n this.build(isOn('hotkeys.help'));\n this.render();\n this.searchEl?.focus();\n }\n\n close(): void {\n if (!this.backdrop) return;\n this.backdrop.remove();\n this.backdrop = this.bodyEl = this.searchEl = this.liveEl = null;\n this.callbacks.onClose();\n const back = this.returnFocus;\n this.returnFocus = null;\n if (back?.isConnected) back.focus();\n }\n\n isOpen(): boolean {\n return this.backdrop !== null;\n }\n\n destroy(): void {\n this.close();\n }\n\n /** The sheet; `withFoot` says how to open it again (while its key is on). */\n private build(withFoot: boolean): void {\n const backdrop = document.createElement('div');\n backdrop.className = 'tcw-modal-backdrop';\n // A press that starts in the sheet 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-hotkey-sheet';\n modal.setAttribute('role', 'dialog');\n modal.setAttribute('aria-modal', 'true');\n modal.setAttribute('aria-labelledby', `tcw-hotkey-title-${this.uid}`);\n modal.tabIndex = -1;\n modal.addEventListener('keydown', (e) => {\n e.stopPropagation(); // keys here must not drive chart shortcuts\n // An input method's Escape ends what it composes; the sheet stays.\n if (e.key === 'Escape' && !e.isComposing && e.keyCode !== IME_KEY_CODE) {\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-hotkey-title-${this.uid}`;\n title.textContent = this.t('hotkeys.title');\n const closeBtn = document.createElement('button');\n closeBtn.type = 'button';\n closeBtn.className = 'tcw-modal-close';\n closeBtn.setAttribute('aria-label', this.t('hotkeys.close'));\n closeBtn.innerHTML = createIcon('x', 16);\n closeBtn.addEventListener('click', () => this.close());\n header.append(title, closeBtn);\n\n const search = document.createElement('label');\n search.className = 'tcw-ss-search tcw-hotkey-search';\n search.innerHTML = createIcon('search', 16);\n const input = document.createElement('input');\n input.type = 'search';\n input.placeholder = this.t('hotkeys.search');\n input.setAttribute('aria-label', this.t('hotkeys.search'));\n input.autocomplete = 'off';\n input.spellcheck = false;\n input.addEventListener('input', () => this.render());\n search.appendChild(input);\n\n const body = document.createElement('div');\n body.className = 'tcw-hotkey-body';\n\n const live = document.createElement('div');\n live.className = 'tcw-hotkey-live tcw-visually-hidden';\n live.setAttribute('aria-live', 'polite');\n\n modal.append(header, search, body, live);\n if (withFoot) {\n const foot = document.createElement('div');\n foot.className = 'tcw-hotkey-foot';\n foot.textContent = this.t('hotkeys.footer');\n modal.appendChild(foot);\n }\n backdrop.appendChild(modal);\n this.host().appendChild(backdrop);\n this.backdrop = backdrop;\n this.bodyEl = body;\n this.searchEl = input;\n this.liveEl = live;\n }\n\n /** The groups and their shortcuts that match what is typed. */\n private render(): void {\n const body = this.bodyEl;\n if (!body) return;\n const query = searchForm((this.searchEl?.value ?? '').trim());\n const sections: HTMLElement[] = [];\n for (const group of this.groups) {\n const entries = query ? group.entries.filter((e) => this.matches(e, query)) : group.entries;\n if (entries.length === 0) continue;\n const section = document.createElement('section');\n section.className = 'tcw-hotkey-group';\n const heading = document.createElement('div');\n heading.className = 'tcw-hotkey-heading';\n const titleKey = GROUP_TITLE_KEYS[group.title];\n heading.textContent = titleKey ? this.t(titleKey) : group.title;\n section.append(heading, ...entries.map((entry) => this.row(entry)));\n sections.push(section);\n }\n if (this.liveEl) this.liveEl.textContent = sections.length === 0 ? this.t('hotkeys.noMatch') : '';\n if (sections.length === 0) {\n const empty = document.createElement('div');\n empty.className = 'tcw-hotkey-empty';\n empty.textContent = this.t('hotkeys.noMatch');\n body.replaceChildren(empty);\n return;\n }\n // In columns inside the box that scrolls, as tall as they are: columns on a box of a fixed height\n // lay what doesn't fit in more columns to the side, out of sight and out of reach of the scroll.\n const columns = document.createElement('div');\n columns.className = 'tcw-hotkey-columns';\n columns.append(...sections);\n body.replaceChildren(columns);\n }\n\n private label(entry: HotkeyEntry | HostHotkeyEntry): string {\n return 'text' in entry ? entry.text : this.t(entry.label as MessageKey);\n }\n\n /** A key's words: its own, or a gesture's in the widget's language. */\n private keyText(key: string): string {\n return key.startsWith('key.') ? this.t(key as MessageKey) : key;\n }\n\n /** What it does, or its keys written as \"alt+g\", holds the query. */\n private matches(entry: HotkeyEntry | HostHotkeyEntry, query: string): boolean {\n if (searchForm(this.label(entry)).includes(query)) return true;\n return searchForm(entry.keys.map((k) => this.keyText(k)).join('+')).includes(query);\n }\n\n /** What it does, then its keys: a key as a key, a gesture as words. */\n private row(entry: HotkeyEntry | HostHotkeyEntry): HTMLDivElement {\n const row = document.createElement('div');\n row.className = 'tcw-hotkey-row';\n const label = document.createElement('span');\n label.className = 'tcw-hotkey-label';\n label.textContent = this.label(entry);\n const keys = document.createElement('span');\n keys.className = 'tcw-hotkey-keys';\n // A combination reads left to right in any widget: Ctrl + K, never K + Ctrl.\n keys.dir = 'ltr';\n entry.keys.forEach((key, i) => {\n if (i > 0) {\n const plus = document.createElement('span');\n plus.className = 'tcw-hotkey-sep';\n plus.textContent = '+';\n keys.appendChild(plus);\n }\n const gesture = key.startsWith('key.');\n const el = document.createElement(gesture ? 'span' : 'kbd');\n el.className = gesture ? 'tcw-hotkey-gesture' : 'tcw-hotkey-key';\n el.textContent = this.keyText(key);\n keys.appendChild(el);\n });\n row.append(label, keys);\n return row;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"WidgetHotkeySheet-y5LWKSG6.cjs","names":[],"sources":["../src/widget/WidgetHotkeySheet.ts"],"sourcesContent":["import type { MessageKey, Translator } from './i18n.js';\nimport type { WidgetFeature } from './widgetFeatures.js';\nimport { createIcon } from './icons.js';\nimport { keepTabInside } from './focusTrap.js';\nimport { MOD_KEY, searchForm } from './platformKeys.js';\n\n/**\n * Keyboard-shortcut reference sheet. Bound to `?` (and `Shift+/`). Surfaces\n * shortcuts so users discover the widget's hidden interaction model — a\n * premium-feel detail polished desktop tools all ship.\n */\nexport interface HotkeySheetCallbacks {\n onClose: () => void;\n}\n\n/** The key code a browser gives a key an input method takes. */\nconst IME_KEY_CODE = 229;\n\n// Group titles translate via this lookup; individual shortcut labels (and\n// the `keys` badges) stay English for now — see README \"Widget i18n\".\nconst GROUP_TITLE_KEYS: Record<string, MessageKey> = {\n 'Search & navigation': 'hotkeys.group.searchNavigation',\n 'Chart manipulation': 'hotkeys.group.chartManipulation',\n 'Touch (mobile / tablet)': 'hotkeys.group.touch',\n 'Keyboard': 'hotkeys.group.keyboard',\n 'Bar replay': 'toolbar.replay',\n 'Drawing': 'hotkeys.group.drawing',\n 'More': 'hotkeys.group.more',\n};\n\ninterface HotkeyEntry {\n keys: string[];\n label: string;\n /** The switch it goes with: left out of the sheet while that is off. */\n feature?: WidgetFeature;\n}\n\n/** A shortcut of the host's, shown as it was given. */\nexport interface HostHotkeyEntry {\n keys: string[];\n text: string;\n}\n\n/** What else the sheet shows: the switches that are on, and the host's shortcuts. */\nexport interface HotkeySheetOptions {\n isOn?: (feature: WidgetFeature) => boolean;\n extra?: readonly HostHotkeyEntry[];\n}\n\ninterface HotkeyGroup {\n title: string;\n entries: HotkeyEntry[];\n}\n\nconst mod = MOD_KEY;\n\nconst GROUPS: HotkeyGroup[] = [\n {\n title: 'Search & navigation',\n entries: [\n { keys: [mod, 'K'], label: 'hotkeys.commandPalette', feature: 'hotkeys.commandPalette' },\n { keys: [mod, 'P'], label: 'hotkeys.symbolSearch', feature: 'hotkeys.symbolSearch' },\n { keys: ['Alt', 'G'], label: 'hotkeys.goToDate', feature: 'hotkeys.goToDate' },\n { keys: ['Alt', 'I'], label: 'hotkeys.invertScale', feature: 'hotkeys.invertScale' },\n { keys: ['Alt', 'R'], label: 'chartMenu.resetView', feature: 'hotkeys.resetView' },\n { keys: [mod, 'S'], label: 'hotkeys.saveLayout', feature: 'hotkeys.save' },\n { keys: ['0–9'], label: 'hotkeys.typeInterval', feature: 'intervalTyping' },\n { keys: ['A–Z'], label: 'hotkeys.typeSymbol', feature: 'symbolTyping' },\n { keys: ['?'], label: 'hotkeys.showSheet', feature: 'hotkeys.help' },\n ],\n },\n {\n title: 'Chart manipulation',\n entries: [\n { keys: ['key.drag'], label: 'hotkeys.pan' },\n { keys: [mod, 'key.drag'], label: 'hotkeys.boxSelect' },\n { keys: [mod, 'key.click'], label: 'hotkeys.toggleSelect' },\n { keys: ['Shift', 'key.drag'], label: 'hotkeys.measure' },\n { keys: ['key.drag', 'key.priceAxis'], label: 'hotkeys.scalePrice' },\n { keys: ['key.drag', 'key.timeAxis'], label: 'hotkeys.zoomTime' },\n { keys: ['key.doubleClick', 'key.priceAxis'], label: 'hotkeys.autoScale' },\n { keys: ['key.doubleClick', 'key.timeAxis'], label: 'hotkeys.fit' },\n { keys: ['key.scroll'], label: 'hotkeys.zoomCursor' },\n ],\n },\n {\n title: 'Touch (mobile / tablet)',\n entries: [\n { keys: ['key.oneFinger', 'key.drag'], label: 'hotkeys.touchPan' },\n { keys: ['key.twoFingers', 'key.pinch'], label: 'hotkeys.pinch' },\n { keys: ['key.longPress'], label: 'hotkeys.longPress' },\n { keys: ['key.drag', 'key.axisStrip'], label: 'hotkeys.touchAxis' },\n ],\n },\n {\n title: 'Keyboard',\n entries: [\n { keys: ['←', '→'], label: 'hotkeys.panBar' },\n { keys: ['+', '−'], label: 'hotkeys.zoomInOut' },\n { keys: ['Home'], label: 'hotkeys.toStart' },\n { keys: ['End'], label: 'hotkeys.toEnd' },\n { keys: ['Space'], label: 'hotkeys.fit' },\n ],\n },\n {\n title: 'Bar replay',\n entries: [\n { keys: ['Shift', '←', '→'], label: 'hotkeys.replayStep', feature: 'replay' },\n // Outside a replay, Space fits the chart (above).\n { keys: ['Space'], label: 'hotkeys.replayPlay', feature: 'replay' },\n { keys: ['Esc'], label: 'hotkeys.replayCancel', feature: 'replay' },\n ],\n },\n {\n title: 'Drawing',\n entries: [\n { keys: [mod, 'Z'], label: 'hotkeys.undo' },\n { keys: [mod, 'Shift', 'Z'], label: 'hotkeys.redo' },\n { keys: ['Esc'], label: 'hotkeys.cancelDrawing' },\n { keys: [mod, 'C'], label: 'hotkeys.copy' },\n { keys: [mod, 'V'], label: 'hotkeys.paste' },\n { keys: ['key.rightClick'], label: 'hotkeys.drawingMenu' },\n { keys: ['key.doubleClick'], label: 'hotkeys.drawingSettings' },\n { keys: [mod, 'G'], label: 'hotkeys.group' },\n { keys: [mod, 'Shift', 'G'], label: 'hotkeys.ungroup' },\n { keys: [mod, ']', '['], label: 'hotkeys.orderStep' },\n { keys: [mod, 'Shift', ']', '['], label: 'hotkeys.orderEnd' },\n { keys: ['Enter'], label: 'hotkeys.finishPath' },\n { keys: [mod, 'key.drag'], label: 'hotkeys.copyDrag' },\n { keys: ['Shift'], label: 'hotkeys.keepAngle' },\n { keys: [mod], label: 'hotkeys.flipMagnet' },\n { keys: ['←', '→', '↑', '↓'], label: 'hotkeys.nudge' },\n { keys: ['Alt', 'T'], label: 'tool.trendLine', feature: 'hotkeys.tools' },\n { keys: ['Alt', 'H'], label: 'tool.horizontalLine', feature: 'hotkeys.tools' },\n { keys: ['Alt', 'J'], label: 'tool.horizontalRay', feature: 'hotkeys.tools' },\n { keys: ['Alt', 'V'], label: 'tool.verticalLine', feature: 'hotkeys.tools' },\n { keys: ['Alt', 'C'], label: 'tool.crossLine', feature: 'hotkeys.tools' },\n { keys: ['Alt', 'F'], label: 'tool.fibRetracement', feature: 'hotkeys.tools' },\n ],\n },\n];\n\nlet sheetSeq = 0;\n\n/**\n * The shortcut sheet: what each key does, by group, searchable by the words\n * or by the keys (\"alt+g\"). A gesture (drag, double-click) is written as\n * words, a key as a key. The keys switched off are left out; the host's\n * shortcuts come last, in place of a built-in one on the same keys.\n */\nexport class WidgetHotkeySheet {\n private backdrop: HTMLDivElement | null = null;\n private bodyEl: HTMLDivElement | null = null;\n private searchEl: HTMLInputElement | null = null;\n /** Says \"No shortcuts match\" to a screen reader. */\n private liveEl: HTMLDivElement | null = null;\n private groups: { title: string; entries: (HotkeyEntry | HostHotkeyEntry)[] }[] = [];\n private returnFocus: HTMLElement | null = null;\n private readonly uid = ++sheetSeq;\n\n /** Where the overlay mounts (the widget's portal: themed, and inside it when fullscreen). */\n constructor(\n private readonly callbacks: HotkeySheetCallbacks,\n private readonly t: Translator,\n private readonly host: () => HTMLElement = () => document.body,\n ) {}\n\n open(options: HotkeySheetOptions = {}): void {\n if (this.backdrop) return;\n this.returnFocus = document.activeElement instanceof HTMLElement ? document.activeElement : null;\n const isOn = options.isOn ?? (() => true);\n // The widget's own, less those switched off (a whole group goes when all its keys do).\n // A host's shortcut on the same keys replaces the built-in one.\n const replaced = new Set((options.extra ?? []).map((e) => e.keys.join('+').toLowerCase()));\n this.groups = GROUPS\n .map((group) => ({\n title: group.title,\n entries: group.entries.filter((e) => (!e.feature || isOn(e.feature)) && !replaced.has(e.keys.join('+').toLowerCase())) as (HotkeyEntry | HostHotkeyEntry)[],\n }))\n .filter((group) => group.entries.length > 0);\n if (options.extra && options.extra.length > 0) this.groups.push({ title: 'More', entries: [...options.extra] });\n this.build(isOn('hotkeys.help'));\n this.render();\n this.searchEl?.focus();\n }\n\n close(): void {\n if (!this.backdrop) return;\n this.backdrop.remove();\n this.backdrop = this.bodyEl = this.searchEl = this.liveEl = null;\n this.callbacks.onClose();\n const back = this.returnFocus;\n this.returnFocus = null;\n if (back?.isConnected) back.focus();\n }\n\n isOpen(): boolean {\n return this.backdrop !== null;\n }\n\n destroy(): void {\n this.close();\n }\n\n /** The sheet; `withFoot` says how to open it again (while its key is on). */\n private build(withFoot: boolean): void {\n const backdrop = document.createElement('div');\n backdrop.className = 'tcw-modal-backdrop';\n // A press that starts in the sheet 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-hotkey-sheet';\n modal.setAttribute('role', 'dialog');\n modal.setAttribute('aria-modal', 'true');\n modal.setAttribute('aria-labelledby', `tcw-hotkey-title-${this.uid}`);\n modal.tabIndex = -1;\n modal.addEventListener('keydown', (e) => {\n e.stopPropagation(); // keys here must not drive chart shortcuts\n // An input method's Escape ends what it composes; the sheet stays.\n if (e.key === 'Escape' && !e.isComposing && e.keyCode !== IME_KEY_CODE) {\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-hotkey-title-${this.uid}`;\n title.textContent = this.t('hotkeys.title');\n const closeBtn = document.createElement('button');\n closeBtn.type = 'button';\n closeBtn.className = 'tcw-modal-close';\n closeBtn.setAttribute('aria-label', this.t('hotkeys.close'));\n closeBtn.innerHTML = createIcon('x', 16);\n closeBtn.addEventListener('click', () => this.close());\n header.append(title, closeBtn);\n\n const search = document.createElement('label');\n search.className = 'tcw-ss-search tcw-hotkey-search';\n search.innerHTML = createIcon('search', 16);\n const input = document.createElement('input');\n input.type = 'search';\n input.placeholder = this.t('hotkeys.search');\n input.setAttribute('aria-label', this.t('hotkeys.search'));\n input.autocomplete = 'off';\n input.spellcheck = false;\n input.addEventListener('input', () => this.render());\n search.appendChild(input);\n\n const body = document.createElement('div');\n body.className = 'tcw-hotkey-body';\n\n const live = document.createElement('div');\n live.className = 'tcw-hotkey-live tcw-visually-hidden';\n live.setAttribute('aria-live', 'polite');\n\n modal.append(header, search, body, live);\n if (withFoot) {\n const foot = document.createElement('div');\n foot.className = 'tcw-hotkey-foot';\n foot.textContent = this.t('hotkeys.footer');\n modal.appendChild(foot);\n }\n backdrop.appendChild(modal);\n this.host().appendChild(backdrop);\n this.backdrop = backdrop;\n this.bodyEl = body;\n this.searchEl = input;\n this.liveEl = live;\n }\n\n /** The groups and their shortcuts that match what is typed. */\n private render(): void {\n const body = this.bodyEl;\n if (!body) return;\n const query = searchForm((this.searchEl?.value ?? '').trim());\n const sections: HTMLElement[] = [];\n for (const group of this.groups) {\n const entries = query ? group.entries.filter((e) => this.matches(e, query)) : group.entries;\n if (entries.length === 0) continue;\n const section = document.createElement('section');\n section.className = 'tcw-hotkey-group';\n const heading = document.createElement('div');\n heading.className = 'tcw-hotkey-heading';\n const titleKey = GROUP_TITLE_KEYS[group.title];\n heading.textContent = titleKey ? this.t(titleKey) : group.title;\n section.append(heading, ...entries.map((entry) => this.row(entry)));\n sections.push(section);\n }\n if (this.liveEl) this.liveEl.textContent = sections.length === 0 ? this.t('hotkeys.noMatch') : '';\n if (sections.length === 0) {\n const empty = document.createElement('div');\n empty.className = 'tcw-hotkey-empty';\n empty.textContent = this.t('hotkeys.noMatch');\n body.replaceChildren(empty);\n return;\n }\n // In columns inside the box that scrolls, as tall as they are: columns on a box of a fixed height\n // lay what doesn't fit in more columns to the side, out of sight and out of reach of the scroll.\n const columns = document.createElement('div');\n columns.className = 'tcw-hotkey-columns';\n columns.append(...sections);\n body.replaceChildren(columns);\n }\n\n private label(entry: HotkeyEntry | HostHotkeyEntry): string {\n return 'text' in entry ? entry.text : this.t(entry.label as MessageKey);\n }\n\n /** A key's words: its own, or a gesture's in the widget's language. */\n private keyText(key: string): string {\n return key.startsWith('key.') ? this.t(key as MessageKey) : key;\n }\n\n /** What it does, or its keys written as \"alt+g\", holds the query. */\n private matches(entry: HotkeyEntry | HostHotkeyEntry, query: string): boolean {\n if (searchForm(this.label(entry)).includes(query)) return true;\n return searchForm(entry.keys.map((k) => this.keyText(k)).join('+')).includes(query);\n }\n\n /** What it does, then its keys: a key as a key, a gesture as words. */\n private row(entry: HotkeyEntry | HostHotkeyEntry): HTMLDivElement {\n const row = document.createElement('div');\n row.className = 'tcw-hotkey-row';\n const label = document.createElement('span');\n label.className = 'tcw-hotkey-label';\n label.textContent = this.label(entry);\n const keys = document.createElement('span');\n keys.className = 'tcw-hotkey-keys';\n // A combination reads left to right in any widget: Ctrl + K, never K + Ctrl.\n keys.dir = 'ltr';\n entry.keys.forEach((key, i) => {\n if (i > 0) {\n const plus = document.createElement('span');\n plus.className = 'tcw-hotkey-sep';\n plus.textContent = '+';\n keys.appendChild(plus);\n }\n const gesture = key.startsWith('key.');\n const el = document.createElement(gesture ? 'span' : 'kbd');\n el.className = gesture ? 'tcw-hotkey-gesture' : 'tcw-hotkey-key';\n el.textContent = this.keyText(key);\n keys.appendChild(el);\n });\n row.append(label, keys);\n return row;\n }\n}\n"],"mappings":"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"}