@matchstick-trading/primitives 0.0.10

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 (216) hide show
  1. package/AGENTS.md +44 -0
  2. package/CHANGELOG.md +19 -0
  3. package/LICENSE +201 -0
  4. package/README.md +636 -0
  5. package/dist/Chart.d.ts +18 -0
  6. package/dist/Chart.js +6 -0
  7. package/dist/ChartParts.d.ts +151 -0
  8. package/dist/ChartParts.js +389 -0
  9. package/dist/CsvDrop.d.ts +8 -0
  10. package/dist/CsvDrop.js +5 -0
  11. package/dist/CsvDropParts.d.ts +33 -0
  12. package/dist/CsvDropParts.js +77 -0
  13. package/dist/Cursor.d.ts +6 -0
  14. package/dist/Cursor.js +5 -0
  15. package/dist/CursorParts.d.ts +51 -0
  16. package/dist/CursorParts.js +40 -0
  17. package/dist/DepthProfile.d.ts +13 -0
  18. package/dist/DepthProfile.js +5 -0
  19. package/dist/DepthProfileParts.d.ts +45 -0
  20. package/dist/DepthProfileParts.js +96 -0
  21. package/dist/DiffTable.d.ts +13 -0
  22. package/dist/DiffTable.js +5 -0
  23. package/dist/DiffTableParts.d.ts +57 -0
  24. package/dist/DiffTableParts.js +103 -0
  25. package/dist/Drawings.d.ts +13 -0
  26. package/dist/Drawings.js +5 -0
  27. package/dist/DrawingsParts.d.ts +194 -0
  28. package/dist/DrawingsParts.js +775 -0
  29. package/dist/Fullscreen.d.ts +7 -0
  30. package/dist/Fullscreen.js +5 -0
  31. package/dist/FullscreenParts.d.ts +54 -0
  32. package/dist/FullscreenParts.js +250 -0
  33. package/dist/Ladder.d.ts +13 -0
  34. package/dist/Ladder.js +5 -0
  35. package/dist/LadderParts.d.ts +49 -0
  36. package/dist/LadderParts.js +56 -0
  37. package/dist/Menu.d.ts +11 -0
  38. package/dist/Menu.js +5 -0
  39. package/dist/MenuParts.d.ts +52 -0
  40. package/dist/MenuParts.js +202 -0
  41. package/dist/Notice.d.ts +18 -0
  42. package/dist/Notice.js +9 -0
  43. package/dist/Panels.d.ts +9 -0
  44. package/dist/Panels.js +5 -0
  45. package/dist/PanelsParts.d.ts +62 -0
  46. package/dist/PanelsParts.js +387 -0
  47. package/dist/Receipt.d.ts +11 -0
  48. package/dist/Receipt.js +5 -0
  49. package/dist/ReceiptParts.d.ts +45 -0
  50. package/dist/ReceiptParts.js +71 -0
  51. package/dist/Scrubber.d.ts +9 -0
  52. package/dist/Scrubber.js +5 -0
  53. package/dist/ScrubberParts.d.ts +74 -0
  54. package/dist/ScrubberParts.js +126 -0
  55. package/dist/Selection.d.ts +14 -0
  56. package/dist/Selection.js +7 -0
  57. package/dist/SelectionParts.d.ts +63 -0
  58. package/dist/SelectionParts.js +208 -0
  59. package/dist/Studies.d.ts +9 -0
  60. package/dist/Studies.js +5 -0
  61. package/dist/StudiesParts.d.ts +45 -0
  62. package/dist/StudiesParts.js +153 -0
  63. package/dist/Submenu.d.ts +7 -0
  64. package/dist/Submenu.js +4 -0
  65. package/dist/SymbolSearch.d.ts +10 -0
  66. package/dist/SymbolSearch.js +5 -0
  67. package/dist/SymbolSearchParts.d.ts +39 -0
  68. package/dist/SymbolSearchParts.js +86 -0
  69. package/dist/Tape.d.ts +15 -0
  70. package/dist/Tape.js +5 -0
  71. package/dist/TapeParts.d.ts +69 -0
  72. package/dist/TapeParts.js +139 -0
  73. package/dist/Toolbar.d.ts +8 -0
  74. package/dist/Toolbar.js +5 -0
  75. package/dist/ToolbarParts.d.ts +61 -0
  76. package/dist/ToolbarParts.js +122 -0
  77. package/dist/Transport.d.ts +15 -0
  78. package/dist/Transport.js +5 -0
  79. package/dist/TransportParts.d.ts +52 -0
  80. package/dist/TransportParts.js +47 -0
  81. package/dist/VolumeProfile.d.ts +7 -0
  82. package/dist/VolumeProfile.js +5 -0
  83. package/dist/VolumeProfileParts.d.ts +37 -0
  84. package/dist/VolumeProfileParts.js +57 -0
  85. package/dist/accessible-name.d.ts +26 -0
  86. package/dist/accessible-name.js +14 -0
  87. package/dist/attributes.d.ts +8 -0
  88. package/dist/attributes.js +9 -0
  89. package/dist/core/adapter.d.ts +38 -0
  90. package/dist/core/adapter.js +64 -0
  91. package/dist/core/attributes.d.ts +36 -0
  92. package/dist/core/attributes.js +22 -0
  93. package/dist/core/candles.d.ts +6 -0
  94. package/dist/core/candles.js +19 -0
  95. package/dist/core/chart-gesture.d.ts +109 -0
  96. package/dist/core/chart-gesture.js +161 -0
  97. package/dist/core/chart-scale.d.ts +141 -0
  98. package/dist/core/chart-scale.js +302 -0
  99. package/dist/core/csv-bars.d.ts +54 -0
  100. package/dist/core/csv-bars.js +325 -0
  101. package/dist/core/depth-profile.d.ts +43 -0
  102. package/dist/core/depth-profile.js +66 -0
  103. package/dist/core/diff-table.d.ts +57 -0
  104. package/dist/core/diff-table.js +77 -0
  105. package/dist/core/drawing-fields.d.ts +11 -0
  106. package/dist/core/drawing-fields.js +15 -0
  107. package/dist/core/drawings.d.ts +194 -0
  108. package/dist/core/drawings.js +403 -0
  109. package/dist/core/fullscreen.d.ts +32 -0
  110. package/dist/core/fullscreen.js +16 -0
  111. package/dist/core/index.d.ts +41 -0
  112. package/dist/core/index.js +23 -0
  113. package/dist/core/ladder-keys.d.ts +35 -0
  114. package/dist/core/ladder-keys.js +43 -0
  115. package/dist/core/ladder.d.ts +65 -0
  116. package/dist/core/ladder.js +93 -0
  117. package/dist/core/panels.d.ts +72 -0
  118. package/dist/core/panels.js +151 -0
  119. package/dist/core/receipt-rules.d.ts +42 -0
  120. package/dist/core/receipt-rules.js +20 -0
  121. package/dist/core/scrubber-gesture.d.ts +66 -0
  122. package/dist/core/scrubber-gesture.js +69 -0
  123. package/dist/core/selection.d.ts +24 -0
  124. package/dist/core/selection.js +33 -0
  125. package/dist/core/series.d.ts +161 -0
  126. package/dist/core/series.js +331 -0
  127. package/dist/core/studies.d.ts +80 -0
  128. package/dist/core/studies.js +237 -0
  129. package/dist/core/symbol-search.d.ts +89 -0
  130. package/dist/core/symbol-search.js +223 -0
  131. package/dist/core/tape.d.ts +77 -0
  132. package/dist/core/tape.js +136 -0
  133. package/dist/core/volume-profile.d.ts +47 -0
  134. package/dist/core/volume-profile.js +86 -0
  135. package/dist/index.d.ts +82 -0
  136. package/dist/index.js +42 -0
  137. package/dist/ladder-keys.d.ts +16 -0
  138. package/dist/ladder-keys.js +86 -0
  139. package/dist/panels-context.d.ts +47 -0
  140. package/dist/panels-context.js +3 -0
  141. package/dist/selection-context.d.ts +41 -0
  142. package/dist/selection-context.js +4 -0
  143. package/examples/csv-to-chart-frame.mjs +17 -0
  144. package/examples/studies.mjs +12 -0
  145. package/llms.txt +16 -0
  146. package/package.json +67 -0
  147. package/src/Chart.tsx +7 -0
  148. package/src/ChartParts.tsx +529 -0
  149. package/src/CsvDrop.tsx +6 -0
  150. package/src/CsvDropParts.tsx +128 -0
  151. package/src/Cursor.tsx +6 -0
  152. package/src/CursorParts.tsx +77 -0
  153. package/src/DepthProfile.tsx +6 -0
  154. package/src/DepthProfileParts.tsx +154 -0
  155. package/src/DiffTable.tsx +6 -0
  156. package/src/DiffTableParts.tsx +160 -0
  157. package/src/Drawings.tsx +6 -0
  158. package/src/DrawingsParts.tsx +1080 -0
  159. package/src/Fullscreen.tsx +6 -0
  160. package/src/FullscreenParts.tsx +318 -0
  161. package/src/Ladder.tsx +6 -0
  162. package/src/LadderParts.tsx +114 -0
  163. package/src/Menu.tsx +6 -0
  164. package/src/MenuParts.tsx +364 -0
  165. package/src/Notice.tsx +27 -0
  166. package/src/Panels.tsx +6 -0
  167. package/src/PanelsParts.tsx +454 -0
  168. package/src/Receipt.tsx +6 -0
  169. package/src/ReceiptParts.tsx +118 -0
  170. package/src/Scrubber.tsx +6 -0
  171. package/src/ScrubberParts.tsx +207 -0
  172. package/src/Selection.tsx +12 -0
  173. package/src/SelectionParts.tsx +251 -0
  174. package/src/Studies.tsx +6 -0
  175. package/src/StudiesParts.tsx +226 -0
  176. package/src/Submenu.tsx +5 -0
  177. package/src/SymbolSearch.tsx +6 -0
  178. package/src/SymbolSearchParts.tsx +166 -0
  179. package/src/Tape.tsx +6 -0
  180. package/src/TapeParts.tsx +208 -0
  181. package/src/Toolbar.tsx +6 -0
  182. package/src/ToolbarParts.tsx +179 -0
  183. package/src/Transport.tsx +6 -0
  184. package/src/TransportParts.tsx +128 -0
  185. package/src/VolumeProfile.tsx +6 -0
  186. package/src/VolumeProfileParts.tsx +106 -0
  187. package/src/accessible-name.tsx +34 -0
  188. package/src/attributes.ts +12 -0
  189. package/src/core/adapter.ts +91 -0
  190. package/src/core/attributes.ts +51 -0
  191. package/src/core/candles.ts +22 -0
  192. package/src/core/chart-gesture.ts +228 -0
  193. package/src/core/chart-scale.ts +368 -0
  194. package/src/core/csv-bars.ts +328 -0
  195. package/src/core/depth-profile.ts +84 -0
  196. package/src/core/diff-table.ts +105 -0
  197. package/src/core/drawing-fields.ts +17 -0
  198. package/src/core/drawings.ts +478 -0
  199. package/src/core/fullscreen.ts +32 -0
  200. package/src/core/index.ts +42 -0
  201. package/src/core/ladder-keys.ts +61 -0
  202. package/src/core/ladder.ts +156 -0
  203. package/src/core/panels.ts +177 -0
  204. package/src/core/receipt-rules.ts +45 -0
  205. package/src/core/scrubber-gesture.ts +102 -0
  206. package/src/core/selection.ts +41 -0
  207. package/src/core/series.ts +374 -0
  208. package/src/core/studies.ts +260 -0
  209. package/src/core/symbol-search.ts +256 -0
  210. package/src/core/tape.ts +159 -0
  211. package/src/core/volume-profile.ts +116 -0
  212. package/src/index.ts +83 -0
  213. package/src/ladder-keys.ts +83 -0
  214. package/src/panels-context.ts +46 -0
  215. package/src/selection-context.ts +38 -0
  216. package/src/tokens.css +235 -0
@@ -0,0 +1,202 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { createContext, useCallback, useContext, useEffect, useId, useMemo, useRef, useState } from 'react';
4
+ import { buttonFace } from './accessible-name.js';
5
+ import { useNameCheck, useToolbarContext, useToolbarItem } from './ToolbarParts.js';
6
+ const Context = createContext(null);
7
+ function useMenu(part) {
8
+ const context = useContext(Context);
9
+ if (!context)
10
+ throw new Error(`${part} needs a Menu.Root (or Submenu.Root) above it.`);
11
+ return context;
12
+ }
13
+ function useMenuState(parent, { open, onOpenChange }) {
14
+ const [inner, setInner] = useState(false);
15
+ const isOpen = open !== undefined ? open : inner;
16
+ const triggerRef = useRef(null);
17
+ const focusOnOpen = useRef('first');
18
+ const triggerId = useId();
19
+ const listId = useId();
20
+ const set = useCallback((next) => {
21
+ if (open === undefined)
22
+ setInner(next);
23
+ onOpenChange?.(next);
24
+ }, [open, onOpenChange]);
25
+ const show = useCallback((focus) => { focusOnOpen.current = focus; set(true); }, [set]);
26
+ const hide = useCallback((returnFocus) => {
27
+ set(false);
28
+ if (returnFocus)
29
+ triggerRef.current?.focus();
30
+ }, [set]);
31
+ const hideAll = useCallback((returnFocus) => {
32
+ if (parent)
33
+ parent.hideAll(returnFocus);
34
+ else
35
+ hide(returnFocus);
36
+ }, [parent, hide]);
37
+ return useMemo(() => ({ open: isOpen, show, hide, hideAll, triggerId, listId, triggerRef, focusOnOpen, submenu: parent !== null }), [isOpen, show, hide, hideAll, triggerId, listId, parent]);
38
+ }
39
+ /** Holds the open state and the wrapper element the List is placed against. */
40
+ function Root({ className, open, onOpenChange, children }) {
41
+ const context = useMenuState(null, { open, onOpenChange });
42
+ const element = useRef(null);
43
+ // A press outside the menu, anywhere in the page, closes it.
44
+ useEffect(() => {
45
+ if (!context.open)
46
+ return;
47
+ const onDown = (event) => { if (!element.current?.contains(event.target))
48
+ context.hide(false); };
49
+ document.addEventListener('pointerdown', onDown);
50
+ return () => document.removeEventListener('pointerdown', onDown);
51
+ }, [context]);
52
+ return (_jsx(Context.Provider, { value: context, children: _jsx("div", { ref: element, className: className, "data-mt": "", "data-part": "menu", "data-state": context.open ? 'open' : 'closed',
53
+ // Focus leaving for another control (Tab, or a script) closes the menu; a null target is the window losing focus.
54
+ onBlur: (event) => { if (context.open && event.relatedTarget && !element.current?.contains(event.relatedTarget))
55
+ context.hide(false); }, children: children }) }));
56
+ }
57
+ /** The button that opens the menu: `aria-haspopup="menu"` and `aria-expanded`. Inside a Toolbar it is one of its items. */
58
+ function Trigger({ className, icon, label, hideLabel, children }) {
59
+ const menu = useMenu('Menu.Trigger');
60
+ const toolbar = useToolbarContext();
61
+ const item = useToolbarItem(menu.triggerRef);
62
+ const face = buttonFace(undefined, { icon, label, hideLabel, children });
63
+ useNameCheck(menu.triggerRef, 'Menu.Trigger');
64
+ const onKeyDown = (event) => {
65
+ if (event.altKey || event.ctrlKey || event.metaKey)
66
+ return;
67
+ // In a vertical toolbar Up and Down move between items, so the menu opens to the side instead.
68
+ const vertical = toolbar?.orientation === 'vertical';
69
+ const open = vertical ? event.key === 'ArrowRight' : event.key === 'ArrowDown';
70
+ if (!open && !(!vertical && event.key === 'ArrowUp'))
71
+ return;
72
+ event.preventDefault();
73
+ event.stopPropagation();
74
+ menu.show(event.key === 'ArrowUp' ? 'last' : 'first');
75
+ };
76
+ return (_jsx("button", { ref: menu.triggerRef, id: menu.triggerId, type: "button", className: className, "data-mt": "", "data-part": "menu-trigger", "data-touch-target": "", "data-state": menu.open ? 'open' : 'closed', "aria-haspopup": "menu", "aria-expanded": menu.open, "aria-controls": menu.open ? menu.listId : undefined, "aria-label": face['aria-label'], tabIndex: item.tabIndex, onFocus: item.onFocus, onKeyDown: onKeyDown, onClick: () => (menu.open ? menu.hide(false) : menu.show('first')), children: face.content }));
77
+ }
78
+ /** The items of a list: its own menuitems, not those of a submenu inside it. Disabled ones are skipped. */
79
+ function itemsOf(list) {
80
+ return [...list.querySelectorAll("[role='menuitem'], [role='menuitemradio']")].filter((el) => el.closest("[role='menu']") === list && !el.disabled);
81
+ }
82
+ /** The open list, role="menu". It is not in the page while closed. */
83
+ function List({ className, 'aria-label': label, children }) {
84
+ const menu = useMenu('Menu.List');
85
+ const ref = useRef(null);
86
+ const typed = useRef({ text: '', timer: 0 });
87
+ // Opening puts focus on an item. Arrow keys, a tap and Enter all arrive here.
88
+ useEffect(() => {
89
+ if (!menu.open)
90
+ return;
91
+ const items = itemsOf(ref.current);
92
+ (menu.focusOnOpen.current === 'last' ? items[items.length - 1] : items[0])?.focus();
93
+ }, [menu.open, menu.focusOnOpen]);
94
+ useEffect(() => () => window.clearTimeout(typed.current.timer), []);
95
+ if (!menu.open)
96
+ return null;
97
+ const onKeyDown = (event) => {
98
+ const list = ref.current;
99
+ // Keys from inside a submenu are handled by that submenu's list.
100
+ if (!list || event.target.closest("[role='menu']") !== list)
101
+ return;
102
+ if (event.altKey || event.ctrlKey || event.metaKey)
103
+ return;
104
+ const items = itemsOf(list);
105
+ const at = items.indexOf(document.activeElement);
106
+ const go = (index) => { event.preventDefault(); event.stopPropagation(); items[index]?.focus(); };
107
+ switch (event.key) {
108
+ case 'ArrowDown': return go(items.length === 0 ? 0 : (at + 1) % items.length);
109
+ case 'ArrowUp': return go(items.length === 0 ? 0 : (at - 1 + items.length) % items.length);
110
+ case 'Home': return go(0);
111
+ case 'End': return go(items.length - 1);
112
+ case 'Escape':
113
+ event.preventDefault();
114
+ event.stopPropagation(); // the toolbar or page behind must not also react
115
+ return menu.hide(true);
116
+ case 'ArrowLeft':
117
+ case 'ArrowRight': {
118
+ // A submenu closes with the arrow that points back toward its parent: Left in left-to-right text, Right in right-to-left.
119
+ const closing = getComputedStyle(list).direction === 'rtl' ? 'ArrowRight' : 'ArrowLeft';
120
+ if (!menu.submenu || event.key !== closing)
121
+ return;
122
+ event.preventDefault();
123
+ event.stopPropagation();
124
+ return menu.hide(true);
125
+ }
126
+ case 'Tab':
127
+ return menu.hideAll(true); // focus goes back to the trigger first, so Tab then moves on from there
128
+ default:
129
+ }
130
+ // Typeahead: letters build a prefix (reset after a short pause); one letter repeated steps through items that share it.
131
+ if (event.key.length === 1 && event.key !== ' ') {
132
+ event.preventDefault();
133
+ window.clearTimeout(typed.current.timer);
134
+ typed.current.text += event.key.toLowerCase();
135
+ typed.current.timer = window.setTimeout(() => { typed.current.text = ''; }, 600);
136
+ const word = typed.current.text;
137
+ const name = (el) => {
138
+ const label = el.getAttribute('aria-label');
139
+ if (label !== null)
140
+ return label.trim().toLowerCase();
141
+ const copy = el.cloneNode(true); // an icon is aria-hidden: its text is not the item's name
142
+ copy.querySelectorAll('[aria-hidden="true"]').forEach((hidden) => hidden.remove());
143
+ return (copy.textContent ?? '').trim().toLowerCase();
144
+ };
145
+ const repeated = word.length > 1 && [...word].every((c) => c === word[0]);
146
+ const prefix = repeated ? word[0] : word;
147
+ const order = [...items.slice(at + 1), ...items.slice(0, at + 1)];
148
+ (order.find((el) => name(el).startsWith(prefix)) ?? order.find((el) => name(el).startsWith(word)))?.focus();
149
+ }
150
+ };
151
+ return (_jsx("div", { ref: ref, id: menu.listId, className: className, "data-mt": "", "data-part": menu.submenu ? 'submenu-list' : 'menu-list', role: "menu", "aria-labelledby": label ? undefined : menu.triggerId, "aria-label": label, "aria-orientation": "vertical", onKeyDown: onKeyDown, children: children }));
152
+ }
153
+ /** One choice. A menuitem button; the roving focus is handled by the list, so it is not a tab stop. */
154
+ function Item({ className, disabled, onSelect, keepOpen, icon, label, hideLabel, children }) {
155
+ const menu = useMenu('Menu.Item');
156
+ const ref = useRef(null);
157
+ const face = buttonFace(undefined, { icon, label, hideLabel, children });
158
+ useNameCheck(ref, 'Menu.Item');
159
+ return (_jsx("button", { ref: ref, type: "button", className: className, "data-mt": "", "data-part": "menu-item", "data-touch-target": "", role: "menuitem", tabIndex: -1, disabled: disabled, "aria-label": face['aria-label'], onClick: () => { onSelect?.(); if (!keepOpen)
160
+ menu.hideAll(true); }, children: face.content }));
161
+ }
162
+ /** A choice among several: `aria-checked` says which is on. Picking it runs onSelect and closes the menu. */
163
+ function RadioItem({ className, checked, disabled, onSelect, keepOpen, icon, label, hideLabel, children }) {
164
+ const menu = useMenu('Menu.RadioItem');
165
+ const ref = useRef(null);
166
+ const face = buttonFace(undefined, { icon, label, hideLabel, children });
167
+ useNameCheck(ref, 'Menu.RadioItem');
168
+ return (_jsx("button", { ref: ref, type: "button", className: className, "data-mt": "", "data-part": "menu-radio-item", "data-touch-target": "", role: "menuitemradio", "aria-checked": checked, tabIndex: -1, disabled: disabled, "aria-label": face['aria-label'], onClick: () => { onSelect?.(); if (!keepOpen)
169
+ menu.hideAll(true); }, children: face.content }));
170
+ }
171
+ /** A thin divider between groups of items. */
172
+ function Separator({ className }) {
173
+ return _jsx("div", { className: className, "data-mt": "", "data-part": "menu-separator", role: "separator" });
174
+ }
175
+ /** A menu inside a menu: put Submenu.Trigger and Submenu.List inside it, among the items of the parent List. */
176
+ function SubmenuRoot({ className, open, onOpenChange, children }) {
177
+ const parent = useMenu('Submenu.Root');
178
+ const context = useMenuState(parent, { open, onOpenChange });
179
+ const element = useRef(null);
180
+ return (_jsx(Context.Provider, { value: context, children: _jsx("div", { ref: element, className: className, "data-mt": "", "data-part": "submenu", "data-state": context.open ? 'open' : 'closed',
181
+ // Focus moving to a sibling item of the parent closes the submenu.
182
+ onBlur: (event) => { if (context.open && event.relatedTarget && !element.current?.contains(event.relatedTarget))
183
+ context.hide(false); }, children: children }) }));
184
+ }
185
+ /** The item that opens a submenu: a menuitem with `aria-haspopup` and `aria-expanded`. ArrowRight (ArrowLeft in right-to-left text) opens it. */
186
+ function SubmenuTrigger({ className, icon, label, hideLabel, children }) {
187
+ const menu = useMenu('Submenu.Trigger');
188
+ const face = buttonFace(undefined, { icon, label, hideLabel, children });
189
+ useNameCheck(menu.triggerRef, 'Submenu.Trigger');
190
+ const onKeyDown = (event) => {
191
+ if (event.altKey || event.ctrlKey || event.metaKey)
192
+ return;
193
+ const rtl = getComputedStyle(event.currentTarget).direction === 'rtl';
194
+ if (event.key !== (rtl ? 'ArrowLeft' : 'ArrowRight'))
195
+ return;
196
+ event.preventDefault();
197
+ event.stopPropagation();
198
+ menu.show('first');
199
+ };
200
+ return (_jsx("button", { ref: menu.triggerRef, id: menu.triggerId, type: "button", className: className, "data-mt": "", "data-part": "submenu-trigger", "data-touch-target": "", "data-state": menu.open ? 'open' : 'closed', role: "menuitem", "aria-haspopup": "menu", "aria-expanded": menu.open, "aria-controls": menu.open ? menu.listId : undefined, "aria-label": face['aria-label'], tabIndex: -1, onKeyDown: onKeyDown, onClick: () => (menu.open ? menu.hide(false) : menu.show('first')), children: face.content }));
201
+ }
202
+ export { Root, Trigger, List, Item, RadioItem, Separator, SubmenuRoot, SubmenuTrigger };
@@ -0,0 +1,18 @@
1
+ import type { ReactNode } from 'react';
2
+ export type NoticeKind = 'gap' | 'stale' | 'truncated' | 'unsupported' | 'rights' | 'info' | 'ok' | 'error';
3
+ export interface NoticeProps {
4
+ /** `status` is polite (a normal condition); `alert` is for refusals and errors. */
5
+ role?: 'status' | 'alert';
6
+ kind?: NoticeKind;
7
+ /** Element to render: a paragraph by default, a span for inline text. */
8
+ as?: 'p' | 'span';
9
+ /** Host class, kept so existing styles keep matching. */
10
+ className?: string;
11
+ children?: ReactNode;
12
+ }
13
+ /**
14
+ * One integrity message. Replaces the repeated `<p role="status">` / `<p role="alert">` paragraphs: the same
15
+ * element, role and class as before, plus `data-mt` (the scope the token styles hang on) and `data-kind` so a host can style by kind.
16
+ */
17
+ export declare function Notice({ role, kind, as, className, children }: NoticeProps): import("react").JSX.Element;
18
+ //# sourceMappingURL=Notice.d.ts.map
package/dist/Notice.js ADDED
@@ -0,0 +1,9 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /**
3
+ * One integrity message. Replaces the repeated `<p role="status">` / `<p role="alert">` paragraphs: the same
4
+ * element, role and class as before, plus `data-mt` (the scope the token styles hang on) and `data-kind` so a host can style by kind.
5
+ */
6
+ export function Notice({ role = 'status', kind, as = 'p', className, children }) {
7
+ const Tag = as;
8
+ return (_jsx(Tag, { role: role, className: className, "data-mt": true, "data-kind": kind, children: children }));
9
+ }
@@ -0,0 +1,9 @@
1
+ import { Root, Layer, List, Move } from './PanelsParts.js';
2
+ export * from './PanelsParts.js';
3
+ export declare const Panels: {
4
+ Root: typeof Root;
5
+ Layer: typeof Layer;
6
+ List: typeof List;
7
+ Move: typeof Move;
8
+ };
9
+ //# sourceMappingURL=Panels.d.ts.map
package/dist/Panels.js ADDED
@@ -0,0 +1,5 @@
1
+ import { Root, Layer, List, Move } from './PanelsParts.js';
2
+ // The parts live in PanelsParts.js (a client module). The namespace object is built here, outside it, so a server
3
+ // component can write `Panels.Root`: a property of an object exported from a client module is not reachable there.
4
+ export * from './PanelsParts.js';
5
+ export const Panels = { Root, Layer, List, Move };
@@ -0,0 +1,62 @@
1
+ import { type ReactNode } from 'react';
2
+ import { type PanelLayout, type PanelSpec } from './core/panels.js';
3
+ import { type AlignOptions } from './core/series.js';
4
+ import { type PanelSeriesData, type PanelsContextValue } from './panels-context.js';
5
+ /**
6
+ * Panels as parts: Panels.Root, Panels.Layer, Panels.List and Panels.Move. A chart is one or more vertical bands, each
7
+ * with its own price scale, on one shared time axis. The chart's own bars and volume stay in the first band, `main`.
8
+ * Other series (a value per bar, like a study) are placed in a band by id and moved between bands by a menu of relative
9
+ * destinations ("Move up to Main", "Move to a new panel below"), which works by touch and keyboard. Headless: the parts
10
+ * set `data-part` and `data-state='selected'`; tokens.css gives the lines a stroke and the host gives them colour.
11
+ *
12
+ * Everything is opt-in: without a Panels.Root above it, Chart draws exactly as it did.
13
+ */
14
+ export interface PanelsRootProps {
15
+ /** How a series with `points` is placed on the bars. Default `{ mode: 'exact' }`; `asof` with `toleranceMs` takes the latest point at or before each bar. */
16
+ align?: AlignOptions;
17
+ /** The series to draw besides the chart's bars. Keep it stable (useMemo); a new array each render only costs a layout pass. */
18
+ series: readonly (PanelSeriesData & {
19
+ panel?: string;
20
+ })[];
21
+ /** Names and weights for the bands the series start in, below `main`. A panel a series names but this leaves out is created with its id as the label. */
22
+ panels?: readonly PanelSpec[];
23
+ /** Controlled layout; without it Root keeps its own. */
24
+ layout?: PanelLayout;
25
+ onLayoutChange?: (layout: PanelLayout) => void;
26
+ children?: ReactNode;
27
+ }
28
+ /** Holds the layout, says each move in a polite live region and tells Chart how tall the first band is. Wrap the Chart in it. */
29
+ declare function Root({ series: given, panels: named, layout, onLayoutChange, align, children }: PanelsRootProps): import("react").JSX.Element;
30
+ declare function usePanels(part: string): PanelsContextValue;
31
+ /**
32
+ * Draws the series in their bands. Put it inside `Chart.Plot`, after the chart's own parts. The series in `main` use the
33
+ * chart's price scale (and keep themselves in view); every other band has its own scale, grid, price labels and
34
+ * a divider. With a Selection.Root above, a tap on a series line selects it. A band is never shorter than 40, so give the
35
+ * chart enough height for the bands you use.
36
+ */
37
+ declare function Layer(): import("react").JSX.Element;
38
+ export interface PanelsListProps {
39
+ className?: string;
40
+ /** Prints one value. Default: the chart's decimals. */
41
+ format?: (value: number) => string;
42
+ }
43
+ /**
44
+ * The series as text: each one's name, its panel, and its value at the crosshair bar (the last bar in view when there is
45
+ * no crosshair), so the numbers are not only a picture. With a Selection.Root above, each row has a button that selects
46
+ * the series. Put it outside the SVG, inside `Chart.Root`.
47
+ */
48
+ declare function List({ className, format }: PanelsListProps): import("react").JSX.Element | null;
49
+ export interface PanelsMoveProps {
50
+ className?: string;
51
+ /** The series to move. Default: the selected one (it needs a Selection.Root above). */
52
+ seriesId?: string;
53
+ }
54
+ /**
55
+ * The way to move a series to another panel without dragging: buttons for where it can go, relative to where it is
56
+ * (up, down, a new panel above, a new panel below). It shows while a series of the Root is selected (or for `seriesId`)
57
+ * and says each move in the live region. After a move the focus goes to the group, so a keyboard or screen reader user
58
+ * is not left on a button that is gone.
59
+ */
60
+ declare function Move({ className, seriesId }: PanelsMoveProps): import("react").JSX.Element | null;
61
+ export { Root, Layer, List, Move, usePanels };
62
+ //# sourceMappingURL=PanelsParts.d.ts.map