@ttoss/fsl-ui 0.2.5 → 0.2.7

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 (38) hide show
  1. package/README.md +1 -1
  2. package/dist/components/AppShell/AppShell.cjs +8 -4
  3. package/dist/components/AppShell/AppShell.mjs +8 -4
  4. package/dist/components/Container/Container.cjs +2 -1
  5. package/dist/components/Container/Container.mjs +2 -1
  6. package/dist/components/Grid/Grid.cjs +20 -1
  7. package/dist/components/Grid/Grid.d.cts +5 -0
  8. package/dist/components/Grid/Grid.d.mts +5 -0
  9. package/dist/components/Grid/Grid.mjs +18 -1
  10. package/dist/components/Icon/Icon.cjs +2 -1
  11. package/dist/components/Icon/Icon.d.cts +59 -0
  12. package/dist/components/Icon/Icon.d.mts +59 -0
  13. package/dist/components/Icon/Icon.mjs +1 -1
  14. package/dist/components/Icon/glyphs.cjs +37 -10
  15. package/dist/components/Icon/glyphs.mjs +34 -10
  16. package/dist/components/Icon/intents.cjs +32 -0
  17. package/dist/components/Icon/intents.d.cts +33 -0
  18. package/dist/components/Icon/intents.d.mts +33 -0
  19. package/dist/components/Icon/intents.mjs +32 -0
  20. package/dist/components/Table/Table.cjs +248 -0
  21. package/dist/components/Table/Table.d.cts +128 -0
  22. package/dist/components/Table/Table.d.mts +128 -0
  23. package/dist/components/Table/Table.mjs +239 -0
  24. package/dist/components/Tabs/Tabs.cjs +70 -27
  25. package/dist/components/Tabs/Tabs.d.cts +0 -17
  26. package/dist/components/Tabs/Tabs.d.mts +0 -17
  27. package/dist/components/Tabs/Tabs.mjs +68 -27
  28. package/dist/components/Text/Text.cjs +1 -0
  29. package/dist/components/Text/Text.d.cts +7 -2
  30. package/dist/components/Text/Text.d.mts +7 -2
  31. package/dist/components/Text/Text.mjs +1 -0
  32. package/dist/index.cjs +16 -0
  33. package/dist/index.d.cts +4 -1
  34. package/dist/index.d.mts +4 -1
  35. package/dist/index.mjs +4 -1
  36. package/llms.txt +45 -9
  37. package/package.json +4 -4
  38. package/src/tokens/CONTRACT.md +5 -6
package/README.md CHANGED
@@ -59,7 +59,7 @@ Composites accept no `style`/`className`. Geometry the host legitimately owns is
59
59
 
60
60
  The registered knobs and the full policy live in `src/tokens/CONTRACT.md` §7.
61
61
 
62
- Icons are internal: components render glyphs through a semantic layer over the [Iconify](https://iconify.design/) provider (default set: Lucide), registered offline (no runtime network fetch). There is no public `Icon` export yet see CONTRIBUTING ADR-005.
62
+ Icons are semantic: `Icon` (public since ADR-010) is named by intent (`<Icon intent="status.success" />`), never by glyph. The theme maps intents to [Iconify](https://iconify.design/) glyphs (default set: Lucide), registered offline (no runtime network fetch); color inherits via `currentColor` and size comes from `vars.sizing.icon.*`.
63
63
 
64
64
  ## AI-first surface
65
65
 
@@ -71,7 +71,8 @@ var AppShell = ({
71
71
  "data-scope": "app-shell",
72
72
  "data-part": "header",
73
73
  style: {
74
- borderBlockEnd: HAIRLINE
74
+ borderBlockEnd: HAIRLINE,
75
+ containerType: "inline-size"
75
76
  },
76
77
  children: header
77
78
  }) : null, /* @__PURE__ */(0, react_jsx_runtime.jsxs)("div", {
@@ -93,7 +94,8 @@ var AppShell = ({
93
94
  "aria-label": sidebarLabel,
94
95
  style: {
95
96
  overflowY: "auto",
96
- borderInlineEnd: HAIRLINE
97
+ borderInlineEnd: HAIRLINE,
98
+ containerType: "inline-size"
97
99
  },
98
100
  children: sidebar
99
101
  }) : null, /* @__PURE__ */(0, react_jsx_runtime.jsx)("main", {
@@ -101,7 +103,8 @@ var AppShell = ({
101
103
  "data-part": "main",
102
104
  style: {
103
105
  overflow: "auto",
104
- minInlineSize: 0
106
+ minInlineSize: 0,
107
+ containerType: "inline-size"
105
108
  },
106
109
  children
107
110
  }), hasAside ? /* @__PURE__ */(0, react_jsx_runtime.jsx)("aside", {
@@ -110,7 +113,8 @@ var AppShell = ({
110
113
  "aria-label": asideLabel,
111
114
  style: {
112
115
  overflowY: "auto",
113
- borderInlineStart: HAIRLINE
116
+ borderInlineStart: HAIRLINE,
117
+ containerType: "inline-size"
114
118
  },
115
119
  children: aside
116
120
  }) : null]
@@ -71,7 +71,8 @@ var AppShell = ({
71
71
  "data-scope": "app-shell",
72
72
  "data-part": "header",
73
73
  style: {
74
- borderBlockEnd: HAIRLINE
74
+ borderBlockEnd: HAIRLINE,
75
+ containerType: "inline-size"
75
76
  },
76
77
  children: header
77
78
  }) : null, /* @__PURE__ */jsxs("div", {
@@ -93,7 +94,8 @@ var AppShell = ({
93
94
  "aria-label": sidebarLabel,
94
95
  style: {
95
96
  overflowY: "auto",
96
- borderInlineEnd: HAIRLINE
97
+ borderInlineEnd: HAIRLINE,
98
+ containerType: "inline-size"
97
99
  },
98
100
  children: sidebar
99
101
  }) : null, /* @__PURE__ */jsx("main", {
@@ -101,7 +103,8 @@ var AppShell = ({
101
103
  "data-part": "main",
102
104
  style: {
103
105
  overflow: "auto",
104
- minInlineSize: 0
106
+ minInlineSize: 0,
107
+ containerType: "inline-size"
105
108
  },
106
109
  children
107
110
  }), hasAside ? /* @__PURE__ */jsx("aside", {
@@ -110,7 +113,8 @@ var AppShell = ({
110
113
  "aria-label": asideLabel,
111
114
  style: {
112
115
  overflowY: "auto",
113
- borderInlineStart: HAIRLINE
116
+ borderInlineStart: HAIRLINE,
117
+ containerType: "inline-size"
114
118
  },
115
119
  children: aside
116
120
  }) : null]
@@ -53,7 +53,8 @@ var Container = ({
53
53
  marginInline: "auto",
54
54
  width: "100%",
55
55
  maxWidth: MAX_WIDTH[size],
56
- paddingInline: GUTTER[gutter]
56
+ paddingInline: GUTTER[gutter],
57
+ containerType: "inline-size"
57
58
  },
58
59
  children
59
60
  });
@@ -53,7 +53,8 @@ var Container = ({
53
53
  marginInline: "auto",
54
54
  width: "100%",
55
55
  maxWidth: MAX_WIDTH[size],
56
- paddingInline: GUTTER[gutter]
56
+ paddingInline: GUTTER[gutter],
57
+ containerType: "inline-size"
57
58
  },
58
59
  children
59
60
  });
@@ -1,6 +1,9 @@
1
1
  /** Powered by @ttoss/config. https://ttoss.dev/docs/modules/packages/config/ */
2
+ const require_runtime = require('../../_virtual/_rolldown/runtime.cjs');
2
3
  let _ttoss_fsl_theme_vars = require("@ttoss/fsl-theme/vars");
3
4
  let react_jsx_runtime = require("react/jsx-runtime");
5
+ let react = require("react");
6
+ react = require_runtime.__toESM(react);
4
7
 
5
8
  //#region src/components/Grid/Grid.tsx
6
9
  /** Formal semantic identity — Grid root (Structure entity, 2D layout). */
@@ -45,6 +48,11 @@ var autoTracks = min => {
45
48
  * named scale, so grid rhythm stays consistent across a product. For a single
46
49
  * axis of flow, reach for `Stack` instead.
47
50
  *
51
+ * Each child is hosted in a `data-part="item"` wrapper that establishes
52
+ * `container-type: inline-size` (ADR-011): the theme's container-fluid
53
+ * scales (`cqi` clamps) resolve against the item's track width, so type and
54
+ * spacing inside a narrow tile scale down instead of overflowing.
55
+ *
48
56
  * @example
49
57
  * ```tsx
50
58
  * // Fixed columns
@@ -78,7 +86,18 @@ var Grid = ({
78
86
  alignItems: ALIGN[align],
79
87
  justifyItems: ALIGN[justify]
80
88
  },
81
- children
89
+ children: react.Children.map(children, child => {
90
+ if (child === null || child === void 0) return child;
91
+ return /* @__PURE__ */(0, react_jsx_runtime.jsx)("div", {
92
+ "data-scope": "grid",
93
+ "data-part": "item",
94
+ style: {
95
+ display: "grid",
96
+ containerType: "inline-size"
97
+ },
98
+ children: child
99
+ });
100
+ })
82
101
  });
83
102
  };
84
103
  Grid.displayName = gridMeta.displayName;
@@ -62,6 +62,11 @@ interface GridProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'style' |
62
62
  * named scale, so grid rhythm stays consistent across a product. For a single
63
63
  * axis of flow, reach for `Stack` instead.
64
64
  *
65
+ * Each child is hosted in a `data-part="item"` wrapper that establishes
66
+ * `container-type: inline-size` (ADR-011): the theme's container-fluid
67
+ * scales (`cqi` clamps) resolve against the item's track width, so type and
68
+ * spacing inside a narrow tile scale down instead of overflowing.
69
+ *
65
70
  * @example
66
71
  * ```tsx
67
72
  * // Fixed columns
@@ -62,6 +62,11 @@ interface GridProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'style' |
62
62
  * named scale, so grid rhythm stays consistent across a product. For a single
63
63
  * axis of flow, reach for `Stack` instead.
64
64
  *
65
+ * Each child is hosted in a `data-part="item"` wrapper that establishes
66
+ * `container-type: inline-size` (ADR-011): the theme's container-fluid
67
+ * scales (`cqi` clamps) resolve against the item's track width, so type and
68
+ * spacing inside a narrow tile scale down instead of overflowing.
69
+ *
65
70
  * @example
66
71
  * ```tsx
67
72
  * // Fixed columns
@@ -1,6 +1,7 @@
1
1
  /** Powered by @ttoss/config. https://ttoss.dev/docs/modules/packages/config/ */
2
2
  import { vars } from "@ttoss/fsl-theme/vars";
3
3
  import { jsx } from "react/jsx-runtime";
4
+ import * as React from "react";
4
5
 
5
6
  //#region src/components/Grid/Grid.tsx
6
7
  /** Formal semantic identity — Grid root (Structure entity, 2D layout). */
@@ -45,6 +46,11 @@ var autoTracks = min => {
45
46
  * named scale, so grid rhythm stays consistent across a product. For a single
46
47
  * axis of flow, reach for `Stack` instead.
47
48
  *
49
+ * Each child is hosted in a `data-part="item"` wrapper that establishes
50
+ * `container-type: inline-size` (ADR-011): the theme's container-fluid
51
+ * scales (`cqi` clamps) resolve against the item's track width, so type and
52
+ * spacing inside a narrow tile scale down instead of overflowing.
53
+ *
48
54
  * @example
49
55
  * ```tsx
50
56
  * // Fixed columns
@@ -78,7 +84,18 @@ var Grid = ({
78
84
  alignItems: ALIGN[align],
79
85
  justifyItems: ALIGN[justify]
80
86
  },
81
- children
87
+ children: React.Children.map(children, child => {
88
+ if (child === null || child === void 0) return child;
89
+ return /* @__PURE__ */jsx("div", {
90
+ "data-scope": "grid",
91
+ "data-part": "item",
92
+ style: {
93
+ display: "grid",
94
+ containerType: "inline-size"
95
+ },
96
+ children: child
97
+ });
98
+ })
82
99
  });
83
100
  };
84
101
  Grid.displayName = gridMeta.displayName;
@@ -49,4 +49,5 @@ var Icon = ({
49
49
  Icon.displayName = iconMeta.displayName;
50
50
 
51
51
  //#endregion
52
- exports.Icon = Icon;
52
+ exports.Icon = Icon;
53
+ exports.iconMeta = iconMeta;
@@ -0,0 +1,59 @@
1
+
2
+ import { IconIntent } from "./intents.cjs";
3
+
4
+ //#region src/components/Icon/Icon.d.ts
5
+ /** Formal semantic identity — Icon (Structure entity, static glyph). */
6
+ declare const iconMeta: {
7
+ readonly displayName: "Icon";
8
+ readonly entity: "Structure";
9
+ readonly structure: "icon";
10
+ };
11
+ /**
12
+ * Icon size step. Maps directly to `vars.sizing.icon.*` — the sizing scale
13
+ * icon-system.md defines as the Icon contract. This is not the density
14
+ * `size` prop CONTRACT.md §4 bans (that rule governs interactive hit
15
+ * targets): a glyph legitimately scales with the context it reinforces.
16
+ */
17
+ type IconSize = 'sm' | 'md' | 'lg';
18
+ /** Props for the Icon component. */
19
+ interface IconProps {
20
+ /**
21
+ * Semantic intent — what the icon *means* (`icon.{family}.{intent}`), not
22
+ * which glyph renders. The theme maps it to a glyph (default: Lucide).
23
+ */
24
+ intent: IconIntent;
25
+ /**
26
+ * Size step, mapped to `vars.sizing.icon.{size}`. Defaults to `md`.
27
+ */
28
+ size?: IconSize;
29
+ /**
30
+ * Accessible label. When omitted the icon is decorative (`aria-hidden`) —
31
+ * the default, since icons here reinforce a labelled host control. Supply
32
+ * a (caller-localized) label only when the icon is the sole carrier of
33
+ * meaning.
34
+ */
35
+ label?: string;
36
+ }
37
+ /**
38
+ * Renders a semantic glyph via the Iconify provider (ADR-005).
39
+ *
40
+ * Icon is named by intent and inherits its color from context
41
+ * (`currentColor`); only its size is a token. Glyphs are registered offline
42
+ * on first render (`ensureIconGlyphs`), so no network request is made.
43
+ *
44
+ * @example
45
+ * ```tsx
46
+ * <Icon intent="disclosure.expand" />
47
+ * <Icon intent="action.close" size="sm" label={closeLabel} />
48
+ * ```
49
+ */
50
+ declare const Icon: {
51
+ ({
52
+ intent,
53
+ size,
54
+ label
55
+ }: IconProps): import("react/jsx-runtime").JSX.Element;
56
+ displayName: "Icon";
57
+ };
58
+ //#endregion
59
+ export { Icon, IconProps, IconSize, iconMeta };
@@ -0,0 +1,59 @@
1
+
2
+ import { IconIntent } from "./intents.mjs";
3
+
4
+ //#region src/components/Icon/Icon.d.ts
5
+ /** Formal semantic identity — Icon (Structure entity, static glyph). */
6
+ declare const iconMeta: {
7
+ readonly displayName: "Icon";
8
+ readonly entity: "Structure";
9
+ readonly structure: "icon";
10
+ };
11
+ /**
12
+ * Icon size step. Maps directly to `vars.sizing.icon.*` — the sizing scale
13
+ * icon-system.md defines as the Icon contract. This is not the density
14
+ * `size` prop CONTRACT.md §4 bans (that rule governs interactive hit
15
+ * targets): a glyph legitimately scales with the context it reinforces.
16
+ */
17
+ type IconSize = 'sm' | 'md' | 'lg';
18
+ /** Props for the Icon component. */
19
+ interface IconProps {
20
+ /**
21
+ * Semantic intent — what the icon *means* (`icon.{family}.{intent}`), not
22
+ * which glyph renders. The theme maps it to a glyph (default: Lucide).
23
+ */
24
+ intent: IconIntent;
25
+ /**
26
+ * Size step, mapped to `vars.sizing.icon.{size}`. Defaults to `md`.
27
+ */
28
+ size?: IconSize;
29
+ /**
30
+ * Accessible label. When omitted the icon is decorative (`aria-hidden`) —
31
+ * the default, since icons here reinforce a labelled host control. Supply
32
+ * a (caller-localized) label only when the icon is the sole carrier of
33
+ * meaning.
34
+ */
35
+ label?: string;
36
+ }
37
+ /**
38
+ * Renders a semantic glyph via the Iconify provider (ADR-005).
39
+ *
40
+ * Icon is named by intent and inherits its color from context
41
+ * (`currentColor`); only its size is a token. Glyphs are registered offline
42
+ * on first render (`ensureIconGlyphs`), so no network request is made.
43
+ *
44
+ * @example
45
+ * ```tsx
46
+ * <Icon intent="disclosure.expand" />
47
+ * <Icon intent="action.close" size="sm" label={closeLabel} />
48
+ * ```
49
+ */
50
+ declare const Icon: {
51
+ ({
52
+ intent,
53
+ size,
54
+ label
55
+ }: IconProps): import("react/jsx-runtime").JSX.Element;
56
+ displayName: "Icon";
57
+ };
58
+ //#endregion
59
+ export { Icon, IconProps, IconSize, iconMeta };
@@ -49,4 +49,4 @@ var Icon$1 = ({
49
49
  Icon$1.displayName = iconMeta.displayName;
50
50
 
51
51
  //#endregion
52
- export { Icon$1 as Icon };
52
+ export { Icon$1 as Icon, iconMeta };
@@ -1,8 +1,14 @@
1
1
  /** Powered by @ttoss/config. https://ttoss.dev/docs/modules/packages/config/ */
2
2
  const require_runtime = require('../../_virtual/_rolldown/runtime.cjs');
3
3
  let _iconify_icon_react = require("@iconify-icon/react");
4
+ let _iconify_icons_lucide_arrow_down_wide_narrow_js = require("@iconify/icons-lucide/arrow-down-wide-narrow.js");
5
+ _iconify_icons_lucide_arrow_down_wide_narrow_js = require_runtime.__toESM(_iconify_icons_lucide_arrow_down_wide_narrow_js, 1);
6
+ let _iconify_icons_lucide_arrow_up_narrow_wide_js = require("@iconify/icons-lucide/arrow-up-narrow-wide.js");
7
+ _iconify_icons_lucide_arrow_up_narrow_wide_js = require_runtime.__toESM(_iconify_icons_lucide_arrow_up_narrow_wide_js, 1);
4
8
  let _iconify_icons_lucide_check_js = require("@iconify/icons-lucide/check.js");
5
9
  _iconify_icons_lucide_check_js = require_runtime.__toESM(_iconify_icons_lucide_check_js, 1);
10
+ let _iconify_icons_lucide_check_circle_2_js = require("@iconify/icons-lucide/check-circle-2.js");
11
+ _iconify_icons_lucide_check_circle_2_js = require_runtime.__toESM(_iconify_icons_lucide_check_circle_2_js, 1);
6
12
  let _iconify_icons_lucide_chevron_down_js = require("@iconify/icons-lucide/chevron-down.js");
7
13
  _iconify_icons_lucide_chevron_down_js = require_runtime.__toESM(_iconify_icons_lucide_chevron_down_js, 1);
8
14
  let _iconify_icons_lucide_chevron_right_js = require("@iconify/icons-lucide/chevron-right.js");
@@ -33,18 +39,34 @@ _iconify_icons_lucide_x_js = require_runtime.__toESM(_iconify_icons_lucide_x_js,
33
39
  * @see ../../../CONTRIBUTING.md ADR-005
34
40
  */
35
41
  /**
42
+ * The per-icon lucide modules are CommonJS (`exports.default = data` with
43
+ * `__esModule`). Bundlers disagree on default-import interop: Babel/Jest
44
+ * respect `__esModule` (import = the data), while Node-mode interop
45
+ * (Vite/Rolldown, Node ESM) makes the import the whole `module.exports` —
46
+ * `{ __esModule, default: data }`. Normalize both shapes; without this,
47
+ * `addIcon` receives an invalid payload and rejects it *silently*, every
48
+ * icon falls back to an Iconify API fetch, and no glyph renders in
49
+ * production builds (friction F-012).
50
+ */
51
+ var unwrapGlyph = mod => {
52
+ return "body" in mod ? mod : mod.default;
53
+ };
54
+ /**
36
55
  * The default (Lucide) glyph for every intent. `satisfies Record<…>` makes a
37
56
  * missing intent a compile error — the completeness rule from icon-system.md.
38
57
  */
39
58
  var INTENT_GLYPHS = {
40
- "disclosure.expand": _iconify_icons_lucide_chevron_down_js.default,
41
- "disclosure.collapse": _iconify_icons_lucide_chevron_right_js.default,
42
- "selection.checked": _iconify_icons_lucide_check_js.default,
43
- "selection.indeterminate": _iconify_icons_lucide_minus_js.default,
44
- "action.close": _iconify_icons_lucide_x_js.default,
45
- "action.search": _iconify_icons_lucide_search_js.default,
46
- "action.increment": _iconify_icons_lucide_plus_js.default,
47
- "action.decrement": _iconify_icons_lucide_minus_js.default
59
+ "disclosure.expand": unwrapGlyph(_iconify_icons_lucide_chevron_down_js.default),
60
+ "disclosure.collapse": unwrapGlyph(_iconify_icons_lucide_chevron_right_js.default),
61
+ "selection.checked": unwrapGlyph(_iconify_icons_lucide_check_js.default),
62
+ "selection.indeterminate": unwrapGlyph(_iconify_icons_lucide_minus_js.default),
63
+ "action.close": unwrapGlyph(_iconify_icons_lucide_x_js.default),
64
+ "action.search": unwrapGlyph(_iconify_icons_lucide_search_js.default),
65
+ "action.increment": unwrapGlyph(_iconify_icons_lucide_plus_js.default),
66
+ "action.decrement": unwrapGlyph(_iconify_icons_lucide_minus_js.default),
67
+ "action.sortAscending": unwrapGlyph(_iconify_icons_lucide_arrow_up_narrow_wide_js.default),
68
+ "action.sortDescending": unwrapGlyph(_iconify_icons_lucide_arrow_down_wide_narrow_js.default),
69
+ "status.success": unwrapGlyph(_iconify_icons_lucide_check_circle_2_js.default)
48
70
  };
49
71
  /**
50
72
  * The Iconify registry name for an intent — `fsl-ui:{family}-{intent}`. The
@@ -52,7 +74,9 @@ var INTENT_GLYPHS = {
52
74
  * clobbered by) icons an application registers under bare names.
53
75
  */
54
76
  var iconifyName = intent => {
55
- return `fsl-ui:${intent.replace(".", "-")}`;
77
+ return `fsl-ui:${intent.replace(".", "-").replace(/[A-Z]/g, letter => {
78
+ return `-${letter.toLowerCase()}`;
79
+ })}`;
56
80
  };
57
81
  var registered = false;
58
82
  /**
@@ -63,7 +87,10 @@ var registered = false;
63
87
  */
64
88
  var ensureIconGlyphs = () => {
65
89
  if (registered) return;
66
- for (const [intent, glyph] of Object.entries(INTENT_GLYPHS)) (0, _iconify_icon_react.addIcon)(iconifyName(intent), glyph);
90
+ for (const [intent, glyph] of Object.entries(INTENT_GLYPHS)) {
91
+ const name = iconifyName(intent);
92
+ if (!(0, _iconify_icon_react.addIcon)(name, glyph)) throw new Error(`fsl-ui Icon: failed to register glyph "${name}" for intent "${intent}" — invalid Iconify name or data.`);
93
+ }
67
94
  registered = true;
68
95
  };
69
96
 
@@ -1,6 +1,9 @@
1
1
  /** Powered by @ttoss/config. https://ttoss.dev/docs/modules/packages/config/ */
2
2
  import { addIcon } from "@iconify-icon/react";
3
+ import arrowDownWideNarrowGlyph from "@iconify/icons-lucide/arrow-down-wide-narrow.js";
4
+ import arrowUpNarrowWideGlyph from "@iconify/icons-lucide/arrow-up-narrow-wide.js";
3
5
  import checkGlyph from "@iconify/icons-lucide/check.js";
6
+ import checkCircleGlyph from "@iconify/icons-lucide/check-circle-2.js";
4
7
  import chevronDownGlyph from "@iconify/icons-lucide/chevron-down.js";
5
8
  import chevronRightGlyph from "@iconify/icons-lucide/chevron-right.js";
6
9
  import minusGlyph from "@iconify/icons-lucide/minus.js";
@@ -25,18 +28,34 @@ import xGlyph from "@iconify/icons-lucide/x.js";
25
28
  * @see ../../../CONTRIBUTING.md ADR-005
26
29
  */
27
30
  /**
31
+ * The per-icon lucide modules are CommonJS (`exports.default = data` with
32
+ * `__esModule`). Bundlers disagree on default-import interop: Babel/Jest
33
+ * respect `__esModule` (import = the data), while Node-mode interop
34
+ * (Vite/Rolldown, Node ESM) makes the import the whole `module.exports` —
35
+ * `{ __esModule, default: data }`. Normalize both shapes; without this,
36
+ * `addIcon` receives an invalid payload and rejects it *silently*, every
37
+ * icon falls back to an Iconify API fetch, and no glyph renders in
38
+ * production builds (friction F-012).
39
+ */
40
+ var unwrapGlyph = mod => {
41
+ return "body" in mod ? mod : mod.default;
42
+ };
43
+ /**
28
44
  * The default (Lucide) glyph for every intent. `satisfies Record<…>` makes a
29
45
  * missing intent a compile error — the completeness rule from icon-system.md.
30
46
  */
31
47
  var INTENT_GLYPHS = {
32
- "disclosure.expand": chevronDownGlyph,
33
- "disclosure.collapse": chevronRightGlyph,
34
- "selection.checked": checkGlyph,
35
- "selection.indeterminate": minusGlyph,
36
- "action.close": xGlyph,
37
- "action.search": searchGlyph,
38
- "action.increment": plusGlyph,
39
- "action.decrement": minusGlyph
48
+ "disclosure.expand": unwrapGlyph(chevronDownGlyph),
49
+ "disclosure.collapse": unwrapGlyph(chevronRightGlyph),
50
+ "selection.checked": unwrapGlyph(checkGlyph),
51
+ "selection.indeterminate": unwrapGlyph(minusGlyph),
52
+ "action.close": unwrapGlyph(xGlyph),
53
+ "action.search": unwrapGlyph(searchGlyph),
54
+ "action.increment": unwrapGlyph(plusGlyph),
55
+ "action.decrement": unwrapGlyph(minusGlyph),
56
+ "action.sortAscending": unwrapGlyph(arrowUpNarrowWideGlyph),
57
+ "action.sortDescending": unwrapGlyph(arrowDownWideNarrowGlyph),
58
+ "status.success": unwrapGlyph(checkCircleGlyph)
40
59
  };
41
60
  /**
42
61
  * The Iconify registry name for an intent — `fsl-ui:{family}-{intent}`. The
@@ -44,7 +63,9 @@ var INTENT_GLYPHS = {
44
63
  * clobbered by) icons an application registers under bare names.
45
64
  */
46
65
  var iconifyName = intent => {
47
- return `fsl-ui:${intent.replace(".", "-")}`;
66
+ return `fsl-ui:${intent.replace(".", "-").replace(/[A-Z]/g, letter => {
67
+ return `-${letter.toLowerCase()}`;
68
+ })}`;
48
69
  };
49
70
  var registered = false;
50
71
  /**
@@ -55,7 +76,10 @@ var registered = false;
55
76
  */
56
77
  var ensureIconGlyphs = () => {
57
78
  if (registered) return;
58
- for (const [intent, glyph] of Object.entries(INTENT_GLYPHS)) addIcon(iconifyName(intent), glyph);
79
+ for (const [intent, glyph] of Object.entries(INTENT_GLYPHS)) {
80
+ const name = iconifyName(intent);
81
+ if (!addIcon(name, glyph)) throw new Error(`fsl-ui Icon: failed to register glyph "${name}" for intent "${intent}" — invalid Iconify name or data.`);
82
+ }
59
83
  registered = true;
60
84
  };
61
85
 
@@ -0,0 +1,32 @@
1
+ /** Powered by @ttoss/config. https://ttoss.dev/docs/modules/packages/config/ */
2
+ //#region src/components/Icon/intents.ts
3
+ /**
4
+ * Icon semantic layer — the intent vocabulary.
5
+ *
6
+ * This is the provider-agnostic contract described by
7
+ * `docs/design/design-system/components/icon-system.md`: an Icon is named by
8
+ * **what it means** (`icon.{family}.{intent}`), never by which glyph or which
9
+ * icon library renders it. The theme (here: the default Lucide mapping in
10
+ * `glyphs.ts`) decides the glyph; this file decides the meanings.
11
+ *
12
+ * Scope (ROADMAP B1): this is intentionally a **subset** of the canonical
13
+ * registry — only the intents that shipped components actually consume today.
14
+ * The registry "grows slowly and shrinks never" (icon-system.md § Change
15
+ * Rules): add an intent here (and its glyph in `glyphs.ts`) when a new
16
+ * component needs one, never speculatively.
17
+ *
18
+ * This module is the seed of the future standalone `@ttoss/fsl-icon` package;
19
+ * it stays free of React and token imports so it can be lifted out whole.
20
+ *
21
+ * @see ../../../CONTRIBUTING.md ADR-005 — Iconify provider + internal intent layer
22
+ * @see docs/design/design-system/components/icon-system.md — full contract
23
+ */
24
+ /**
25
+ * The intents currently backed by a glyph, grouped by family. Values are the
26
+ * canonical `{family}.{intent}` identifiers. Adding a member here is a
27
+ * type-level requirement to add its glyph in `INTENT_GLYPHS` (`glyphs.ts`).
28
+ */
29
+ var ICON_INTENTS = ["disclosure.expand", "disclosure.collapse", "selection.checked", "selection.indeterminate", "action.close", "action.search", "action.increment", "action.decrement", "action.sortAscending", "action.sortDescending", "status.success"];
30
+
31
+ //#endregion
32
+ exports.ICON_INTENTS = ICON_INTENTS;
@@ -0,0 +1,33 @@
1
+
2
+ //#region src/components/Icon/intents.d.ts
3
+ /**
4
+ * Icon semantic layer — the intent vocabulary.
5
+ *
6
+ * This is the provider-agnostic contract described by
7
+ * `docs/design/design-system/components/icon-system.md`: an Icon is named by
8
+ * **what it means** (`icon.{family}.{intent}`), never by which glyph or which
9
+ * icon library renders it. The theme (here: the default Lucide mapping in
10
+ * `glyphs.ts`) decides the glyph; this file decides the meanings.
11
+ *
12
+ * Scope (ROADMAP B1): this is intentionally a **subset** of the canonical
13
+ * registry — only the intents that shipped components actually consume today.
14
+ * The registry "grows slowly and shrinks never" (icon-system.md § Change
15
+ * Rules): add an intent here (and its glyph in `glyphs.ts`) when a new
16
+ * component needs one, never speculatively.
17
+ *
18
+ * This module is the seed of the future standalone `@ttoss/fsl-icon` package;
19
+ * it stays free of React and token imports so it can be lifted out whole.
20
+ *
21
+ * @see ../../../CONTRIBUTING.md ADR-005 — Iconify provider + internal intent layer
22
+ * @see docs/design/design-system/components/icon-system.md — full contract
23
+ */
24
+ /**
25
+ * The intents currently backed by a glyph, grouped by family. Values are the
26
+ * canonical `{family}.{intent}` identifiers. Adding a member here is a
27
+ * type-level requirement to add its glyph in `INTENT_GLYPHS` (`glyphs.ts`).
28
+ */
29
+ declare const ICON_INTENTS: readonly ["disclosure.expand", "disclosure.collapse", "selection.checked", "selection.indeterminate", "action.close", "action.search", "action.increment", "action.decrement", "action.sortAscending", "action.sortDescending", "status.success"];
30
+ /** A semantic icon intent — the public "name" of an icon. */
31
+ type IconIntent = (typeof ICON_INTENTS)[number];
32
+ //#endregion
33
+ export { ICON_INTENTS, IconIntent };
@@ -0,0 +1,33 @@
1
+
2
+ //#region src/components/Icon/intents.d.ts
3
+ /**
4
+ * Icon semantic layer — the intent vocabulary.
5
+ *
6
+ * This is the provider-agnostic contract described by
7
+ * `docs/design/design-system/components/icon-system.md`: an Icon is named by
8
+ * **what it means** (`icon.{family}.{intent}`), never by which glyph or which
9
+ * icon library renders it. The theme (here: the default Lucide mapping in
10
+ * `glyphs.ts`) decides the glyph; this file decides the meanings.
11
+ *
12
+ * Scope (ROADMAP B1): this is intentionally a **subset** of the canonical
13
+ * registry — only the intents that shipped components actually consume today.
14
+ * The registry "grows slowly and shrinks never" (icon-system.md § Change
15
+ * Rules): add an intent here (and its glyph in `glyphs.ts`) when a new
16
+ * component needs one, never speculatively.
17
+ *
18
+ * This module is the seed of the future standalone `@ttoss/fsl-icon` package;
19
+ * it stays free of React and token imports so it can be lifted out whole.
20
+ *
21
+ * @see ../../../CONTRIBUTING.md ADR-005 — Iconify provider + internal intent layer
22
+ * @see docs/design/design-system/components/icon-system.md — full contract
23
+ */
24
+ /**
25
+ * The intents currently backed by a glyph, grouped by family. Values are the
26
+ * canonical `{family}.{intent}` identifiers. Adding a member here is a
27
+ * type-level requirement to add its glyph in `INTENT_GLYPHS` (`glyphs.ts`).
28
+ */
29
+ declare const ICON_INTENTS: readonly ["disclosure.expand", "disclosure.collapse", "selection.checked", "selection.indeterminate", "action.close", "action.search", "action.increment", "action.decrement", "action.sortAscending", "action.sortDescending", "status.success"];
30
+ /** A semantic icon intent — the public "name" of an icon. */
31
+ type IconIntent = (typeof ICON_INTENTS)[number];
32
+ //#endregion
33
+ export { ICON_INTENTS, IconIntent };
@@ -0,0 +1,32 @@
1
+ /** Powered by @ttoss/config. https://ttoss.dev/docs/modules/packages/config/ */
2
+ //#region src/components/Icon/intents.ts
3
+ /**
4
+ * Icon semantic layer — the intent vocabulary.
5
+ *
6
+ * This is the provider-agnostic contract described by
7
+ * `docs/design/design-system/components/icon-system.md`: an Icon is named by
8
+ * **what it means** (`icon.{family}.{intent}`), never by which glyph or which
9
+ * icon library renders it. The theme (here: the default Lucide mapping in
10
+ * `glyphs.ts`) decides the glyph; this file decides the meanings.
11
+ *
12
+ * Scope (ROADMAP B1): this is intentionally a **subset** of the canonical
13
+ * registry — only the intents that shipped components actually consume today.
14
+ * The registry "grows slowly and shrinks never" (icon-system.md § Change
15
+ * Rules): add an intent here (and its glyph in `glyphs.ts`) when a new
16
+ * component needs one, never speculatively.
17
+ *
18
+ * This module is the seed of the future standalone `@ttoss/fsl-icon` package;
19
+ * it stays free of React and token imports so it can be lifted out whole.
20
+ *
21
+ * @see ../../../CONTRIBUTING.md ADR-005 — Iconify provider + internal intent layer
22
+ * @see docs/design/design-system/components/icon-system.md — full contract
23
+ */
24
+ /**
25
+ * The intents currently backed by a glyph, grouped by family. Values are the
26
+ * canonical `{family}.{intent}` identifiers. Adding a member here is a
27
+ * type-level requirement to add its glyph in `INTENT_GLYPHS` (`glyphs.ts`).
28
+ */
29
+ var ICON_INTENTS = ["disclosure.expand", "disclosure.collapse", "selection.checked", "selection.indeterminate", "action.close", "action.search", "action.increment", "action.decrement", "action.sortAscending", "action.sortDescending", "status.success"];
30
+
31
+ //#endregion
32
+ export { ICON_INTENTS };