@typed/ui 1.0.0-beta.1 → 1.0.0-beta.11

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 (155) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +54 -10
  3. package/dist/Alert.d.ts +72 -0
  4. package/dist/Alert.d.ts.map +1 -0
  5. package/dist/Alert.js +40 -0
  6. package/dist/Button.d.ts +100 -0
  7. package/dist/Button.d.ts.map +1 -0
  8. package/dist/Button.js +42 -0
  9. package/dist/Carousel.d.ts +346 -0
  10. package/dist/Carousel.d.ts.map +1 -0
  11. package/dist/Carousel.js +264 -0
  12. package/dist/Checkbox.d.ts +168 -0
  13. package/dist/Checkbox.d.ts.map +1 -0
  14. package/dist/Checkbox.js +146 -0
  15. package/dist/Collection.d.ts +254 -0
  16. package/dist/Collection.d.ts.map +1 -0
  17. package/dist/Collection.js +218 -0
  18. package/dist/Combobox.d.ts +367 -0
  19. package/dist/Combobox.d.ts.map +1 -0
  20. package/dist/Combobox.js +300 -0
  21. package/dist/Composite.d.ts +823 -0
  22. package/dist/Composite.d.ts.map +1 -0
  23. package/dist/Composite.js +615 -0
  24. package/dist/Dialog.d.ts +544 -0
  25. package/dist/Dialog.d.ts.map +1 -0
  26. package/dist/Dialog.js +357 -0
  27. package/dist/Disclosure.d.ts +219 -0
  28. package/dist/Disclosure.d.ts.map +1 -0
  29. package/dist/Disclosure.js +128 -0
  30. package/dist/Dom/Events.d.ts +122 -0
  31. package/dist/Dom/Events.d.ts.map +1 -0
  32. package/dist/Dom/Events.js +192 -0
  33. package/dist/Dom/Props.d.ts +161 -0
  34. package/dist/Dom/Props.d.ts.map +1 -0
  35. package/dist/Dom/Props.js +110 -0
  36. package/dist/Dom/Refs.d.ts +58 -0
  37. package/dist/Dom/Refs.d.ts.map +1 -0
  38. package/dist/Dom/Refs.js +61 -0
  39. package/dist/Dom/Render.d.ts +59 -0
  40. package/dist/Dom/Render.d.ts.map +1 -0
  41. package/dist/Dom/Render.js +71 -0
  42. package/dist/Dom/Types.d.ts +570 -0
  43. package/dist/Dom/Types.d.ts.map +1 -0
  44. package/dist/Dom/Types.js +1 -0
  45. package/dist/Dom/index.d.ts +20 -0
  46. package/dist/Dom/index.d.ts.map +1 -0
  47. package/dist/Dom/index.js +8 -0
  48. package/dist/Dom.d.ts +14 -0
  49. package/dist/Dom.d.ts.map +1 -0
  50. package/dist/Dom.js +13 -0
  51. package/dist/Focusable.d.ts +85 -0
  52. package/dist/Focusable.d.ts.map +1 -0
  53. package/dist/Focusable.js +35 -0
  54. package/dist/Form.d.ts +1727 -0
  55. package/dist/Form.d.ts.map +1 -0
  56. package/dist/Form.js +1142 -0
  57. package/dist/Grid.d.ts +388 -0
  58. package/dist/Grid.d.ts.map +1 -0
  59. package/dist/Grid.js +284 -0
  60. package/dist/Group.d.ts +128 -0
  61. package/dist/Group.d.ts.map +1 -0
  62. package/dist/Group.js +71 -0
  63. package/dist/Heading.d.ts +87 -0
  64. package/dist/Heading.d.ts.map +1 -0
  65. package/dist/Heading.js +58 -0
  66. package/dist/Hovercard.d.ts +297 -0
  67. package/dist/Hovercard.d.ts.map +1 -0
  68. package/dist/Hovercard.js +188 -0
  69. package/dist/HttpRouter.d.ts +129 -6
  70. package/dist/HttpRouter.d.ts.map +1 -1
  71. package/dist/HttpRouter.js +198 -55
  72. package/dist/Link.d.ts +63 -28
  73. package/dist/Link.d.ts.map +1 -1
  74. package/dist/Link.js +84 -37
  75. package/dist/Listbox.d.ts +305 -0
  76. package/dist/Listbox.d.ts.map +1 -0
  77. package/dist/Listbox.js +245 -0
  78. package/dist/Menu.d.ts +663 -0
  79. package/dist/Menu.d.ts.map +1 -0
  80. package/dist/Menu.js +569 -0
  81. package/dist/Menubar.d.ts +249 -0
  82. package/dist/Menubar.d.ts.map +1 -0
  83. package/dist/Menubar.js +207 -0
  84. package/dist/Meter.d.ts +157 -0
  85. package/dist/Meter.d.ts.map +1 -0
  86. package/dist/Meter.js +87 -0
  87. package/dist/NativeDetails.d.ts +41 -0
  88. package/dist/NativeDetails.d.ts.map +1 -0
  89. package/dist/NativeDetails.js +40 -0
  90. package/dist/NativeDialog.d.ts +66 -0
  91. package/dist/NativeDialog.d.ts.map +1 -0
  92. package/dist/NativeDialog.js +88 -0
  93. package/dist/NativePopover.d.ts +43 -0
  94. package/dist/NativePopover.d.ts.map +1 -0
  95. package/dist/NativePopover.js +84 -0
  96. package/dist/Popover.d.ts +240 -0
  97. package/dist/Popover.d.ts.map +1 -0
  98. package/dist/Popover.js +140 -0
  99. package/dist/RadioGroup.d.ts +330 -0
  100. package/dist/RadioGroup.d.ts.map +1 -0
  101. package/dist/RadioGroup.js +241 -0
  102. package/dist/Role.d.ts +64 -0
  103. package/dist/Role.d.ts.map +1 -0
  104. package/dist/Role.js +27 -0
  105. package/dist/Select.d.ts +418 -0
  106. package/dist/Select.d.ts.map +1 -0
  107. package/dist/Select.js +357 -0
  108. package/dist/Separator.d.ts +58 -0
  109. package/dist/Separator.d.ts.map +1 -0
  110. package/dist/Separator.js +32 -0
  111. package/dist/Slider.d.ts +141 -0
  112. package/dist/Slider.d.ts.map +1 -0
  113. package/dist/Slider.js +101 -0
  114. package/dist/SpinButton.d.ts +141 -0
  115. package/dist/SpinButton.d.ts.map +1 -0
  116. package/dist/SpinButton.js +101 -0
  117. package/dist/Storybook.d.ts +76 -0
  118. package/dist/Storybook.d.ts.map +1 -0
  119. package/dist/Storybook.js +102 -0
  120. package/dist/Switch.d.ts +148 -0
  121. package/dist/Switch.d.ts.map +1 -0
  122. package/dist/Switch.js +110 -0
  123. package/dist/Tab.d.ts +26 -0
  124. package/dist/Tab.d.ts.map +1 -0
  125. package/dist/Tab.js +25 -0
  126. package/dist/Tabs.d.ts +411 -0
  127. package/dist/Tabs.d.ts.map +1 -0
  128. package/dist/Tabs.js +262 -0
  129. package/dist/Toolbar.d.ts +248 -0
  130. package/dist/Toolbar.d.ts.map +1 -0
  131. package/dist/Toolbar.js +187 -0
  132. package/dist/Tooltip.d.ts +296 -0
  133. package/dist/Tooltip.d.ts.map +1 -0
  134. package/dist/Tooltip.js +172 -0
  135. package/dist/Tree.d.ts +405 -0
  136. package/dist/Tree.d.ts.map +1 -0
  137. package/dist/Tree.js +333 -0
  138. package/dist/TreeGrid.d.ts +426 -0
  139. package/dist/TreeGrid.d.ts.map +1 -0
  140. package/dist/TreeGrid.js +308 -0
  141. package/dist/VisuallyHidden.d.ts +68 -0
  142. package/dist/VisuallyHidden.d.ts.map +1 -0
  143. package/dist/VisuallyHidden.js +44 -0
  144. package/dist/WindowSplitter.d.ts +336 -0
  145. package/dist/WindowSplitter.d.ts.map +1 -0
  146. package/dist/WindowSplitter.js +305 -0
  147. package/dist/index.d.ts +48 -0
  148. package/dist/index.d.ts.map +1 -1
  149. package/dist/index.js +48 -0
  150. package/package.json +48 -20
  151. package/src/HttpRouter.test.ts +0 -294
  152. package/src/HttpRouter.ts +0 -168
  153. package/src/Link.test.ts +0 -84
  154. package/src/Link.ts +0 -107
  155. package/src/index.ts +0 -2
@@ -0,0 +1,308 @@
1
+ /**
2
+ * TreeGrid combines tree expansion with virtual-focus grid movement. Rows carry hierarchy and
3
+ * expansion, cells carry column positions, and the root retains focus while aria-activedescendant
4
+ * identifies the active cell.
5
+ *
6
+ * @remarks
7
+ * The module keeps policy, state transitions, and DOM rendering separable so applications can use
8
+ * the state and pure operations without mounting UI, or supply custom hosts without replacing native
9
+ * events and browser-owned focus.
10
+ *
11
+ * Learn the interaction in the [TreeGrid guide](/explore/ui-tree-grid).
12
+ *
13
+ * @since 1.0.0
14
+ * @category modules
15
+ * @packageDocumentation
16
+ */
17
+ import * as Effect from "effect/Effect";
18
+ import { RefSubject } from "@typed/fx";
19
+ import { EventHandler, html, } from "@typed/template";
20
+ import * as Collection from "./Collection.js";
21
+ import * as Dom from "./Dom.js";
22
+ import * as Grid from "./Grid.js";
23
+ import * as Tree from "./Tree.js";
24
+ /**
25
+ * Effect Schema used by makeState to encode, decode, and hydrate TreeGrid state.
26
+ *
27
+ * @remarks
28
+ * @example
29
+ * ```ts
30
+ * import * as Schema from "effect/Schema";
31
+ * import * as TreeGrid from "@typed/ui/TreeGrid";
32
+ *
33
+ * const decodeState = Schema.decodeUnknownEffect(TreeGrid.StateSchema);
34
+ * ```
35
+ * @since 1.0.0
36
+ * @category Hierarchy and cell focus
37
+ */
38
+ export const StateSchema = Tree.StateSchema;
39
+ /**
40
+ * Creates hydrated TreeGrid state. Uses Tree state and defaults.
41
+ *
42
+ * @remarks
43
+ * The returned Effect creates the RefSubject when run. That state is renderer-independent;
44
+ * collection registrations belong to the separate Scope that runs register or ref, not to state
45
+ * creation.
46
+ *
47
+ * @example
48
+ * ```ts
49
+ * import * as Effect from "effect/Effect";
50
+ * import * as TreeGrid from "@typed/ui/TreeGrid";
51
+ *
52
+ * const program = Effect.scoped(
53
+ * Effect.gen(function* () {
54
+ * const state = yield* TreeGrid.makeState({});
55
+ * const collection = yield* TreeGrid.makeCollection();
56
+ * return { state: yield* state, collection: yield* collection };
57
+ * }),
58
+ * );
59
+ * ```
60
+ * @since 1.0.0
61
+ * @category Hierarchy and cell focus
62
+ */
63
+ export const makeState = Tree.makeState;
64
+ /**
65
+ * Tests membership in expandedIds without touching the DOM; this is the Tree helper exposed from
66
+ * the TreeGrid entrypoint.
67
+ *
68
+ * @remarks
69
+ * Separating this deterministic policy from event wiring lets applications test it directly and
70
+ * reuse it in custom composites.
71
+ *
72
+ * This is a synchronous calculation. It acquires no resources and does not mutate the input array,
73
+ * state, event, or DOM.
74
+ *
75
+ * @example
76
+ * ```ts
77
+ * import * as TreeGrid from "@typed/ui/TreeGrid";
78
+ *
79
+ * const expanded = TreeGrid.isExpanded({ activeId: null, expandedIds: ["parent"], loop: true }, "parent");
80
+ * ```
81
+ * @since 1.0.0
82
+ * @category Row expansion
83
+ */
84
+ export const isExpanded = Tree.isExpanded;
85
+ /**
86
+ * Adds a row id once to expandedIds using the Tree state transition.
87
+ *
88
+ * @remarks
89
+ * The operation exposes TreeGrid's transition directly so callers can compose it in Effect
90
+ * programs and native event handlers.
91
+ *
92
+ * @since 1.0.0
93
+ * @category Row expansion
94
+ */
95
+ export const expand = Tree.expand;
96
+ /**
97
+ * Removes a row id from expandedIds using the Tree state transition.
98
+ *
99
+ * @remarks
100
+ * The operation exposes TreeGrid's transition directly so callers can compose it in Effect
101
+ * programs and native event handlers.
102
+ *
103
+ * @since 1.0.0
104
+ * @category Row expansion
105
+ */
106
+ export const collapse = Tree.collapse;
107
+ /**
108
+ * Sets the active TreeGrid row or cell id without changing expansion.
109
+ *
110
+ * @remarks
111
+ * The operation exposes TreeGrid's transition directly so callers can compose it in Effect
112
+ * programs and native event handlers.
113
+ *
114
+ * @since 1.0.0
115
+ * @category Hierarchy and cell focus
116
+ */
117
+ export const activate = Tree.activate;
118
+ /**
119
+ * Creates a scoped Collection for TreeGrid items.
120
+ *
121
+ * @remarks
122
+ * The returned Effect allocates the RefSubject in the caller's Scope. Each later registration is
123
+ * owned by the Scope that runs register, independently of this construction Effect.
124
+ *
125
+ * @example
126
+ * ```ts
127
+ * import * as Effect from "effect/Effect";
128
+ * import * as TreeGrid from "@typed/ui/TreeGrid";
129
+ *
130
+ * const program = Effect.scoped(
131
+ * Effect.gen(function* () {
132
+ * const collection = yield* TreeGrid.makeCollection();
133
+ * return yield* collection;
134
+ * }),
135
+ * );
136
+ * ```
137
+ * @since 1.0.0
138
+ * @category Cell registration
139
+ */
140
+ export const makeCollection = Collection.makeState;
141
+ function rootInternalProps(options) {
142
+ const onfocus = options.collection === undefined
143
+ ? undefined
144
+ : Effect.gen(function* () {
145
+ const current = yield* options.state;
146
+ if (current.activeId !== null)
147
+ return;
148
+ const first = visibleItems(yield* options.collection, current)[0];
149
+ if (first !== undefined)
150
+ yield* activate(options.state, first.id);
151
+ });
152
+ const onkeydown = options.collection === undefined
153
+ ? undefined
154
+ : EventHandler.make(Effect.fn((event) => onKeyDown(options.state, options.collection, event)));
155
+ return ({ property }) => ({
156
+ role: "treegrid",
157
+ tabindex: 0,
158
+ "aria-label": property("label", undefined),
159
+ "aria-activedescendant": RefSubject.map(options.state, (state) => state.activeId ?? undefined),
160
+ onfocus,
161
+ onkeydown,
162
+ ref: options.state,
163
+ });
164
+ }
165
+ /**
166
+ * Renders the focus-owning treegrid root and applies combined grid and expansion keys.
167
+ *
168
+ * @remarks
169
+ * The returned Fx installs DOM refs, native listeners, state subscriptions, and optional
170
+ * collection registrations only when rendered. The rendering Scope removes those resources;
171
+ * unrelated nodes and attributes remain caller-owned.
172
+ *
173
+ * @since 1.0.0
174
+ * @category Treegrid surface
175
+ */
176
+ export function Root(options, host) {
177
+ return Dom.renderHost()(options, host, rootInternalProps(options), options.content, (props, content) => {
178
+ return html `<div ...${props}>${content}</div>`;
179
+ });
180
+ }
181
+ function rowInternalProps(options) {
182
+ const hasChildren = options.hasChildren ?? false;
183
+ return ({ property }) => ({
184
+ id: options.rowId,
185
+ role: "row",
186
+ "aria-level": property("level", 1),
187
+ "aria-expanded": hasChildren
188
+ ? RefSubject.map(options.state, (state) => isExpanded(state, options.rowId))
189
+ : undefined,
190
+ });
191
+ }
192
+ /**
193
+ * Renders a hierarchical row with level and optional expanded state.
194
+ *
195
+ * @remarks
196
+ * The returned Fx installs DOM refs, native listeners, state subscriptions, and optional
197
+ * collection registrations only when rendered. The rendering Scope removes those resources;
198
+ * unrelated nodes and attributes remain caller-owned.
199
+ *
200
+ * @since 1.0.0
201
+ * @category Hierarchical rows
202
+ */
203
+ export function Row(options, host) {
204
+ return Dom.renderHost()(options, host, rowInternalProps(options), options.content, (props, content) => html `<div ...${props}>${content}</div>`);
205
+ }
206
+ function cellInternalProps(options) {
207
+ const active = RefSubject.map(options.state, (state) => state.activeId === options.id);
208
+ const register = options.collection === undefined
209
+ ? undefined
210
+ : Collection.ref(options.collection, {
211
+ id: options.id,
212
+ value: {
213
+ rowId: options.rowId,
214
+ columnIndex: options.columnIndex,
215
+ parentId: options.parentId,
216
+ hasChildren: options.hasChildren ?? false,
217
+ },
218
+ textValue: options.id,
219
+ });
220
+ return () => ({
221
+ id: options.id,
222
+ role: "gridcell",
223
+ "aria-colindex": options.columnIndex,
224
+ "?data-active": active,
225
+ ref: register,
226
+ });
227
+ }
228
+ /**
229
+ * Renders and optionally registers one gridcell with row and column coordinates.
230
+ *
231
+ * @remarks
232
+ * The returned Fx installs DOM refs, native listeners, state subscriptions, and optional
233
+ * collection registrations only when rendered. The rendering Scope removes those resources;
234
+ * unrelated nodes and attributes remain caller-owned.
235
+ *
236
+ * @since 1.0.0
237
+ * @category Navigable cells
238
+ */
239
+ export function Cell(options, host) {
240
+ return Dom.renderHost()(options, host, cellInternalProps(options), options.content, (props, content) => {
241
+ return html `<div ...${props}>${content}</div>`;
242
+ });
243
+ }
244
+ function groupInternalProps(options) {
245
+ return () => ({
246
+ role: "rowgroup",
247
+ "?hidden": RefSubject.map(options.state, (state) => !isExpanded(state, options.parentId)),
248
+ });
249
+ }
250
+ /**
251
+ * Renders a rowgroup and hides it while its parent row is collapsed.
252
+ *
253
+ * @remarks
254
+ * The returned Fx installs DOM refs, native listeners, state subscriptions, and optional
255
+ * collection registrations only when rendered. The rendering Scope removes those resources;
256
+ * unrelated nodes and attributes remain caller-owned.
257
+ *
258
+ * @since 1.0.0
259
+ * @category Child row visibility
260
+ */
261
+ export function Group(options, host) {
262
+ return Dom.renderHost()(options, host, groupInternalProps(options), options.content, (props, content) => html `<div ...${props}>${content}</div>`);
263
+ }
264
+ function onKeyDown(state, collection, event) {
265
+ return Effect.gen(function* () {
266
+ const current = yield* state;
267
+ const all = yield* collection;
268
+ const visible = visibleItems(all, current);
269
+ const active = current.activeId === null ? undefined : all.find((item) => item.id === current.activeId);
270
+ const activeValue = active?.value;
271
+ if (activeValue?.columnIndex === 1 &&
272
+ (event.key === "ArrowRight" || event.key === "ArrowLeft")) {
273
+ if (event.key === "ArrowRight" && activeValue.hasChildren) {
274
+ event.preventDefault();
275
+ if (!isExpanded(current, activeValue.rowId))
276
+ return yield* expand(state, activeValue.rowId);
277
+ const child = visible.find((item) => item.value?.parentId === activeValue.rowId && item.value?.columnIndex === 1);
278
+ return child === undefined ? current : yield* activate(state, child.id);
279
+ }
280
+ if (event.key === "ArrowLeft") {
281
+ event.preventDefault();
282
+ if (activeValue.hasChildren && isExpanded(current, activeValue.rowId))
283
+ return yield* collapse(state, activeValue.rowId);
284
+ const parent = activeValue.parentId === undefined
285
+ ? undefined
286
+ : visible.find((item) => item.value?.rowId === activeValue.parentId && item.value?.columnIndex === 1);
287
+ return parent === undefined ? current : yield* activate(state, parent.id);
288
+ }
289
+ }
290
+ const next = Grid.moveActiveId(visible, current.activeId, event);
291
+ if (next === undefined)
292
+ return current;
293
+ event.preventDefault();
294
+ return yield* activate(state, next);
295
+ });
296
+ }
297
+ function visibleItems(items, state) {
298
+ const ordered = Collection.byDomOrder(items);
299
+ return ordered.filter((item) => {
300
+ let parentId = item.value?.parentId;
301
+ while (parentId !== undefined) {
302
+ if (!isExpanded(state, parentId))
303
+ return false;
304
+ parentId = ordered.find((candidate) => candidate.value?.rowId === parentId)?.value?.parentId;
305
+ }
306
+ return true;
307
+ });
308
+ }
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Text clipped visually but retained for accessible names and descriptions.
3
+ * The clipping style has no focus-reveal behavior and must not hide interactive descendants.
4
+ *
5
+ * Read the [VisuallyHidden guide](/explore/ui-visually-hidden) for a complete example.
6
+ *
7
+ * [Platform reference](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Attributes/aria-hidden).
8
+ * @since 1.0.0
9
+ * @category Overview
10
+ * @packageDocumentation
11
+ */
12
+ import type * as Scope from "effect/Scope";
13
+ import type { Fx } from "@typed/fx/Fx";
14
+ import { type Renderable, type RenderEvent, type RenderTemplate } from "@typed/template";
15
+ import * as Dom from "./Dom.js";
16
+ import type { HostResult } from "./Dom/Types.js";
17
+ /**
18
+ */
19
+ export interface VisuallyHiddenOptions extends Dom.HostOptions<HTMLSpanElement> {
20
+ /**
21
+ * Content retained in the document and accessibility tree.
22
+ * @since 1.0.0
23
+ * @category Rendered content
24
+ */
25
+ readonly content: Renderable.Any;
26
+ }
27
+ declare function internalProps(): {
28
+ style: string;
29
+ };
30
+ type VisuallyHiddenInternalProps = ReturnType<typeof internalProps>;
31
+ /**
32
+ * Clips text visually while retaining it for accessible naming and reading.
33
+ *
34
+ * @remarks
35
+ * The span uses absolute positioning and a one-pixel clipping recipe. It has no focus-reveal
36
+ * behavior: do not hide interactive controls or use it alone as a skip link. An aria-hidden
37
+ * ancestor still removes its content from accessibility exposure. Custom hosts must preserve the
38
+ * supplied style.
39
+ *
40
+ * @example
41
+ * ```ts
42
+ * import { RefSubject } from "@typed/fx";
43
+ * import { html } from "@typed/template";
44
+ * import { Button } from "@typed/ui/Button";
45
+ * import { component } from "@typed/template";
46
+ * import { VisuallyHidden } from "@typed/ui/VisuallyHidden";
47
+ *
48
+ * export const ResetCounter = component(function* () {
49
+ * const count = yield* RefSubject.make(3);
50
+ * return html`<div>
51
+ * <p>Selected items: ${count}</p>
52
+ * ${Button({
53
+ * content: html`
54
+ * <span aria-hidden="true">×</span>
55
+ * ${VisuallyHidden({ content: "Clear selection" })}
56
+ * `,
57
+ * props: { class: "icon-action" },
58
+ * onclick: RefSubject.set(count, 0),
59
+ * })}
60
+ * </div>`;
61
+ * });
62
+ * ```
63
+ * @since 1.0.0
64
+ * @category Structure and naming
65
+ */
66
+ export declare function VisuallyHidden<const Options extends VisuallyHiddenOptions, const Host extends HostResult = never>(options: Options, host?: Dom.HostOverride<Dom.RenderHostProps<Options, VisuallyHiddenInternalProps>, Options["content"], Host>): Fx<RenderEvent, Renderable.Error<Options | Host>, Renderable.Services<Options | Host> | Scope.Scope | RenderTemplate>;
67
+ export {};
68
+ //# sourceMappingURL=VisuallyHidden.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"VisuallyHidden.d.ts","sourceRoot":"","sources":["../src/VisuallyHidden.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,KAAK,KAAK,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAQ,KAAK,UAAU,EAAE,KAAK,WAAW,EAAE,KAAK,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAC/F,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AAChC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAKjD;GACG;AACH,MAAM,WAAW,qBAAsB,SAAQ,GAAG,CAAC,WAAW,CAAC,eAAe,CAAC;IAC7E;;;;OAIG;IACH,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,CAAC;CAClC;AAED,iBAAS,aAAa;;EAErB;AAED,KAAK,2BAA2B,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AAEpE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAgB,cAAc,CAC5B,KAAK,CAAC,OAAO,SAAS,qBAAqB,EAC3C,KAAK,CAAC,IAAI,SAAS,UAAU,GAAG,KAAK,EAErC,OAAO,EAAE,OAAO,EAChB,IAAI,CAAC,EAAE,GAAG,CAAC,YAAY,CACrB,GAAG,CAAC,eAAe,CAAC,OAAO,EAAE,2BAA2B,CAAC,EACzD,OAAO,CAAC,SAAS,CAAC,EAClB,IAAI,CACL,GACA,EAAE,CACH,WAAW,EACX,UAAU,CAAC,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,EAChC,UAAU,CAAC,QAAQ,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,KAAK,CAAC,KAAK,GAAG,cAAc,CACnE,CAQA"}
@@ -0,0 +1,44 @@
1
+ import { html } from "@typed/template";
2
+ import * as Dom from "./Dom.js";
3
+ const style = "border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;white-space:nowrap;width:1px";
4
+ function internalProps() {
5
+ return { style };
6
+ }
7
+ /**
8
+ * Clips text visually while retaining it for accessible naming and reading.
9
+ *
10
+ * @remarks
11
+ * The span uses absolute positioning and a one-pixel clipping recipe. It has no focus-reveal
12
+ * behavior: do not hide interactive controls or use it alone as a skip link. An aria-hidden
13
+ * ancestor still removes its content from accessibility exposure. Custom hosts must preserve the
14
+ * supplied style.
15
+ *
16
+ * @example
17
+ * ```ts
18
+ * import { RefSubject } from "@typed/fx";
19
+ * import { html } from "@typed/template";
20
+ * import { Button } from "@typed/ui/Button";
21
+ * import { component } from "@typed/template";
22
+ * import { VisuallyHidden } from "@typed/ui/VisuallyHidden";
23
+ *
24
+ * export const ResetCounter = component(function* () {
25
+ * const count = yield* RefSubject.make(3);
26
+ * return html`<div>
27
+ * <p>Selected items: ${count}</p>
28
+ * ${Button({
29
+ * content: html`
30
+ * <span aria-hidden="true">×</span>
31
+ * ${VisuallyHidden({ content: "Clear selection" })}
32
+ * `,
33
+ * props: { class: "icon-action" },
34
+ * onclick: RefSubject.set(count, 0),
35
+ * })}
36
+ * </div>`;
37
+ * });
38
+ * ```
39
+ * @since 1.0.0
40
+ * @category Structure and naming
41
+ */
42
+ export function VisuallyHidden(options, host) {
43
+ return Dom.renderHost()(options, host, internalProps, options.content, (props, content) => html `<span ...${props}>${content}</span>`);
44
+ }