@tokis/core 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +41 -0
  3. package/dist/a11y/aria-helpers.d.ts +19 -0
  4. package/dist/a11y/aria-helpers.d.ts.map +1 -0
  5. package/dist/a11y/aria-helpers.js +40 -0
  6. package/dist/a11y/aria-helpers.js.map +1 -0
  7. package/dist/a11y/id-generator.d.ts +18 -0
  8. package/dist/a11y/id-generator.d.ts.map +1 -0
  9. package/dist/a11y/id-generator.js +25 -0
  10. package/dist/a11y/id-generator.js.map +1 -0
  11. package/dist/cjs/a11y/aria-helpers.js +51 -0
  12. package/dist/cjs/a11y/id-generator.js +28 -0
  13. package/dist/cjs/focus/focus-trap.js +30 -0
  14. package/dist/cjs/focus/roving-tabindex.js +36 -0
  15. package/dist/cjs/focus/use-focus-visible.js +42 -0
  16. package/dist/cjs/index.js +24 -0
  17. package/dist/cjs/package.json +3 -0
  18. package/dist/cjs/primitives/button/button.machine.js +13 -0
  19. package/dist/cjs/primitives/button/button.types.js +2 -0
  20. package/dist/cjs/primitives/button/index.js +18 -0
  21. package/dist/cjs/state/controllable-state.js +22 -0
  22. package/dist/cjs/state/create-machine.js +27 -0
  23. package/dist/focus/focus-trap.d.ts +2 -0
  24. package/dist/focus/focus-trap.d.ts.map +1 -0
  25. package/dist/focus/focus-trap.js +28 -0
  26. package/dist/focus/focus-trap.js.map +1 -0
  27. package/dist/focus/roving-tabindex.d.ts +4 -0
  28. package/dist/focus/roving-tabindex.d.ts.map +1 -0
  29. package/dist/focus/roving-tabindex.js +34 -0
  30. package/dist/focus/roving-tabindex.js.map +1 -0
  31. package/dist/focus/use-focus-visible.d.ts +7 -0
  32. package/dist/focus/use-focus-visible.d.ts.map +1 -0
  33. package/dist/focus/use-focus-visible.js +40 -0
  34. package/dist/focus/use-focus-visible.js.map +1 -0
  35. package/dist/index.d.ts +9 -0
  36. package/dist/index.d.ts.map +1 -0
  37. package/dist/index.js +9 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/primitives/button/button.machine.d.ts +5 -0
  40. package/dist/primitives/button/button.machine.d.ts.map +1 -0
  41. package/dist/primitives/button/button.machine.js +11 -0
  42. package/dist/primitives/button/button.machine.js.map +1 -0
  43. package/dist/primitives/button/button.types.d.ts +9 -0
  44. package/dist/primitives/button/button.types.d.ts.map +1 -0
  45. package/dist/primitives/button/button.types.js +2 -0
  46. package/dist/primitives/button/button.types.js.map +1 -0
  47. package/dist/primitives/button/index.d.ts +3 -0
  48. package/dist/primitives/button/index.d.ts.map +1 -0
  49. package/dist/primitives/button/index.js +3 -0
  50. package/dist/primitives/button/index.js.map +1 -0
  51. package/dist/state/controllable-state.d.ts +15 -0
  52. package/dist/state/controllable-state.d.ts.map +1 -0
  53. package/dist/state/controllable-state.js +19 -0
  54. package/dist/state/controllable-state.js.map +1 -0
  55. package/dist/state/create-machine.d.ts +29 -0
  56. package/dist/state/create-machine.d.ts.map +1 -0
  57. package/dist/state/create-machine.js +23 -0
  58. package/dist/state/create-machine.js.map +1 -0
  59. package/package.json +59 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Tokis Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,41 @@
1
+ # /core
2
+
3
+ Framework-agnostic headless primitives for the Tokis design system — state machines, accessibility helpers, and focus management. Zero dependencies.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install /core
9
+ ```
10
+
11
+ > Or install everything at once: `npm install tokis`
12
+
13
+ ## Usage
14
+
15
+ ```ts
16
+ import {
17
+ createFocusTrap,
18
+ rovingTabIndex,
19
+ generateId,
20
+ createMachine,
21
+ } from '/core';
22
+ ```
23
+
24
+ ## What's Included
25
+
26
+ - **Focus Management** — `createFocusTrap`, `rovingTabIndex`, `useFocusVisible`
27
+ - **Accessibility** — ARIA attribute helpers, unique ID generation
28
+ - **State Machines** — Lightweight `createMachine` for component behavior
29
+ - **Controllable State** — Unified controlled/uncontrolled state pattern
30
+
31
+ ## Why Use This?
32
+
33
+ `/core` contains no React, no DOM assumptions, and no CSS. Use it to build your own component library on top of Tokis's battle-tested accessibility and state management layer — in React, Vue, Svelte, or vanilla JS.
34
+
35
+ ## Documentation
36
+
37
+ Visit [tokis.dev](https://tokis.dev) for the full documentation.
38
+
39
+ ## License
40
+
41
+ MIT
@@ -0,0 +1,19 @@
1
+ /**
2
+ * ARIA attribute helpers.
3
+ * Imperative setters for use in non-framework environments.
4
+ * Framework adapters should use the corresponding prop-object helpers instead.
5
+ */
6
+ export declare function setAriaLabelledBy(element: HTMLElement, id: string): void;
7
+ export declare function setAriaDescribedBy(element: HTMLElement, id: string): void;
8
+ export declare function setAriaExpanded(element: HTMLElement, expanded: boolean): void;
9
+ export declare function setAriaHidden(element: HTMLElement, hidden: boolean): void;
10
+ export declare function setAriaControls(element: HTMLElement, id: string): void;
11
+ export declare function ariaLabelledByProps(id: string): Record<string, string>;
12
+ export declare function ariaDescribedByProps(id: string): Record<string, string>;
13
+ export declare function ariaExpandedProps(expanded: boolean): Record<string, string>;
14
+ export declare function ariaHiddenProps(hidden: boolean): Record<string, string>;
15
+ /** @deprecated Use setAriaLabelledBy instead */
16
+ export declare const ariaLabelledBy: typeof setAriaLabelledBy;
17
+ /** @deprecated Use setAriaDescribedBy instead */
18
+ export declare const ariaDescribedBy: typeof setAriaDescribedBy;
19
+ //# sourceMappingURL=aria-helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aria-helpers.d.ts","sourceRoot":"","sources":["../../src/a11y/aria-helpers.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,WAAW,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,CAExE;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,WAAW,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,CAEzE;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,OAAO,GAAG,IAAI,CAE7E;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,GAAG,IAAI,CAEzE;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,WAAW,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,CAEtE;AAID,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAEtE;AAED,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAEvE;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAE3E;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAEvE;AAGD,gDAAgD;AAChD,eAAO,MAAM,cAAc,0BAAoB,CAAC;AAChD,iDAAiD;AACjD,eAAO,MAAM,eAAe,2BAAqB,CAAC"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * ARIA attribute helpers.
3
+ * Imperative setters for use in non-framework environments.
4
+ * Framework adapters should use the corresponding prop-object helpers instead.
5
+ */
6
+ // --- Imperative setters ---
7
+ export function setAriaLabelledBy(element, id) {
8
+ element.setAttribute('aria-labelledby', id);
9
+ }
10
+ export function setAriaDescribedBy(element, id) {
11
+ element.setAttribute('aria-describedby', id);
12
+ }
13
+ export function setAriaExpanded(element, expanded) {
14
+ element.setAttribute('aria-expanded', String(expanded));
15
+ }
16
+ export function setAriaHidden(element, hidden) {
17
+ element.setAttribute('aria-hidden', String(hidden));
18
+ }
19
+ export function setAriaControls(element, id) {
20
+ element.setAttribute('aria-controls', id);
21
+ }
22
+ // --- Prop-object helpers (framework-agnostic, usable in React/Vue/etc.) ---
23
+ export function ariaLabelledByProps(id) {
24
+ return { 'aria-labelledby': id };
25
+ }
26
+ export function ariaDescribedByProps(id) {
27
+ return { 'aria-describedby': id };
28
+ }
29
+ export function ariaExpandedProps(expanded) {
30
+ return { 'aria-expanded': String(expanded) };
31
+ }
32
+ export function ariaHiddenProps(hidden) {
33
+ return { 'aria-hidden': String(hidden) };
34
+ }
35
+ // Keep legacy exports for backward compatibility
36
+ /** @deprecated Use setAriaLabelledBy instead */
37
+ export const ariaLabelledBy = setAriaLabelledBy;
38
+ /** @deprecated Use setAriaDescribedBy instead */
39
+ export const ariaDescribedBy = setAriaDescribedBy;
40
+ //# sourceMappingURL=aria-helpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aria-helpers.js","sourceRoot":"","sources":["../../src/a11y/aria-helpers.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,6BAA6B;AAE7B,MAAM,UAAU,iBAAiB,CAAC,OAAoB,EAAE,EAAU;IAChE,OAAO,CAAC,YAAY,CAAC,iBAAiB,EAAE,EAAE,CAAC,CAAC;AAC9C,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,OAAoB,EAAE,EAAU;IACjE,OAAO,CAAC,YAAY,CAAC,kBAAkB,EAAE,EAAE,CAAC,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,OAAoB,EAAE,QAAiB;IACrE,OAAO,CAAC,YAAY,CAAC,eAAe,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;AAC1D,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,OAAoB,EAAE,MAAe;IACjE,OAAO,CAAC,YAAY,CAAC,aAAa,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;AACtD,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,OAAoB,EAAE,EAAU;IAC9D,OAAO,CAAC,YAAY,CAAC,eAAe,EAAE,EAAE,CAAC,CAAC;AAC5C,CAAC;AAED,6EAA6E;AAE7E,MAAM,UAAU,mBAAmB,CAAC,EAAU;IAC5C,OAAO,EAAE,iBAAiB,EAAE,EAAE,EAAE,CAAC;AACnC,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,EAAU;IAC7C,OAAO,EAAE,kBAAkB,EAAE,EAAE,EAAE,CAAC;AACpC,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,QAAiB;IACjD,OAAO,EAAE,eAAe,EAAE,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,MAAe;IAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;AAC3C,CAAC;AAED,iDAAiD;AACjD,gDAAgD;AAChD,MAAM,CAAC,MAAM,cAAc,GAAG,iBAAiB,CAAC;AAChD,iDAAiD;AACjD,MAAM,CAAC,MAAM,eAAe,GAAG,kBAAkB,CAAC"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Generates a globally unique, incrementing ID with an optional prefix.
3
+ * SSR Note: IDs generated server-side and client-side may differ.
4
+ * Prefer `createIdScope` for component-level ID generation.
5
+ */
6
+ export declare function generateId(prefix?: string): string;
7
+ /**
8
+ * Creates a scoped ID generator for a component instance.
9
+ * All IDs within a scope share the same numeric suffix,
10
+ * making them predictably related (e.g., label-1 / input-1).
11
+ *
12
+ * @example
13
+ * const scope = createIdScope('field');
14
+ * scope('label'); // → 'field-label-1'
15
+ * scope('input'); // → 'field-input-1'
16
+ */
17
+ export declare function createIdScope(componentName: string): (part: string) => string;
18
+ //# sourceMappingURL=id-generator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"id-generator.d.ts","sourceRoot":"","sources":["../../src/a11y/id-generator.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,MAAM,SAAU,GAAG,MAAM,CAGnD;AAED;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,aAAa,EAAE,MAAM,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAG7E"}
@@ -0,0 +1,25 @@
1
+ let globalCounter = 0;
2
+ /**
3
+ * Generates a globally unique, incrementing ID with an optional prefix.
4
+ * SSR Note: IDs generated server-side and client-side may differ.
5
+ * Prefer `createIdScope` for component-level ID generation.
6
+ */
7
+ export function generateId(prefix = 'tokis') {
8
+ globalCounter += 1;
9
+ return `${prefix}-${globalCounter}`;
10
+ }
11
+ /**
12
+ * Creates a scoped ID generator for a component instance.
13
+ * All IDs within a scope share the same numeric suffix,
14
+ * making them predictably related (e.g., label-1 / input-1).
15
+ *
16
+ * @example
17
+ * const scope = createIdScope('field');
18
+ * scope('label'); // → 'field-label-1'
19
+ * scope('input'); // → 'field-input-1'
20
+ */
21
+ export function createIdScope(componentName) {
22
+ const id = generateId(componentName);
23
+ return (part) => `${id}-${part}`;
24
+ }
25
+ //# sourceMappingURL=id-generator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"id-generator.js","sourceRoot":"","sources":["../../src/a11y/id-generator.ts"],"names":[],"mappings":"AAAA,IAAI,aAAa,GAAG,CAAC,CAAC;AAEtB;;;;GAIG;AACH,MAAM,UAAU,UAAU,CAAC,MAAM,GAAG,OAAO;IACzC,aAAa,IAAI,CAAC,CAAC;IACnB,OAAO,GAAG,MAAM,IAAI,aAAa,EAAE,CAAC;AACtC,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,aAAa,CAAC,aAAqB;IACjD,MAAM,EAAE,GAAG,UAAU,CAAC,aAAa,CAAC,CAAC;IACrC,OAAO,CAAC,IAAY,EAAE,EAAE,CAAC,GAAG,EAAE,IAAI,IAAI,EAAE,CAAC;AAC3C,CAAC"}
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ /**
3
+ * ARIA attribute helpers.
4
+ * Imperative setters for use in non-framework environments.
5
+ * Framework adapters should use the corresponding prop-object helpers instead.
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.ariaDescribedBy = exports.ariaLabelledBy = void 0;
9
+ exports.setAriaLabelledBy = setAriaLabelledBy;
10
+ exports.setAriaDescribedBy = setAriaDescribedBy;
11
+ exports.setAriaExpanded = setAriaExpanded;
12
+ exports.setAriaHidden = setAriaHidden;
13
+ exports.setAriaControls = setAriaControls;
14
+ exports.ariaLabelledByProps = ariaLabelledByProps;
15
+ exports.ariaDescribedByProps = ariaDescribedByProps;
16
+ exports.ariaExpandedProps = ariaExpandedProps;
17
+ exports.ariaHiddenProps = ariaHiddenProps;
18
+ // --- Imperative setters ---
19
+ function setAriaLabelledBy(element, id) {
20
+ element.setAttribute('aria-labelledby', id);
21
+ }
22
+ function setAriaDescribedBy(element, id) {
23
+ element.setAttribute('aria-describedby', id);
24
+ }
25
+ function setAriaExpanded(element, expanded) {
26
+ element.setAttribute('aria-expanded', String(expanded));
27
+ }
28
+ function setAriaHidden(element, hidden) {
29
+ element.setAttribute('aria-hidden', String(hidden));
30
+ }
31
+ function setAriaControls(element, id) {
32
+ element.setAttribute('aria-controls', id);
33
+ }
34
+ // --- Prop-object helpers (framework-agnostic, usable in React/Vue/etc.) ---
35
+ function ariaLabelledByProps(id) {
36
+ return { 'aria-labelledby': id };
37
+ }
38
+ function ariaDescribedByProps(id) {
39
+ return { 'aria-describedby': id };
40
+ }
41
+ function ariaExpandedProps(expanded) {
42
+ return { 'aria-expanded': String(expanded) };
43
+ }
44
+ function ariaHiddenProps(hidden) {
45
+ return { 'aria-hidden': String(hidden) };
46
+ }
47
+ // Keep legacy exports for backward compatibility
48
+ /** @deprecated Use setAriaLabelledBy instead */
49
+ exports.ariaLabelledBy = setAriaLabelledBy;
50
+ /** @deprecated Use setAriaDescribedBy instead */
51
+ exports.ariaDescribedBy = setAriaDescribedBy;
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateId = generateId;
4
+ exports.createIdScope = createIdScope;
5
+ let globalCounter = 0;
6
+ /**
7
+ * Generates a globally unique, incrementing ID with an optional prefix.
8
+ * SSR Note: IDs generated server-side and client-side may differ.
9
+ * Prefer `createIdScope` for component-level ID generation.
10
+ */
11
+ function generateId(prefix = 'tokis') {
12
+ globalCounter += 1;
13
+ return `${prefix}-${globalCounter}`;
14
+ }
15
+ /**
16
+ * Creates a scoped ID generator for a component instance.
17
+ * All IDs within a scope share the same numeric suffix,
18
+ * making them predictably related (e.g., label-1 / input-1).
19
+ *
20
+ * @example
21
+ * const scope = createIdScope('field');
22
+ * scope('label'); // → 'field-label-1'
23
+ * scope('input'); // → 'field-input-1'
24
+ */
25
+ function createIdScope(componentName) {
26
+ const id = generateId(componentName);
27
+ return (part) => `${id}-${part}`;
28
+ }
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.trapFocus = trapFocus;
4
+ function trapFocus(element) {
5
+ const focusableSelectors = 'a[href], button, textarea, input, select, [tabindex]:not([tabindex="-1"])';
6
+ const focusable = Array.from(element.querySelectorAll(focusableSelectors));
7
+ if (!focusable.length)
8
+ return;
9
+ const first = focusable[0];
10
+ const last = focusable[focusable.length - 1];
11
+ const handleKeyDown = (e) => {
12
+ if (e.key !== 'Tab')
13
+ return;
14
+ if (e.shiftKey) {
15
+ if (document.activeElement === first) {
16
+ e.preventDefault();
17
+ last.focus();
18
+ }
19
+ }
20
+ else {
21
+ if (document.activeElement === last) {
22
+ e.preventDefault();
23
+ first.focus();
24
+ }
25
+ }
26
+ };
27
+ element.addEventListener('keydown', handleKeyDown);
28
+ // Return cleanup function
29
+ return () => element.removeEventListener('keydown', handleKeyDown);
30
+ }
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.rovingTabIndex = rovingTabIndex;
4
+ function rovingTabIndex(container, options) {
5
+ const focusable = Array.from(container.querySelectorAll('button:not([disabled]), [role="button"], a[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])'));
6
+ let activeIndex = options?.activeIndex ?? 0;
7
+ const setActive = (index) => {
8
+ activeIndex = index;
9
+ focusable.forEach((el, i) => {
10
+ el.setAttribute('tabindex', i === index ? '0' : '-1');
11
+ });
12
+ focusable[index]?.focus();
13
+ };
14
+ if (focusable.length > 0) {
15
+ setActive(activeIndex);
16
+ }
17
+ const handleKeyDown = (e) => {
18
+ if (!['ArrowRight', 'ArrowLeft', 'ArrowDown', 'ArrowUp', 'Home', 'End'].includes(e.key))
19
+ return;
20
+ e.preventDefault();
21
+ if (e.key === 'ArrowRight' || e.key === 'ArrowDown') {
22
+ setActive((activeIndex + 1) % focusable.length);
23
+ }
24
+ else if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') {
25
+ setActive((activeIndex - 1 + focusable.length) % focusable.length);
26
+ }
27
+ else if (e.key === 'Home') {
28
+ setActive(0);
29
+ }
30
+ else if (e.key === 'End') {
31
+ setActive(focusable.length - 1);
32
+ }
33
+ };
34
+ container.addEventListener('keydown', handleKeyDown);
35
+ return () => container.removeEventListener('keydown', handleKeyDown);
36
+ }
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useFocusVisible = useFocusVisible;
4
+ /**
5
+ * Tracks whether focus was triggered via keyboard input and applies
6
+ * the `focus-visible` class accordingly — matching the CSS :focus-visible spec.
7
+ * Returns a cleanup function to remove all listeners.
8
+ */
9
+ function useFocusVisible(element) {
10
+ let hadKeyboardEvent = false;
11
+ const onPointerDown = () => {
12
+ hadKeyboardEvent = false;
13
+ };
14
+ const onKeyDown = (e) => {
15
+ // Ignore modifier-only keypresses
16
+ if (!['Tab', 'ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'Enter', ' '].includes(e.key))
17
+ return;
18
+ hadKeyboardEvent = true;
19
+ };
20
+ const onFocus = (e) => {
21
+ const target = e.target;
22
+ if (target && hadKeyboardEvent) {
23
+ target.classList.add('focus-visible');
24
+ }
25
+ };
26
+ const onBlur = (e) => {
27
+ const target = e.target;
28
+ if (target) {
29
+ target.classList.remove('focus-visible');
30
+ }
31
+ };
32
+ element.addEventListener('pointerdown', onPointerDown, true);
33
+ element.addEventListener('keydown', onKeyDown, true);
34
+ element.addEventListener('focus', onFocus, true);
35
+ element.addEventListener('blur', onBlur, true);
36
+ return () => {
37
+ element.removeEventListener('pointerdown', onPointerDown, true);
38
+ element.removeEventListener('keydown', onKeyDown, true);
39
+ element.removeEventListener('focus', onFocus, true);
40
+ element.removeEventListener('blur', onBlur, true);
41
+ };
42
+ }
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./primitives/button/index"), exports);
18
+ __exportStar(require("./focus/focus-trap"), exports);
19
+ __exportStar(require("./focus/roving-tabindex"), exports);
20
+ __exportStar(require("./focus/use-focus-visible"), exports);
21
+ __exportStar(require("./a11y/aria-helpers"), exports);
22
+ __exportStar(require("./a11y/id-generator"), exports);
23
+ __exportStar(require("./state/controllable-state"), exports);
24
+ __exportStar(require("./state/create-machine"), exports);
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "commonjs"
3
+ }
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buttonMachine = void 0;
4
+ const create_machine_js_1 = require("../../state/create-machine");
5
+ exports.buttonMachine = (0, create_machine_js_1.createMachine)({
6
+ id: 'button',
7
+ initial: 'idle',
8
+ context: { disabled: false },
9
+ states: {
10
+ idle: { PRESS: 'pressed' },
11
+ pressed: { RELEASE: 'idle' },
12
+ },
13
+ });
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./button.machine"), exports);
18
+ __exportStar(require("./button.types"), exports);
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isControlled = isControlled;
4
+ exports.resolveInitialState = resolveInitialState;
5
+ /**
6
+ * Determines whether a value is controlled (externally managed) or uncontrolled.
7
+ * Returns the resolved value and a no-op setter indicator for framework adapters.
8
+ *
9
+ * Framework adapters (e.g. react/) are responsible for implementing
10
+ * useControllableState using this utility as a type helper.
11
+ */
12
+ function isControlled(value) {
13
+ return typeof value !== 'undefined';
14
+ }
15
+ /**
16
+ * Resolves the initial state for an uncontrolled component.
17
+ * If `controlled` is defined, returns it directly.
18
+ * Otherwise returns `defaultValue`.
19
+ */
20
+ function resolveInitialState(controlled, defaultValue) {
21
+ return isControlled(controlled) ? controlled : defaultValue;
22
+ }
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ /**
3
+ * Lightweight state machine factory — no external dependencies.
4
+ * A minimal alternative to XState for simple state transitions.
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.createMachine = createMachine;
8
+ exports.assign = assign;
9
+ exports.setup = setup;
10
+ function createMachine(config) {
11
+ return {
12
+ id: config.id,
13
+ initialState: config.initial,
14
+ initialContext: config.context,
15
+ transition(state, event) {
16
+ return config.states[state]?.[event] ?? state;
17
+ },
18
+ };
19
+ }
20
+ /** No-op assign helper for API compatibility */
21
+ function assign(updater) {
22
+ return updater;
23
+ }
24
+ /** No-op setup helper for API compatibility */
25
+ function setup(_config) {
26
+ return { createMachine };
27
+ }
@@ -0,0 +1,2 @@
1
+ export declare function trapFocus(element: HTMLElement): (() => void) | undefined;
2
+ //# sourceMappingURL=focus-trap.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"focus-trap.d.ts","sourceRoot":"","sources":["../../src/focus/focus-trap.ts"],"names":[],"mappings":"AAAA,wBAAgB,SAAS,CAAC,OAAO,EAAE,WAAW,4BAyB7C"}
@@ -0,0 +1,28 @@
1
+ export function trapFocus(element) {
2
+ const focusableSelectors = 'a[href], button, textarea, input, select, [tabindex]:not([tabindex="-1"])';
3
+ const focusable = Array.from(element.querySelectorAll(focusableSelectors));
4
+ if (!focusable.length)
5
+ return;
6
+ const first = focusable[0];
7
+ const last = focusable[focusable.length - 1];
8
+ const handleKeyDown = (e) => {
9
+ if (e.key !== 'Tab')
10
+ return;
11
+ if (e.shiftKey) {
12
+ if (document.activeElement === first) {
13
+ e.preventDefault();
14
+ last.focus();
15
+ }
16
+ }
17
+ else {
18
+ if (document.activeElement === last) {
19
+ e.preventDefault();
20
+ first.focus();
21
+ }
22
+ }
23
+ };
24
+ element.addEventListener('keydown', handleKeyDown);
25
+ // Return cleanup function
26
+ return () => element.removeEventListener('keydown', handleKeyDown);
27
+ }
28
+ //# sourceMappingURL=focus-trap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"focus-trap.js","sourceRoot":"","sources":["../../src/focus/focus-trap.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,SAAS,CAAC,OAAoB;IAC5C,MAAM,kBAAkB,GAAG,2EAAoF,CAAC;IAChH,MAAM,SAAS,GAAG,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,gBAAgB,CAAC,kBAAkB,CAAC,CAAkB,CAAC;IAC5F,IAAI,CAAC,SAAS,CAAC,MAAM;QAAE,OAAO;IAC9B,MAAM,KAAK,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC;IAC3B,MAAM,IAAI,GAAG,SAAS,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAE7C,MAAM,aAAa,GAAG,CAAC,CAAgB,EAAE,EAAE;QACzC,IAAI,CAAC,CAAC,GAAG,KAAK,KAAK;YAAE,OAAO;QAC5B,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC;YACf,IAAI,QAAQ,CAAC,aAAa,KAAK,KAAK,EAAE,CAAC;gBACrC,CAAC,CAAC,cAAc,EAAE,CAAC;gBACnB,IAAI,CAAC,KAAK,EAAE,CAAC;YACf,CAAC;QACH,CAAC;aAAM,CAAC;YACN,IAAI,QAAQ,CAAC,aAAa,KAAK,IAAI,EAAE,CAAC;gBACpC,CAAC,CAAC,cAAc,EAAE,CAAC;gBACnB,KAAK,CAAC,KAAK,EAAE,CAAC;YAChB,CAAC;QACH,CAAC;IACH,CAAC,CAAC;IAEF,OAAO,CAAC,gBAAgB,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;IACnD,0BAA0B;IAC1B,OAAO,GAAG,EAAE,CAAC,OAAO,CAAC,mBAAmB,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;AACrE,CAAC"}
@@ -0,0 +1,4 @@
1
+ export declare function rovingTabIndex(container: HTMLElement, options?: {
2
+ activeIndex?: number;
3
+ }): () => void;
4
+ //# sourceMappingURL=roving-tabindex.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"roving-tabindex.d.ts","sourceRoot":"","sources":["../../src/focus/roving-tabindex.ts"],"names":[],"mappings":"AAAA,wBAAgB,cAAc,CAAC,SAAS,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,cAqCxF"}
@@ -0,0 +1,34 @@
1
+ export function rovingTabIndex(container, options) {
2
+ const focusable = Array.from(container.querySelectorAll('button:not([disabled]), [role="button"], a[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])'));
3
+ let activeIndex = options?.activeIndex ?? 0;
4
+ const setActive = (index) => {
5
+ activeIndex = index;
6
+ focusable.forEach((el, i) => {
7
+ el.setAttribute('tabindex', i === index ? '0' : '-1');
8
+ });
9
+ focusable[index]?.focus();
10
+ };
11
+ if (focusable.length > 0) {
12
+ setActive(activeIndex);
13
+ }
14
+ const handleKeyDown = (e) => {
15
+ if (!['ArrowRight', 'ArrowLeft', 'ArrowDown', 'ArrowUp', 'Home', 'End'].includes(e.key))
16
+ return;
17
+ e.preventDefault();
18
+ if (e.key === 'ArrowRight' || e.key === 'ArrowDown') {
19
+ setActive((activeIndex + 1) % focusable.length);
20
+ }
21
+ else if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') {
22
+ setActive((activeIndex - 1 + focusable.length) % focusable.length);
23
+ }
24
+ else if (e.key === 'Home') {
25
+ setActive(0);
26
+ }
27
+ else if (e.key === 'End') {
28
+ setActive(focusable.length - 1);
29
+ }
30
+ };
31
+ container.addEventListener('keydown', handleKeyDown);
32
+ return () => container.removeEventListener('keydown', handleKeyDown);
33
+ }
34
+ //# sourceMappingURL=roving-tabindex.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"roving-tabindex.js","sourceRoot":"","sources":["../../src/focus/roving-tabindex.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,cAAc,CAAC,SAAsB,EAAE,OAAkC;IACvF,MAAM,SAAS,GAAG,KAAK,CAAC,IAAI,CAC1B,SAAS,CAAC,gBAAgB,CACxB,4JAA4J,CAC7J,CACF,CAAC;IACF,IAAI,WAAW,GAAG,OAAO,EAAE,WAAW,IAAI,CAAC,CAAC;IAE5C,MAAM,SAAS,GAAG,CAAC,KAAa,EAAE,EAAE;QAClC,WAAW,GAAG,KAAK,CAAC;QACpB,SAAS,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,EAAE;YAC1B,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACxD,CAAC,CAAC,CAAC;QACH,SAAS,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,CAAC;IAC5B,CAAC,CAAC;IAEF,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACzB,SAAS,CAAC,WAAW,CAAC,CAAC;IACzB,CAAC;IAED,MAAM,aAAa,GAAG,CAAC,CAAgB,EAAE,EAAE;QACzC,IAAI,CAAC,CAAC,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC;YAAE,OAAO;QAChG,CAAC,CAAC,cAAc,EAAE,CAAC;QAEnB,IAAI,CAAC,CAAC,GAAG,KAAK,YAAY,IAAI,CAAC,CAAC,GAAG,KAAK,WAAW,EAAE,CAAC;YACpD,SAAS,CAAC,CAAC,WAAW,GAAG,CAAC,CAAC,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;QAClD,CAAC;aAAM,IAAI,CAAC,CAAC,GAAG,KAAK,WAAW,IAAI,CAAC,CAAC,GAAG,KAAK,SAAS,EAAE,CAAC;YACxD,SAAS,CAAC,CAAC,WAAW,GAAG,CAAC,GAAG,SAAS,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;QACrE,CAAC;aAAM,IAAI,CAAC,CAAC,GAAG,KAAK,MAAM,EAAE,CAAC;YAC5B,SAAS,CAAC,CAAC,CAAC,CAAC;QACf,CAAC;aAAM,IAAI,CAAC,CAAC,GAAG,KAAK,KAAK,EAAE,CAAC;YAC3B,SAAS,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAClC,CAAC;IACH,CAAC,CAAC;IAEF,SAAS,CAAC,gBAAgB,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;IACrD,OAAO,GAAG,EAAE,CAAC,SAAS,CAAC,mBAAmB,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;AACvE,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Tracks whether focus was triggered via keyboard input and applies
3
+ * the `focus-visible` class accordingly — matching the CSS :focus-visible spec.
4
+ * Returns a cleanup function to remove all listeners.
5
+ */
6
+ export declare function useFocusVisible(element: HTMLElement): () => void;
7
+ //# sourceMappingURL=use-focus-visible.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-focus-visible.d.ts","sourceRoot":"","sources":["../../src/focus/use-focus-visible.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,IAAI,CAsChE"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Tracks whether focus was triggered via keyboard input and applies
3
+ * the `focus-visible` class accordingly — matching the CSS :focus-visible spec.
4
+ * Returns a cleanup function to remove all listeners.
5
+ */
6
+ export function useFocusVisible(element) {
7
+ let hadKeyboardEvent = false;
8
+ const onPointerDown = () => {
9
+ hadKeyboardEvent = false;
10
+ };
11
+ const onKeyDown = (e) => {
12
+ // Ignore modifier-only keypresses
13
+ if (!['Tab', 'ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'Enter', ' '].includes(e.key))
14
+ return;
15
+ hadKeyboardEvent = true;
16
+ };
17
+ const onFocus = (e) => {
18
+ const target = e.target;
19
+ if (target && hadKeyboardEvent) {
20
+ target.classList.add('focus-visible');
21
+ }
22
+ };
23
+ const onBlur = (e) => {
24
+ const target = e.target;
25
+ if (target) {
26
+ target.classList.remove('focus-visible');
27
+ }
28
+ };
29
+ element.addEventListener('pointerdown', onPointerDown, true);
30
+ element.addEventListener('keydown', onKeyDown, true);
31
+ element.addEventListener('focus', onFocus, true);
32
+ element.addEventListener('blur', onBlur, true);
33
+ return () => {
34
+ element.removeEventListener('pointerdown', onPointerDown, true);
35
+ element.removeEventListener('keydown', onKeyDown, true);
36
+ element.removeEventListener('focus', onFocus, true);
37
+ element.removeEventListener('blur', onBlur, true);
38
+ };
39
+ }
40
+ //# sourceMappingURL=use-focus-visible.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-focus-visible.js","sourceRoot":"","sources":["../../src/focus/use-focus-visible.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,OAAoB;IAClD,IAAI,gBAAgB,GAAG,KAAK,CAAC;IAE7B,MAAM,aAAa,GAAG,GAAG,EAAE;QACzB,gBAAgB,GAAG,KAAK,CAAC;IAC3B,CAAC,CAAC;IAEF,MAAM,SAAS,GAAG,CAAC,CAAgB,EAAE,EAAE;QACrC,kCAAkC;QAClC,IAAI,CAAC,CAAC,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,WAAW,EAAE,YAAY,EAAE,OAAO,EAAE,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC;YAAE,OAAO;QACtG,gBAAgB,GAAG,IAAI,CAAC;IAC1B,CAAC,CAAC;IAEF,MAAM,OAAO,GAAG,CAAC,CAAa,EAAE,EAAE;QAChC,MAAM,MAAM,GAAG,CAAC,CAAC,MAA4B,CAAC;QAC9C,IAAI,MAAM,IAAI,gBAAgB,EAAE,CAAC;YAC/B,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;QACxC,CAAC;IACH,CAAC,CAAC;IAEF,MAAM,MAAM,GAAG,CAAC,CAAa,EAAE,EAAE;QAC/B,MAAM,MAAM,GAAG,CAAC,CAAC,MAA4B,CAAC;QAC9C,IAAI,MAAM,EAAE,CAAC;YACX,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;QAC3C,CAAC;IACH,CAAC,CAAC;IAEF,OAAO,CAAC,gBAAgB,CAAC,aAAa,EAAE,aAAa,EAAE,IAAI,CAAC,CAAC;IAC7D,OAAO,CAAC,gBAAgB,CAAC,SAAS,EAAE,SAAS,EAAE,IAAI,CAAC,CAAC;IACrD,OAAO,CAAC,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;IACjD,OAAO,CAAC,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;IAE/C,OAAO,GAAG,EAAE;QACV,OAAO,CAAC,mBAAmB,CAAC,aAAa,EAAE,aAAa,EAAE,IAAI,CAAC,CAAC;QAChE,OAAO,CAAC,mBAAmB,CAAC,SAAS,EAAE,SAAS,EAAE,IAAI,CAAC,CAAC;QACxD,OAAO,CAAC,mBAAmB,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;QACpD,OAAO,CAAC,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;IACpD,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,9 @@
1
+ export * from './primitives/button/index.js';
2
+ export * from './focus/focus-trap.js';
3
+ export * from './focus/roving-tabindex.js';
4
+ export * from './focus/use-focus-visible.js';
5
+ export * from './a11y/aria-helpers.js';
6
+ export * from './a11y/id-generator.js';
7
+ export * from './state/controllable-state.js';
8
+ export * from './state/create-machine.js';
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,8BAA8B,CAAC;AAC7C,cAAc,uBAAuB,CAAC;AACtC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,wBAAwB,CAAC;AACvC,cAAc,wBAAwB,CAAC;AACvC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,2BAA2B,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,9 @@
1
+ export * from './primitives/button/index.js';
2
+ export * from './focus/focus-trap.js';
3
+ export * from './focus/roving-tabindex.js';
4
+ export * from './focus/use-focus-visible.js';
5
+ export * from './a11y/aria-helpers.js';
6
+ export * from './a11y/id-generator.js';
7
+ export * from './state/controllable-state.js';
8
+ export * from './state/create-machine.js';
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,8BAA8B,CAAC;AAC7C,cAAc,uBAAuB,CAAC;AACtC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,wBAAwB,CAAC;AACvC,cAAc,wBAAwB,CAAC;AACvC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,2BAA2B,CAAC"}
@@ -0,0 +1,5 @@
1
+ import type { ButtonContext } from './button.types.js';
2
+ type ButtonState = 'idle' | 'pressed';
3
+ export declare const buttonMachine: import("../../state/create-machine.js").MachineInstance<ButtonState, "PRESS" | "RELEASE", ButtonContext>;
4
+ export {};
5
+ //# sourceMappingURL=button.machine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"button.machine.d.ts","sourceRoot":"","sources":["../../../src/primitives/button/button.machine.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAe,MAAM,mBAAmB,CAAC;AAEpE,KAAK,WAAW,GAAG,MAAM,GAAG,SAAS,CAAC;AAGtC,eAAO,MAAM,aAAa,0GAQxB,CAAC"}
@@ -0,0 +1,11 @@
1
+ import { createMachine } from '../../state/create-machine.js';
2
+ export const buttonMachine = createMachine({
3
+ id: 'button',
4
+ initial: 'idle',
5
+ context: { disabled: false },
6
+ states: {
7
+ idle: { PRESS: 'pressed' },
8
+ pressed: { RELEASE: 'idle' },
9
+ },
10
+ });
11
+ //# sourceMappingURL=button.machine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"button.machine.js","sourceRoot":"","sources":["../../../src/primitives/button/button.machine.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAM9D,MAAM,CAAC,MAAM,aAAa,GAAG,aAAa,CAA8C;IACtF,EAAE,EAAE,QAAQ;IACZ,OAAO,EAAE,MAAM;IACf,OAAO,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE;IAC5B,MAAM,EAAE;QACN,IAAI,EAAK,EAAE,KAAK,EAAE,SAAS,EAAE;QAC7B,OAAO,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE;KAC7B;CACF,CAAC,CAAC"}
@@ -0,0 +1,9 @@
1
+ export interface ButtonContext {
2
+ disabled: boolean;
3
+ }
4
+ export type ButtonEvent = {
5
+ type: 'PRESS';
6
+ } | {
7
+ type: 'RELEASE';
8
+ };
9
+ //# sourceMappingURL=button.types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"button.types.d.ts","sourceRoot":"","sources":["../../../src/primitives/button/button.types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,MAAM,WAAW,GACnB;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,GACjB;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=button.types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"button.types.js","sourceRoot":"","sources":["../../../src/primitives/button/button.types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,3 @@
1
+ export * from './button.machine.js';
2
+ export * from './button.types.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/primitives/button/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC"}
@@ -0,0 +1,3 @@
1
+ export * from './button.machine.js';
2
+ export * from './button.types.js';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/primitives/button/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Determines whether a value is controlled (externally managed) or uncontrolled.
3
+ * Returns the resolved value and a no-op setter indicator for framework adapters.
4
+ *
5
+ * Framework adapters (e.g. react/) are responsible for implementing
6
+ * useControllableState using this utility as a type helper.
7
+ */
8
+ export declare function isControlled<T>(value: T | undefined): value is T;
9
+ /**
10
+ * Resolves the initial state for an uncontrolled component.
11
+ * If `controlled` is defined, returns it directly.
12
+ * Otherwise returns `defaultValue`.
13
+ */
14
+ export declare function resolveInitialState<T>(controlled: T | undefined, defaultValue: T): T;
15
+ //# sourceMappingURL=controllable-state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"controllable-state.d.ts","sourceRoot":"","sources":["../../src/state/controllable-state.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,SAAS,GAAG,KAAK,IAAI,CAAC,CAEhE;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,GAAG,SAAS,EAAE,YAAY,EAAE,CAAC,GAAG,CAAC,CAEpF"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Determines whether a value is controlled (externally managed) or uncontrolled.
3
+ * Returns the resolved value and a no-op setter indicator for framework adapters.
4
+ *
5
+ * Framework adapters (e.g. react/) are responsible for implementing
6
+ * useControllableState using this utility as a type helper.
7
+ */
8
+ export function isControlled(value) {
9
+ return typeof value !== 'undefined';
10
+ }
11
+ /**
12
+ * Resolves the initial state for an uncontrolled component.
13
+ * If `controlled` is defined, returns it directly.
14
+ * Otherwise returns `defaultValue`.
15
+ */
16
+ export function resolveInitialState(controlled, defaultValue) {
17
+ return isControlled(controlled) ? controlled : defaultValue;
18
+ }
19
+ //# sourceMappingURL=controllable-state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"controllable-state.js","sourceRoot":"","sources":["../../src/state/controllable-state.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAAI,KAAoB;IAClD,OAAO,OAAO,KAAK,KAAK,WAAW,CAAC;AACtC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAI,UAAyB,EAAE,YAAe;IAC/E,OAAO,YAAY,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,YAAY,CAAC;AAC9D,CAAC"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Lightweight state machine factory — no external dependencies.
3
+ * A minimal alternative to XState for simple state transitions.
4
+ */
5
+ export type Transition<S extends string, E extends string> = {
6
+ [state in S]?: {
7
+ [event in E]?: S;
8
+ };
9
+ };
10
+ export interface MachineConfig<S extends string, E extends string, C> {
11
+ id: string;
12
+ initial: S;
13
+ context: C;
14
+ states: Transition<S, E>;
15
+ }
16
+ export interface MachineInstance<S extends string, E extends string, C> {
17
+ id: string;
18
+ initialState: S;
19
+ initialContext: C;
20
+ transition: (state: S, event: E) => S;
21
+ }
22
+ export declare function createMachine<S extends string, E extends string, C>(config: MachineConfig<S, E, C>): MachineInstance<S, E, C>;
23
+ /** No-op assign helper for API compatibility */
24
+ export declare function assign<C>(updater: Partial<C> | ((ctx: C) => Partial<C>)): typeof updater;
25
+ /** No-op setup helper for API compatibility */
26
+ export declare function setup<_T>(_config: _T): {
27
+ createMachine: typeof createMachine;
28
+ };
29
+ //# sourceMappingURL=create-machine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-machine.d.ts","sourceRoot":"","sources":["../../src/state/create-machine.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,MAAM,UAAU,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,MAAM,IAAI;KAC1D,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE;SAAG,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;KAAE;CACpC,CAAC;AAEF,MAAM,WAAW,aAAa,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,MAAM,EAAE,CAAC;IAClE,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,CAAC,CAAC;IACX,OAAO,EAAE,CAAC,CAAC;IACX,MAAM,EAAE,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;CAC1B;AAED,MAAM,WAAW,eAAe,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,MAAM,EAAE,CAAC;IACpE,EAAE,EAAE,MAAM,CAAC;IACX,YAAY,EAAE,CAAC,CAAC;IAChB,cAAc,EAAE,CAAC,CAAC;IAClB,UAAU,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,KAAK,CAAC,CAAC;CACvC;AAED,wBAAgB,aAAa,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,MAAM,EAAE,CAAC,EACjE,MAAM,EAAE,aAAa,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAC7B,eAAe,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAS1B;AAED,gDAAgD;AAChD,wBAAgB,MAAM,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,OAAO,OAAO,CAExF;AAED,+CAA+C;AAC/C,wBAAgB,KAAK,CAAC,EAAE,EAAE,OAAO,EAAE,EAAE,GAAG;IAAE,aAAa,EAAE,OAAO,aAAa,CAAA;CAAE,CAE9E"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Lightweight state machine factory — no external dependencies.
3
+ * A minimal alternative to XState for simple state transitions.
4
+ */
5
+ export function createMachine(config) {
6
+ return {
7
+ id: config.id,
8
+ initialState: config.initial,
9
+ initialContext: config.context,
10
+ transition(state, event) {
11
+ return config.states[state]?.[event] ?? state;
12
+ },
13
+ };
14
+ }
15
+ /** No-op assign helper for API compatibility */
16
+ export function assign(updater) {
17
+ return updater;
18
+ }
19
+ /** No-op setup helper for API compatibility */
20
+ export function setup(_config) {
21
+ return { createMachine };
22
+ }
23
+ //# sourceMappingURL=create-machine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-machine.js","sourceRoot":"","sources":["../../src/state/create-machine.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAoBH,MAAM,UAAU,aAAa,CAC3B,MAA8B;IAE9B,OAAO;QACL,EAAE,EAAE,MAAM,CAAC,EAAE;QACb,YAAY,EAAE,MAAM,CAAC,OAAO;QAC5B,cAAc,EAAE,MAAM,CAAC,OAAO;QAC9B,UAAU,CAAC,KAAQ,EAAE,KAAQ;YAC3B,OAAQ,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAmB,IAAI,KAAK,CAAC;QACnE,CAAC;KACF,CAAC;AACJ,CAAC;AAED,gDAAgD;AAChD,MAAM,UAAU,MAAM,CAAI,OAA8C;IACtE,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,+CAA+C;AAC/C,MAAM,UAAU,KAAK,CAAK,OAAW;IACnC,OAAO,EAAE,aAAa,EAAE,CAAC;AAC3B,CAAC"}
package/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "@tokis/core",
3
+ "version": "1.0.0",
4
+ "description": "Framework-agnostic headless primitives for Tokis — state machines, accessibility helpers, and focus management. Zero dependencies.",
5
+ "type": "module",
6
+ "main": "./dist/cjs/index.js",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "require": "./dist/cjs/index.js",
12
+ "import": "./dist/index.js",
13
+ "types": "./dist/index.d.ts"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist",
18
+ "README.md",
19
+ "LICENSE"
20
+ ],
21
+ "sideEffects": false,
22
+ "scripts": {
23
+ "build": "tsc && tsc -p tsconfig.cjs.json && node ../../scripts/stamp-cjs.js",
24
+ "typecheck": "tsc --noEmit",
25
+ "prepack": "npm run build"
26
+ },
27
+ "keywords": [
28
+ "tokis",
29
+ "design-system",
30
+ "headless",
31
+ "headless-ui",
32
+ "accessibility",
33
+ "a11y",
34
+ "aria",
35
+ "wcag",
36
+ "state-machine",
37
+ "focus-management",
38
+ "focus-trap",
39
+ "roving-tabindex",
40
+ "framework-agnostic",
41
+ "typescript",
42
+ "zero-dependencies"
43
+ ],
44
+ "author": "Tokis Contributors",
45
+ "license": "MIT",
46
+ "repository": {
47
+ "type": "git",
48
+ "url": "https://github.com/tokis-design/tokis.git",
49
+ "directory": "packages/core"
50
+ },
51
+ "homepage": "https://tokis.dev",
52
+ "bugs": {
53
+ "url": "https://github.com/tokis-design/tokis/issues"
54
+ },
55
+ "publishConfig": {
56
+ "access": "public",
57
+ "registry": "https://registry.npmjs.org/"
58
+ }
59
+ }