@malilion/block-ui-react 0.3.0 → 0.5.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 (182) hide show
  1. package/dist/components/actions/BlockMenu/BlockMenu.d.ts.map +1 -1
  2. package/dist/components/actions/BlockMenu/BlockMenu.js +17 -117
  3. package/dist/components/actions/BlockMenu/BlockMenu.js.map +1 -1
  4. package/dist/components/actions/BlockMenu/BlockMenu.utils.d.ts +2 -0
  5. package/dist/components/actions/BlockMenu/BlockMenu.utils.d.ts.map +1 -1
  6. package/dist/components/actions/BlockMenu/BlockMenu.utils.js +5 -1
  7. package/dist/components/actions/BlockMenu/BlockMenu.utils.js.map +1 -1
  8. package/dist/components/actions/BlockMenu/MenuList.d.ts +28 -0
  9. package/dist/components/actions/BlockMenu/MenuList.d.ts.map +1 -0
  10. package/dist/components/actions/BlockMenu/MenuList.js +134 -0
  11. package/dist/components/actions/BlockMenu/MenuList.js.map +1 -0
  12. package/dist/components/actions/ContextMenu/ContextMenu.d.ts +8 -0
  13. package/dist/components/actions/ContextMenu/ContextMenu.d.ts.map +1 -0
  14. package/dist/components/actions/ContextMenu/ContextMenu.js +100 -0
  15. package/dist/components/actions/ContextMenu/ContextMenu.js.map +1 -0
  16. package/dist/components/actions/ContextMenu/ContextMenu.module.js +7 -0
  17. package/dist/components/actions/ContextMenu/ContextMenu.module.js.map +1 -0
  18. package/dist/components/actions/ContextMenu/ContextMenu.types.d.ts +15 -0
  19. package/dist/components/actions/ContextMenu/ContextMenu.types.d.ts.map +1 -0
  20. package/dist/components/actions/ContextMenu/index.d.ts +3 -0
  21. package/dist/components/actions/ContextMenu/index.d.ts.map +1 -0
  22. package/dist/components/actions/index.d.ts +1 -0
  23. package/dist/components/actions/index.d.ts.map +1 -1
  24. package/dist/components/crafting/RecipeBook/RecipeBook.js +2 -2
  25. package/dist/components/crafting/RecipeBook/RecipeBook.js.map +1 -1
  26. package/dist/components/display/Avatar/Avatar.d.ts +7 -0
  27. package/dist/components/display/Avatar/Avatar.d.ts.map +1 -0
  28. package/dist/components/display/Avatar/Avatar.js +53 -0
  29. package/dist/components/display/Avatar/Avatar.js.map +1 -0
  30. package/dist/components/display/Avatar/Avatar.module.js +17 -0
  31. package/dist/components/display/Avatar/Avatar.module.js.map +1 -0
  32. package/dist/components/display/Avatar/Avatar.types.d.ts +18 -0
  33. package/dist/components/display/Avatar/Avatar.types.d.ts.map +1 -0
  34. package/dist/components/display/Avatar/Avatar.types.js +11 -0
  35. package/dist/components/display/Avatar/Avatar.types.js.map +1 -0
  36. package/dist/components/display/Avatar/Avatar.utils.d.ts +7 -0
  37. package/dist/components/display/Avatar/Avatar.utils.d.ts.map +1 -0
  38. package/dist/components/display/Avatar/Avatar.utils.js +28 -0
  39. package/dist/components/display/Avatar/Avatar.utils.js.map +1 -0
  40. package/dist/components/display/Avatar/index.d.ts +5 -0
  41. package/dist/components/display/Avatar/index.d.ts.map +1 -0
  42. package/dist/components/display/Kbd/Kbd.d.ts +4 -0
  43. package/dist/components/display/Kbd/Kbd.d.ts.map +1 -0
  44. package/dist/components/display/Kbd/Kbd.js +33 -0
  45. package/dist/components/display/Kbd/Kbd.js.map +1 -0
  46. package/dist/components/display/Kbd/Kbd.module.js +13 -0
  47. package/dist/components/display/Kbd/Kbd.module.js.map +1 -0
  48. package/dist/components/display/Kbd/Kbd.types.d.ts +9 -0
  49. package/dist/components/display/Kbd/Kbd.types.d.ts.map +1 -0
  50. package/dist/components/display/Kbd/index.d.ts +3 -0
  51. package/dist/components/display/Kbd/index.d.ts.map +1 -0
  52. package/dist/components/display/Popover/Popover.d.ts +8 -0
  53. package/dist/components/display/Popover/Popover.d.ts.map +1 -0
  54. package/dist/components/display/Popover/Popover.js +101 -0
  55. package/dist/components/display/Popover/Popover.js.map +1 -0
  56. package/dist/components/display/Popover/Popover.module.js +15 -0
  57. package/dist/components/display/Popover/Popover.module.js.map +1 -0
  58. package/dist/components/display/Popover/Popover.types.d.ts +26 -0
  59. package/dist/components/display/Popover/Popover.types.d.ts.map +1 -0
  60. package/dist/components/display/Popover/index.d.ts +3 -0
  61. package/dist/components/display/Popover/index.d.ts.map +1 -0
  62. package/dist/components/display/SkillTree/SkillTree.js +2 -2
  63. package/dist/components/display/SkillTree/SkillTree.js.map +1 -1
  64. package/dist/components/display/index.d.ts +3 -0
  65. package/dist/components/display/index.d.ts.map +1 -1
  66. package/dist/components/feedback/ConfirmDialog/ConfirmDialog.js +2 -2
  67. package/dist/components/feedback/ConfirmDialog/ConfirmDialog.js.map +1 -1
  68. package/dist/components/feedback/Drawer/Drawer.d.ts +7 -0
  69. package/dist/components/feedback/Drawer/Drawer.d.ts.map +1 -0
  70. package/dist/components/feedback/Drawer/Drawer.js +78 -0
  71. package/dist/components/feedback/Drawer/Drawer.js.map +1 -0
  72. package/dist/components/feedback/Drawer/Drawer.module.js +25 -0
  73. package/dist/components/feedback/Drawer/Drawer.module.js.map +1 -0
  74. package/dist/components/feedback/Drawer/Drawer.types.d.ts +21 -0
  75. package/dist/components/feedback/Drawer/Drawer.types.d.ts.map +1 -0
  76. package/dist/components/feedback/Drawer/index.d.ts +3 -0
  77. package/dist/components/feedback/Drawer/index.d.ts.map +1 -0
  78. package/dist/components/feedback/EmptyState/EmptyState.d.ts +4 -0
  79. package/dist/components/feedback/EmptyState/EmptyState.d.ts.map +1 -0
  80. package/dist/components/feedback/EmptyState/EmptyState.js +35 -0
  81. package/dist/components/feedback/EmptyState/EmptyState.js.map +1 -0
  82. package/dist/components/feedback/EmptyState/EmptyState.module.js +17 -0
  83. package/dist/components/feedback/EmptyState/EmptyState.module.js.map +1 -0
  84. package/dist/components/feedback/EmptyState/EmptyState.types.d.ts +14 -0
  85. package/dist/components/feedback/EmptyState/EmptyState.types.d.ts.map +1 -0
  86. package/dist/components/feedback/EmptyState/index.d.ts +3 -0
  87. package/dist/components/feedback/EmptyState/index.d.ts.map +1 -0
  88. package/dist/components/feedback/Modal/BlockModal.d.ts.map +1 -1
  89. package/dist/components/feedback/Modal/BlockModal.js +3 -12
  90. package/dist/components/feedback/Modal/BlockModal.js.map +1 -1
  91. package/dist/components/feedback/Skeleton/Skeleton.d.ts +7 -0
  92. package/dist/components/feedback/Skeleton/Skeleton.d.ts.map +1 -0
  93. package/dist/components/feedback/Skeleton/Skeleton.js +33 -0
  94. package/dist/components/feedback/Skeleton/Skeleton.js.map +1 -0
  95. package/dist/components/feedback/Skeleton/Skeleton.module.js +12 -0
  96. package/dist/components/feedback/Skeleton/Skeleton.module.js.map +1 -0
  97. package/dist/components/feedback/Skeleton/Skeleton.types.d.ts +14 -0
  98. package/dist/components/feedback/Skeleton/Skeleton.types.d.ts.map +1 -0
  99. package/dist/components/feedback/Skeleton/Skeleton.types.js +11 -0
  100. package/dist/components/feedback/Skeleton/Skeleton.types.js.map +1 -0
  101. package/dist/components/feedback/Skeleton/index.d.ts +4 -0
  102. package/dist/components/feedback/Skeleton/index.d.ts.map +1 -0
  103. package/dist/components/feedback/index.d.ts +3 -0
  104. package/dist/components/feedback/index.d.ts.map +1 -1
  105. package/dist/components/forms/NumberInput/NumberInput.d.ts +8 -0
  106. package/dist/components/forms/NumberInput/NumberInput.d.ts.map +1 -0
  107. package/dist/components/forms/NumberInput/NumberInput.js +122 -0
  108. package/dist/components/forms/NumberInput/NumberInput.js.map +1 -0
  109. package/dist/components/forms/NumberInput/NumberInput.module.js +13 -0
  110. package/dist/components/forms/NumberInput/NumberInput.module.js.map +1 -0
  111. package/dist/components/forms/NumberInput/NumberInput.types.d.ts +20 -0
  112. package/dist/components/forms/NumberInput/NumberInput.types.d.ts.map +1 -0
  113. package/dist/components/forms/NumberInput/NumberInput.utils.d.ts +11 -0
  114. package/dist/components/forms/NumberInput/NumberInput.utils.d.ts.map +1 -0
  115. package/dist/components/forms/NumberInput/NumberInput.utils.js +26 -0
  116. package/dist/components/forms/NumberInput/NumberInput.utils.js.map +1 -0
  117. package/dist/components/forms/NumberInput/index.d.ts +3 -0
  118. package/dist/components/forms/NumberInput/index.d.ts.map +1 -0
  119. package/dist/components/forms/index.d.ts +1 -0
  120. package/dist/components/forms/index.d.ts.map +1 -1
  121. package/dist/components/inventory/InventorySlot/InventorySlot.js +2 -2
  122. package/dist/components/inventory/InventorySlot/InventorySlot.js.map +1 -1
  123. package/dist/components/layout/Accordion/Accordion.d.ts +7 -0
  124. package/dist/components/layout/Accordion/Accordion.d.ts.map +1 -0
  125. package/dist/components/layout/Accordion/Accordion.js +95 -0
  126. package/dist/components/layout/Accordion/Accordion.js.map +1 -0
  127. package/dist/components/layout/Accordion/Accordion.module.js +23 -0
  128. package/dist/components/layout/Accordion/Accordion.module.js.map +1 -0
  129. package/dist/components/layout/Accordion/Accordion.types.d.ts +21 -0
  130. package/dist/components/layout/Accordion/Accordion.types.d.ts.map +1 -0
  131. package/dist/components/layout/Accordion/index.d.ts +3 -0
  132. package/dist/components/layout/Accordion/index.d.ts.map +1 -0
  133. package/dist/components/layout/BlockContainer/BlockContainer.d.ts +4 -0
  134. package/dist/components/layout/BlockContainer/BlockContainer.d.ts.map +1 -0
  135. package/dist/components/layout/BlockContainer/BlockContainer.js +18 -0
  136. package/dist/components/layout/BlockContainer/BlockContainer.js.map +1 -0
  137. package/dist/components/layout/BlockContainer/BlockContainer.module.js +7 -0
  138. package/dist/components/layout/BlockContainer/BlockContainer.module.js.map +1 -0
  139. package/dist/components/layout/BlockContainer/BlockContainer.types.d.ts +11 -0
  140. package/dist/components/layout/BlockContainer/BlockContainer.types.d.ts.map +1 -0
  141. package/dist/components/layout/BlockContainer/index.d.ts +3 -0
  142. package/dist/components/layout/BlockContainer/index.d.ts.map +1 -0
  143. package/dist/components/layout/BlockGrid/BlockGrid.d.ts +4 -0
  144. package/dist/components/layout/BlockGrid/BlockGrid.d.ts.map +1 -0
  145. package/dist/components/layout/BlockGrid/BlockGrid.js +25 -0
  146. package/dist/components/layout/BlockGrid/BlockGrid.js.map +1 -0
  147. package/dist/components/layout/BlockGrid/BlockGrid.module.js +7 -0
  148. package/dist/components/layout/BlockGrid/BlockGrid.module.js.map +1 -0
  149. package/dist/components/layout/BlockGrid/BlockGrid.types.d.ts +16 -0
  150. package/dist/components/layout/BlockGrid/BlockGrid.types.d.ts.map +1 -0
  151. package/dist/components/layout/BlockGrid/index.d.ts +3 -0
  152. package/dist/components/layout/BlockGrid/index.d.ts.map +1 -0
  153. package/dist/components/layout/index.d.ts +3 -0
  154. package/dist/components/layout/index.d.ts.map +1 -1
  155. package/dist/components/navigation/BlockSidebar/BlockSidebar.js +3 -3
  156. package/dist/components/navigation/BlockSidebar/BlockSidebar.js.map +1 -1
  157. package/dist/components/navigation/BlockStepper/BlockStepper.d.ts +8 -0
  158. package/dist/components/navigation/BlockStepper/BlockStepper.d.ts.map +1 -0
  159. package/dist/components/navigation/BlockStepper/BlockStepper.js +66 -0
  160. package/dist/components/navigation/BlockStepper/BlockStepper.js.map +1 -0
  161. package/dist/components/navigation/BlockStepper/BlockStepper.module.js +23 -0
  162. package/dist/components/navigation/BlockStepper/BlockStepper.module.js.map +1 -0
  163. package/dist/components/navigation/BlockStepper/BlockStepper.types.d.ts +19 -0
  164. package/dist/components/navigation/BlockStepper/BlockStepper.types.d.ts.map +1 -0
  165. package/dist/components/navigation/BlockStepper/index.d.ts +3 -0
  166. package/dist/components/navigation/BlockStepper/index.d.ts.map +1 -0
  167. package/dist/components/navigation/Breadcrumb/Breadcrumb.js +2 -2
  168. package/dist/components/navigation/Breadcrumb/Breadcrumb.js.map +1 -1
  169. package/dist/components/navigation/HotbarNavigation/HotbarNavigation.js +3 -3
  170. package/dist/components/navigation/HotbarNavigation/HotbarNavigation.js.map +1 -1
  171. package/dist/components/navigation/index.d.ts +1 -0
  172. package/dist/components/navigation/index.d.ts.map +1 -1
  173. package/dist/hooks/useAnchoredPosition.d.ts +3 -1
  174. package/dist/hooks/useAnchoredPosition.d.ts.map +1 -1
  175. package/dist/hooks/useAnchoredPosition.js.map +1 -1
  176. package/dist/hooks/useScrollLock.d.ts +6 -0
  177. package/dist/hooks/useScrollLock.d.ts.map +1 -0
  178. package/dist/hooks/useScrollLock.js +26 -0
  179. package/dist/hooks/useScrollLock.js.map +1 -0
  180. package/dist/index.js +16 -1
  181. package/dist/styles.css +776 -36
  182. package/package.json +4 -4
@@ -0,0 +1,122 @@
1
+ import { cx } from "../../../utils/cx.js";
2
+ import { Field } from "../Field/Field.js";
3
+ import control_module_default from "../Field/control.module.js";
4
+ import { useControllableState } from "../../../hooks/useControllableState.js";
5
+ import NumberInput_module_default from "./NumberInput.module.js";
6
+ import { normalizeNumber, parseNumber, stepPrecision } from "./NumberInput.utils.js";
7
+ import { forwardRef, useState } from "react";
8
+ import { MinusIcon, PlusIcon } from "@malilion/block-ui-icons";
9
+ import { jsx, jsxs } from "react/jsx-runtime";
10
+ //#region src/components/forms/NumberInput/NumberInput.tsx
11
+ /**
12
+ * Number field with − / + buttons, following the WAI-ARIA spinbutton pattern:
13
+ * ↑ / ↓ step, Page Up / Down step ×10, Home / End jump to min / max. Values are
14
+ * clamped and rounded when committed (step, Enter or blur).
15
+ */
16
+ var NumberInput = forwardRef(function NumberInput({ id, label, error, success, helperText, value, defaultValue = null, onValueChange, min, max, step = 1, disabled, required, className, wrapperClassName, onBlur, onKeyDown, "aria-describedby": ariaDescribedBy, ...rest }, ref) {
17
+ const [current, setCurrent] = useControllableState({
18
+ value,
19
+ defaultValue,
20
+ onChange: onValueChange
21
+ });
22
+ const [draft, setDraft] = useState(null);
23
+ const precision = stepPrecision(step);
24
+ const format = (n) => n === null ? "" : n.toFixed(precision);
25
+ const commit = (next) => {
26
+ const normalized = next === null ? null : normalizeNumber(next, {
27
+ min,
28
+ max,
29
+ step
30
+ });
31
+ setDraft(null);
32
+ if (normalized !== current) setCurrent(normalized);
33
+ };
34
+ const base = () => {
35
+ return (draft !== null ? parseNumber(draft) : current) ?? min ?? 0;
36
+ };
37
+ const stepBy = (amount) => {
38
+ if (disabled) return;
39
+ commit(base() + amount);
40
+ };
41
+ const atMin = current !== null && min !== void 0 && current <= min;
42
+ const atMax = current !== null && max !== void 0 && current >= max;
43
+ const handleKeyDown = (event) => {
44
+ onKeyDown?.(event);
45
+ if (event.defaultPrevented) return;
46
+ let handled = true;
47
+ if (event.key === "ArrowUp") stepBy(step);
48
+ else if (event.key === "ArrowDown") stepBy(-step);
49
+ else if (event.key === "PageUp") stepBy(step * 10);
50
+ else if (event.key === "PageDown") stepBy(-step * 10);
51
+ else if (event.key === "Home" && min !== void 0) commit(min);
52
+ else if (event.key === "End" && max !== void 0) commit(max);
53
+ else if (event.key === "Enter" && draft !== null) commit(parseNumber(draft));
54
+ else handled = false;
55
+ if (handled) event.preventDefault();
56
+ };
57
+ return /* @__PURE__ */ jsx(Field, {
58
+ id,
59
+ label,
60
+ error,
61
+ success,
62
+ helperText,
63
+ disabled,
64
+ required,
65
+ className: wrapperClassName,
66
+ children: ({ inputId, describedBy, invalid }) => /* @__PURE__ */ jsxs("div", {
67
+ className: NumberInput_module_default.wrapper,
68
+ children: [
69
+ /* @__PURE__ */ jsx("button", {
70
+ type: "button",
71
+ tabIndex: -1,
72
+ "aria-label": "Decrease",
73
+ "aria-controls": inputId,
74
+ disabled: disabled || atMin,
75
+ className: NumberInput_module_default.button,
76
+ onMouseDown: (event) => event.preventDefault(),
77
+ onClick: () => stepBy(-step),
78
+ children: /* @__PURE__ */ jsx(MinusIcon, { size: 16 })
79
+ }),
80
+ /* @__PURE__ */ jsx("input", {
81
+ ref,
82
+ id: inputId,
83
+ type: "text",
84
+ role: "spinbutton",
85
+ inputMode: precision > 0 || min !== void 0 && min < 0 ? "decimal" : "numeric",
86
+ autoComplete: "off",
87
+ value: draft ?? format(current),
88
+ "aria-valuenow": current ?? void 0,
89
+ "aria-valuemin": min,
90
+ "aria-valuemax": max,
91
+ disabled,
92
+ required,
93
+ "aria-invalid": invalid || void 0,
94
+ "aria-describedby": cx(describedBy, ariaDescribedBy) || void 0,
95
+ className: cx(control_module_default.control, NumberInput_module_default.input, className),
96
+ onChange: (event) => setDraft(event.target.value),
97
+ onBlur: (event) => {
98
+ if (draft !== null) commit(parseNumber(draft));
99
+ onBlur?.(event);
100
+ },
101
+ onKeyDown: handleKeyDown,
102
+ ...rest
103
+ }),
104
+ /* @__PURE__ */ jsx("button", {
105
+ type: "button",
106
+ tabIndex: -1,
107
+ "aria-label": "Increase",
108
+ "aria-controls": inputId,
109
+ disabled: disabled || atMax,
110
+ className: NumberInput_module_default.button,
111
+ onMouseDown: (event) => event.preventDefault(),
112
+ onClick: () => stepBy(step),
113
+ children: /* @__PURE__ */ jsx(PlusIcon, { size: 16 })
114
+ })
115
+ ]
116
+ })
117
+ });
118
+ });
119
+ //#endregion
120
+ export { NumberInput };
121
+
122
+ //# sourceMappingURL=NumberInput.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NumberInput.js","names":["styles","control"],"sources":["../../../../src/components/forms/NumberInput/NumberInput.tsx"],"sourcesContent":["import { MinusIcon, PlusIcon } from \"@malilion/block-ui-icons\";\nimport { forwardRef, useState, type KeyboardEvent } from \"react\";\nimport { useControllableState } from \"../../../hooks/useControllableState\";\nimport { cx } from \"../../../utils/cx\";\nimport { Field } from \"../Field/Field\";\nimport control from \"../Field/control.module.css\";\nimport styles from \"./NumberInput.module.css\";\nimport type { NumberInputProps } from \"./NumberInput.types\";\nimport { normalizeNumber, parseNumber, stepPrecision } from \"./NumberInput.utils\";\n\n/**\n * Number field with − / + buttons, following the WAI-ARIA spinbutton pattern:\n * ↑ / ↓ step, Page Up / Down step ×10, Home / End jump to min / max. Values are\n * clamped and rounded when committed (step, Enter or blur).\n */\nexport const NumberInput = forwardRef<HTMLInputElement, NumberInputProps>(function NumberInput(\n {\n id,\n label,\n error,\n success,\n helperText,\n value,\n defaultValue = null,\n onValueChange,\n min,\n max,\n step = 1,\n disabled,\n required,\n className,\n wrapperClassName,\n onBlur,\n onKeyDown,\n \"aria-describedby\": ariaDescribedBy,\n ...rest\n },\n ref,\n) {\n const [current, setCurrent] = useControllableState<number | null>({\n value,\n defaultValue,\n onChange: onValueChange,\n });\n const [draft, setDraft] = useState<string | null>(null);\n const precision = stepPrecision(step);\n const format = (n: number | null) => (n === null ? \"\" : n.toFixed(precision));\n\n const commit = (next: number | null) => {\n const normalized = next === null ? null : normalizeNumber(next, { min, max, step });\n setDraft(null);\n if (normalized !== current) setCurrent(normalized);\n };\n\n const base = () => {\n const parsed = draft !== null ? parseNumber(draft) : current;\n return parsed ?? min ?? 0;\n };\n const stepBy = (amount: number) => {\n if (disabled) return;\n commit(base() + amount);\n };\n\n const atMin = current !== null && min !== undefined && current <= min;\n const atMax = current !== null && max !== undefined && current >= max;\n\n const handleKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {\n onKeyDown?.(event);\n if (event.defaultPrevented) return;\n let handled = true;\n if (event.key === \"ArrowUp\") stepBy(step);\n else if (event.key === \"ArrowDown\") stepBy(-step);\n else if (event.key === \"PageUp\") stepBy(step * 10);\n else if (event.key === \"PageDown\") stepBy(-step * 10);\n else if (event.key === \"Home\" && min !== undefined) commit(min);\n else if (event.key === \"End\" && max !== undefined) commit(max);\n else if (event.key === \"Enter\" && draft !== null) commit(parseNumber(draft));\n else handled = false;\n if (handled) event.preventDefault();\n };\n\n return (\n <Field\n id={id}\n label={label}\n error={error}\n success={success}\n helperText={helperText}\n disabled={disabled}\n required={required}\n className={wrapperClassName}\n >\n {({ inputId, describedBy, invalid }) => (\n <div className={styles.wrapper}>\n <button\n type=\"button\"\n tabIndex={-1}\n aria-label=\"Decrease\"\n aria-controls={inputId}\n disabled={disabled || atMin}\n className={styles.button}\n onMouseDown={(event) => event.preventDefault()}\n onClick={() => stepBy(-step)}\n >\n <MinusIcon size={16} />\n </button>\n <input\n ref={ref}\n id={inputId}\n type=\"text\"\n role=\"spinbutton\"\n inputMode={precision > 0 || (min !== undefined && min < 0) ? \"decimal\" : \"numeric\"}\n autoComplete=\"off\"\n value={draft ?? format(current)}\n aria-valuenow={current ?? undefined}\n aria-valuemin={min}\n aria-valuemax={max}\n disabled={disabled}\n required={required}\n aria-invalid={invalid || undefined}\n aria-describedby={cx(describedBy, ariaDescribedBy) || undefined}\n className={cx(control.control, styles.input, className)}\n onChange={(event) => setDraft(event.target.value)}\n onBlur={(event) => {\n if (draft !== null) commit(parseNumber(draft));\n onBlur?.(event);\n }}\n onKeyDown={handleKeyDown}\n {...rest}\n />\n <button\n type=\"button\"\n tabIndex={-1}\n aria-label=\"Increase\"\n aria-controls={inputId}\n disabled={disabled || atMax}\n className={styles.button}\n onMouseDown={(event) => event.preventDefault()}\n onClick={() => stepBy(step)}\n >\n <PlusIcon size={16} />\n </button>\n </div>\n )}\n </Field>\n );\n});\n"],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ //#region src/components/forms/NumberInput/NumberInput.module.css
2
+ var wrapper = "block-wrapper-b-npH";
3
+ var input = "block-input-cg-Fv";
4
+ var button = "block-button-8xOEk";
5
+ var NumberInput_module_default = {
6
+ wrapper,
7
+ input,
8
+ button
9
+ };
10
+ //#endregion
11
+ export { button, NumberInput_module_default as default, input, wrapper };
12
+
13
+ //# sourceMappingURL=NumberInput.module.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NumberInput.module.js","names":[],"sources":["../../../../src/components/forms/NumberInput/NumberInput.module.css"],"sourcesContent":[".wrapper {\n display: flex;\n align-items: stretch;\n gap: var(--block-space-1);\n}\n\n.input {\n flex: 1;\n min-width: 0;\n height: var(--block-size-control-md);\n text-align: center;\n font-variant-numeric: tabular-nums;\n}\n\n.button {\n display: inline-flex;\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n width: var(--block-size-control-md);\n padding: 0;\n color: var(--block-text);\n background-color: var(--block-surface-alt);\n border: var(--block-border-width-thin) solid var(--block-border);\n box-shadow: var(--block-shadow-bevel);\n cursor: pointer;\n}\n\n.button:hover:not(:disabled) {\n background-color: var(--block-slot-hover);\n}\n\n.button:active:not(:disabled) {\n box-shadow: var(--block-shadow-inset);\n}\n\n.button:disabled {\n cursor: not-allowed;\n opacity: 0.45;\n}\n"],"mappings":""}
@@ -0,0 +1,20 @@
1
+ import type { InputHTMLAttributes } from "react";
2
+ export interface NumberInputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, "value" | "defaultValue" | "onChange" | "type" | "min" | "max" | "step" | "size"> {
3
+ label?: string;
4
+ error?: string;
5
+ success?: boolean;
6
+ helperText?: string;
7
+ /** Controlled value; `null` is empty. */
8
+ value?: number | null;
9
+ /** Initial value when uncontrolled. @default null */
10
+ defaultValue?: number | null;
11
+ /** Called with the committed value (on step, Enter or blur). */
12
+ onValueChange?: (value: number | null) => void;
13
+ min?: number;
14
+ max?: number;
15
+ /** Amount per step; its decimals set the precision. @default 1 */
16
+ step?: number;
17
+ /** Class name for the outer field wrapper. */
18
+ wrapperClassName?: string;
19
+ }
20
+ //# sourceMappingURL=NumberInput.types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NumberInput.types.d.ts","sourceRoot":"","sources":["../../../../src/components/forms/NumberInput/NumberInput.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,OAAO,CAAC;AAEjD,MAAM,WAAW,gBAAiB,SAAQ,IAAI,CAC5C,mBAAmB,CAAC,gBAAgB,CAAC,EACrC,OAAO,GAAG,cAAc,GAAG,UAAU,GAAG,MAAM,GAAG,KAAK,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,CACjF;IACC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,yCAAyC;IACzC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,qDAAqD;IACrD,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,gEAAgE;IAChE,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IAC/C,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,kEAAkE;IAClE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,8CAA8C;IAC9C,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B"}
@@ -0,0 +1,11 @@
1
+ /** Decimal places of a step, e.g. 0.25 → 2. */
2
+ export declare function stepPrecision(step: number): number;
3
+ /** Clamps to [min, max] and rounds to the step's precision. */
4
+ export declare function normalizeNumber(value: number, { min, max, step }: {
5
+ min?: number;
6
+ max?: number;
7
+ step?: number;
8
+ }): number;
9
+ /** Parses user text ("12", "-3.5", " 4 "); returns null for empty or invalid input. */
10
+ export declare function parseNumber(text: string): number | null;
11
+ //# sourceMappingURL=NumberInput.utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NumberInput.utils.d.ts","sourceRoot":"","sources":["../../../../src/components/forms/NumberInput/NumberInput.utils.ts"],"names":[],"mappings":"AAAA,+CAA+C;AAC/C,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAIlD;AAED,+DAA+D;AAC/D,wBAAgB,eAAe,CAC7B,KAAK,EAAE,MAAM,EACb,EAAE,GAAG,EAAE,GAAG,EAAE,IAAQ,EAAE,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GACpE,MAAM,CAMR;AAED,uFAAuF;AACvF,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAKvD"}
@@ -0,0 +1,26 @@
1
+ //#region src/components/forms/NumberInput/NumberInput.utils.ts
2
+ /** Decimal places of a step, e.g. 0.25 → 2. */
3
+ function stepPrecision(step) {
4
+ const text = String(step);
5
+ const dot = text.indexOf(".");
6
+ return dot < 0 ? 0 : text.length - dot - 1;
7
+ }
8
+ /** Clamps to [min, max] and rounds to the step's precision. */
9
+ function normalizeNumber(value, { min, max, step = 1 }) {
10
+ let next = value;
11
+ if (min !== void 0) next = Math.max(min, next);
12
+ if (max !== void 0) next = Math.min(max, next);
13
+ const precision = stepPrecision(step);
14
+ return Number(next.toFixed(precision));
15
+ }
16
+ /** Parses user text ("12", "-3.5", " 4 "); returns null for empty or invalid input. */
17
+ function parseNumber(text) {
18
+ const trimmed = text.trim().replace(",", ".");
19
+ if (trimmed === "" || trimmed === "-" || trimmed === ".") return null;
20
+ const value = Number(trimmed);
21
+ return Number.isFinite(value) ? value : null;
22
+ }
23
+ //#endregion
24
+ export { normalizeNumber, parseNumber, stepPrecision };
25
+
26
+ //# sourceMappingURL=NumberInput.utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NumberInput.utils.js","names":[],"sources":["../../../../src/components/forms/NumberInput/NumberInput.utils.ts"],"sourcesContent":["/** Decimal places of a step, e.g. 0.25 → 2. */\nexport function stepPrecision(step: number): number {\n const text = String(step);\n const dot = text.indexOf(\".\");\n return dot < 0 ? 0 : text.length - dot - 1;\n}\n\n/** Clamps to [min, max] and rounds to the step's precision. */\nexport function normalizeNumber(\n value: number,\n { min, max, step = 1 }: { min?: number; max?: number; step?: number },\n): number {\n let next = value;\n if (min !== undefined) next = Math.max(min, next);\n if (max !== undefined) next = Math.min(max, next);\n const precision = stepPrecision(step);\n return Number(next.toFixed(precision));\n}\n\n/** Parses user text (\"12\", \"-3.5\", \" 4 \"); returns null for empty or invalid input. */\nexport function parseNumber(text: string): number | null {\n const trimmed = text.trim().replace(\",\", \".\");\n if (trimmed === \"\" || trimmed === \"-\" || trimmed === \".\") return null;\n const value = Number(trimmed);\n return Number.isFinite(value) ? value : null;\n}\n"],"mappings":";;AACA,SAAgB,cAAc,MAAsB;CAClD,MAAM,OAAO,OAAO,IAAI;CACxB,MAAM,MAAM,KAAK,QAAQ,GAAG;CAC5B,OAAO,MAAM,IAAI,IAAI,KAAK,SAAS,MAAM;AAC3C;;AAGA,SAAgB,gBACd,OACA,EAAE,KAAK,KAAK,OAAO,KACX;CACR,IAAI,OAAO;CACX,IAAI,QAAQ,KAAA,GAAW,OAAO,KAAK,IAAI,KAAK,IAAI;CAChD,IAAI,QAAQ,KAAA,GAAW,OAAO,KAAK,IAAI,KAAK,IAAI;CAChD,MAAM,YAAY,cAAc,IAAI;CACpC,OAAO,OAAO,KAAK,QAAQ,SAAS,CAAC;AACvC;;AAGA,SAAgB,YAAY,MAA6B;CACvD,MAAM,UAAU,KAAK,KAAK,CAAC,CAAC,QAAQ,KAAK,GAAG;CAC5C,IAAI,YAAY,MAAM,YAAY,OAAO,YAAY,KAAK,OAAO;CACjE,MAAM,QAAQ,OAAO,OAAO;CAC5B,OAAO,OAAO,SAAS,KAAK,IAAI,QAAQ;AAC1C"}
@@ -0,0 +1,3 @@
1
+ export { NumberInput } from "./NumberInput";
2
+ export type { NumberInputProps } from "./NumberInput.types";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/forms/NumberInput/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,YAAY,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC"}
@@ -5,4 +5,5 @@ export * from "./BlockCheckbox";
5
5
  export * from "./BlockRadio";
6
6
  export * from "./BlockToggle";
7
7
  export * from "./BlockSlider";
8
+ export * from "./NumberInput";
8
9
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/forms/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,iBAAiB,CAAC;AAChC,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/forms/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,iBAAiB,CAAC;AAChC,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC"}
@@ -4,7 +4,7 @@ import { useInventoryGrid, useSlotIndex } from "../InventoryGrid/context.js";
4
4
  import InventorySlot_module_default from "./InventorySlot.module.js";
5
5
  import { forwardRef, useCallback, useId, useLayoutEffect, useRef, useState } from "react";
6
6
  import { LockIcon } from "@malilion/block-ui-icons";
7
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
7
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
8
8
  //#region src/components/inventory/InventorySlot/InventorySlot.tsx
9
9
  /**
10
10
  * A single inventory slot. Inside an `InventoryGrid` it becomes a roving-focus
@@ -61,7 +61,7 @@ var InventorySlot = forwardRef(function InventorySlot({ selected, disabled = fal
61
61
  }
62
62
  onKeyDown?.(event);
63
63
  };
64
- const content = /* @__PURE__ */ jsxs(Fragment, { children: [
64
+ const content = /* @__PURE__ */ jsxs(Fragment$1, { children: [
65
65
  /* @__PURE__ */ jsx("span", {
66
66
  className: InventorySlot_module_default.content,
67
67
  children
@@ -1 +1 @@
1
- {"version":3,"file":"InventorySlot.js","names":["_Fragment","styles"],"sources":["../../../../src/components/inventory/InventorySlot/InventorySlot.tsx"],"sourcesContent":["import { LockIcon } from \"@malilion/block-ui-icons\";\nimport {\n forwardRef,\n useCallback,\n useId,\n useLayoutEffect,\n useRef,\n useState,\n type FocusEvent,\n type KeyboardEvent,\n type MouseEvent,\n type Ref,\n} from \"react\";\nimport { cx } from \"../../../utils/cx\";\nimport { mergeRefs } from \"../../../utils/refs\";\nimport { useInventoryGrid, useSlotIndex } from \"../InventoryGrid/context\";\nimport styles from \"./InventorySlot.module.css\";\nimport type { InventorySlotProps } from \"./InventorySlot.types\";\n\ntype Placement = \"right\" | \"left\";\n\n/**\n * A single inventory slot. Inside an `InventoryGrid` it becomes a roving-focus\n * grid cell; standalone it is a toggle button (when `onClick` is set) or a\n * static well.\n */\nexport const InventorySlot = forwardRef<HTMLElement, InventorySlotProps>(function InventorySlot(\n {\n selected,\n disabled = false,\n locked = false,\n rarity,\n children,\n onClick,\n tooltip,\n label,\n size = \"md\",\n className,\n onKeyDown,\n onMouseEnter,\n onMouseLeave,\n onFocus,\n onBlur,\n ...rest\n },\n ref,\n) {\n const grid = useInventoryGrid();\n const index = useSlotIndex();\n const inGrid = grid !== null && index >= 0;\n const inactive = disabled || locked;\n const isSelected = selected ?? (inGrid ? grid.selectedIndex === index : false);\n const empty = children === undefined || children === null || children === false;\n /** Non-interactive wells cannot carry aria-label, so their label is rendered as hidden text. */\n const staticWell = !inGrid && !onClick;\n\n const tooltipId = useId();\n const [tooltipOpen, setTooltipOpen] = useState(false);\n const [placement, setPlacement] = useState<Placement>(\"right\");\n const tooltipRef = useRef<HTMLDivElement>(null);\n\n useLayoutEffect(() => {\n if (!tooltipOpen || !tooltipRef.current) return;\n const rect = tooltipRef.current.getBoundingClientRect();\n if (placement === \"right\" && rect.right > window.innerWidth - 8) setPlacement(\"left\");\n }, [tooltipOpen, placement]);\n\n const register = useCallback(\n (element: HTMLElement | null) => {\n if (inGrid) grid.registerCell(index, element);\n },\n [grid, inGrid, index],\n );\n\n const openTooltip = () => {\n if (tooltip) setTooltipOpen(true);\n };\n const closeTooltip = () => {\n setTooltipOpen(false);\n setPlacement(\"right\");\n };\n\n const handleClick = () => {\n if (inGrid) {\n grid.setFocusedIndex(index);\n if (!inactive) grid.select(index);\n }\n if (!inactive) onClick?.();\n };\n\n const handleKeyDown = (event: KeyboardEvent<HTMLElement>) => {\n if (event.key === \"Escape\" && tooltipOpen) {\n event.stopPropagation();\n closeTooltip();\n }\n if (inGrid && (event.key === \"Enter\" || event.key === \" \")) {\n event.preventDefault();\n handleClick();\n }\n onKeyDown?.(event);\n };\n\n const content = (\n <>\n <span className={styles.content}>{children}</span>\n {empty && !label ? <span className=\"block-visually-hidden\">Empty slot</span> : null}\n {label && staticWell ? <span className=\"block-visually-hidden\">{label}</span> : null}\n {locked ? (\n <span className={styles.lock} aria-hidden=\"true\">\n <LockIcon size={16} />\n </span>\n ) : null}\n {tooltip ? (\n <div\n ref={tooltipRef}\n id={tooltipId}\n role=\"tooltip\"\n aria-hidden=\"true\"\n hidden={!tooltipOpen}\n className={styles.tooltip}\n data-placement={placement}\n >\n {tooltip}\n </div>\n ) : null}\n </>\n );\n\n const shared = {\n className: cx(styles.slot, !inGrid && styles[size], className),\n \"data-selected\": isSelected || undefined,\n \"data-disabled\": disabled || undefined,\n \"data-locked\": locked || undefined,\n \"data-rarity\": rarity,\n \"data-empty\": empty || undefined,\n \"aria-label\": staticWell ? undefined : label,\n \"aria-describedby\": tooltip ? tooltipId : undefined,\n onKeyDown: handleKeyDown,\n onMouseEnter: (event: MouseEvent<HTMLElement>) => {\n openTooltip();\n onMouseEnter?.(event);\n },\n onMouseLeave: (event: MouseEvent<HTMLElement>) => {\n closeTooltip();\n onMouseLeave?.(event);\n },\n onFocus: (event: FocusEvent<HTMLElement>) => {\n openTooltip();\n onFocus?.(event);\n },\n onBlur: (event: FocusEvent<HTMLElement>) => {\n closeTooltip();\n onBlur?.(event);\n },\n ...rest,\n };\n\n if (inGrid) {\n return (\n <div\n ref={mergeRefs(ref, register)}\n role=\"gridcell\"\n tabIndex={grid.focusedIndex === index ? 0 : -1}\n aria-selected={isSelected}\n aria-disabled={inactive || undefined}\n onClick={handleClick}\n {...shared}\n onKeyDown={handleKeyDown}\n >\n {content}\n </div>\n );\n }\n\n if (onClick) {\n return (\n <button\n ref={ref as Ref<HTMLButtonElement>}\n type=\"button\"\n aria-pressed={isSelected}\n aria-disabled={inactive || undefined}\n onClick={handleClick}\n {...shared}\n >\n {content}\n </button>\n );\n }\n\n return (\n // Non-interactive slots with tooltips must be focusable so keyboard users can trigger the tooltip\n // eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex\n <div ref={ref as Ref<HTMLDivElement>} tabIndex={tooltip ? 0 : undefined} {...shared}>\n {content}\n </div>\n );\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"InventorySlot.js","names":["_Fragment","styles"],"sources":["../../../../src/components/inventory/InventorySlot/InventorySlot.tsx"],"sourcesContent":["import { LockIcon } from \"@malilion/block-ui-icons\";\nimport {\n forwardRef,\n useCallback,\n useId,\n useLayoutEffect,\n useRef,\n useState,\n type FocusEvent,\n type KeyboardEvent,\n type MouseEvent,\n type Ref,\n} from \"react\";\nimport { cx } from \"../../../utils/cx\";\nimport { mergeRefs } from \"../../../utils/refs\";\nimport { useInventoryGrid, useSlotIndex } from \"../InventoryGrid/context\";\nimport styles from \"./InventorySlot.module.css\";\nimport type { InventorySlotProps } from \"./InventorySlot.types\";\n\ntype Placement = \"right\" | \"left\";\n\n/**\n * A single inventory slot. Inside an `InventoryGrid` it becomes a roving-focus\n * grid cell; standalone it is a toggle button (when `onClick` is set) or a\n * static well.\n */\nexport const InventorySlot = forwardRef<HTMLElement, InventorySlotProps>(function InventorySlot(\n {\n selected,\n disabled = false,\n locked = false,\n rarity,\n children,\n onClick,\n tooltip,\n label,\n size = \"md\",\n className,\n onKeyDown,\n onMouseEnter,\n onMouseLeave,\n onFocus,\n onBlur,\n ...rest\n },\n ref,\n) {\n const grid = useInventoryGrid();\n const index = useSlotIndex();\n const inGrid = grid !== null && index >= 0;\n const inactive = disabled || locked;\n const isSelected = selected ?? (inGrid ? grid.selectedIndex === index : false);\n const empty = children === undefined || children === null || children === false;\n /** Non-interactive wells cannot carry aria-label, so their label is rendered as hidden text. */\n const staticWell = !inGrid && !onClick;\n\n const tooltipId = useId();\n const [tooltipOpen, setTooltipOpen] = useState(false);\n const [placement, setPlacement] = useState<Placement>(\"right\");\n const tooltipRef = useRef<HTMLDivElement>(null);\n\n useLayoutEffect(() => {\n if (!tooltipOpen || !tooltipRef.current) return;\n const rect = tooltipRef.current.getBoundingClientRect();\n if (placement === \"right\" && rect.right > window.innerWidth - 8) setPlacement(\"left\");\n }, [tooltipOpen, placement]);\n\n const register = useCallback(\n (element: HTMLElement | null) => {\n if (inGrid) grid.registerCell(index, element);\n },\n [grid, inGrid, index],\n );\n\n const openTooltip = () => {\n if (tooltip) setTooltipOpen(true);\n };\n const closeTooltip = () => {\n setTooltipOpen(false);\n setPlacement(\"right\");\n };\n\n const handleClick = () => {\n if (inGrid) {\n grid.setFocusedIndex(index);\n if (!inactive) grid.select(index);\n }\n if (!inactive) onClick?.();\n };\n\n const handleKeyDown = (event: KeyboardEvent<HTMLElement>) => {\n if (event.key === \"Escape\" && tooltipOpen) {\n event.stopPropagation();\n closeTooltip();\n }\n if (inGrid && (event.key === \"Enter\" || event.key === \" \")) {\n event.preventDefault();\n handleClick();\n }\n onKeyDown?.(event);\n };\n\n const content = (\n <>\n <span className={styles.content}>{children}</span>\n {empty && !label ? <span className=\"block-visually-hidden\">Empty slot</span> : null}\n {label && staticWell ? <span className=\"block-visually-hidden\">{label}</span> : null}\n {locked ? (\n <span className={styles.lock} aria-hidden=\"true\">\n <LockIcon size={16} />\n </span>\n ) : null}\n {tooltip ? (\n <div\n ref={tooltipRef}\n id={tooltipId}\n role=\"tooltip\"\n aria-hidden=\"true\"\n hidden={!tooltipOpen}\n className={styles.tooltip}\n data-placement={placement}\n >\n {tooltip}\n </div>\n ) : null}\n </>\n );\n\n const shared = {\n className: cx(styles.slot, !inGrid && styles[size], className),\n \"data-selected\": isSelected || undefined,\n \"data-disabled\": disabled || undefined,\n \"data-locked\": locked || undefined,\n \"data-rarity\": rarity,\n \"data-empty\": empty || undefined,\n \"aria-label\": staticWell ? undefined : label,\n \"aria-describedby\": tooltip ? tooltipId : undefined,\n onKeyDown: handleKeyDown,\n onMouseEnter: (event: MouseEvent<HTMLElement>) => {\n openTooltip();\n onMouseEnter?.(event);\n },\n onMouseLeave: (event: MouseEvent<HTMLElement>) => {\n closeTooltip();\n onMouseLeave?.(event);\n },\n onFocus: (event: FocusEvent<HTMLElement>) => {\n openTooltip();\n onFocus?.(event);\n },\n onBlur: (event: FocusEvent<HTMLElement>) => {\n closeTooltip();\n onBlur?.(event);\n },\n ...rest,\n };\n\n if (inGrid) {\n return (\n <div\n ref={mergeRefs(ref, register)}\n role=\"gridcell\"\n tabIndex={grid.focusedIndex === index ? 0 : -1}\n aria-selected={isSelected}\n aria-disabled={inactive || undefined}\n onClick={handleClick}\n {...shared}\n onKeyDown={handleKeyDown}\n >\n {content}\n </div>\n );\n }\n\n if (onClick) {\n return (\n <button\n ref={ref as Ref<HTMLButtonElement>}\n type=\"button\"\n aria-pressed={isSelected}\n aria-disabled={inactive || undefined}\n onClick={handleClick}\n {...shared}\n >\n {content}\n </button>\n );\n }\n\n return (\n // Non-interactive slots with tooltips must be focusable so keyboard users can trigger the tooltip\n // eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex\n <div ref={ref as Ref<HTMLDivElement>} tabIndex={tooltip ? 0 : undefined} {...shared}>\n {content}\n </div>\n );\n});\n"],"mappings":";;;;;;;;;;;;;AA0BA,IAAa,gBAAgB,WAA4C,SAAS,cAChF,EACE,UACA,WAAW,OACX,SAAS,OACT,QACA,UACA,SACA,SACA,OACA,OAAO,MACP,WACA,WACA,cACA,cACA,SACA,QACA,GAAG,QAEL,KACA;CACA,MAAM,OAAO,iBAAiB;CAC9B,MAAM,QAAQ,aAAa;CAC3B,MAAM,SAAS,SAAS,QAAQ,SAAS;CACzC,MAAM,WAAW,YAAY;CAC7B,MAAM,aAAa,aAAa,SAAS,KAAK,kBAAkB,QAAQ;CACxE,MAAM,QAAQ,aAAa,KAAA,KAAa,aAAa,QAAQ,aAAa;;CAE1E,MAAM,aAAa,CAAC,UAAU,CAAC;CAE/B,MAAM,YAAY,MAAM;CACxB,MAAM,CAAC,aAAa,kBAAkB,SAAS,KAAK;CACpD,MAAM,CAAC,WAAW,gBAAgB,SAAoB,OAAO;CAC7D,MAAM,aAAa,OAAuB,IAAI;CAE9C,sBAAsB;EACpB,IAAI,CAAC,eAAe,CAAC,WAAW,SAAS;EACzC,MAAM,OAAO,WAAW,QAAQ,sBAAsB;EACtD,IAAI,cAAc,WAAW,KAAK,QAAQ,OAAO,aAAa,GAAG,aAAa,MAAM;CACtF,GAAG,CAAC,aAAa,SAAS,CAAC;CAE3B,MAAM,WAAW,aACd,YAAgC;EAC/B,IAAI,QAAQ,KAAK,aAAa,OAAO,OAAO;CAC9C,GACA;EAAC;EAAM;EAAQ;CAAK,CACtB;CAEA,MAAM,oBAAoB;EACxB,IAAI,SAAS,eAAe,IAAI;CAClC;CACA,MAAM,qBAAqB;EACzB,eAAe,KAAK;EACpB,aAAa,OAAO;CACtB;CAEA,MAAM,oBAAoB;EACxB,IAAI,QAAQ;GACV,KAAK,gBAAgB,KAAK;GAC1B,IAAI,CAAC,UAAU,KAAK,OAAO,KAAK;EAClC;EACA,IAAI,CAAC,UAAU,UAAU;CAC3B;CAEA,MAAM,iBAAiB,UAAsC;EAC3D,IAAI,MAAM,QAAQ,YAAY,aAAa;GACzC,MAAM,gBAAgB;GACtB,aAAa;EACf;EACA,IAAI,WAAW,MAAM,QAAQ,WAAW,MAAM,QAAQ,MAAM;GAC1D,MAAM,eAAe;GACrB,YAAY;EACd;EACA,YAAY,KAAK;CACnB;CAEA,MAAM,UACJ,qBAAAA,YAAA,EAAA,UAAA;EACE,oBAAC,QAAD;GAAM,WAAWC,6BAAO;GAAU;EAAe,CAAA;EAChD,SAAS,CAAC,QAAQ,oBAAC,QAAD;GAAM,WAAU;GAAwB,UAAA;EAAgB,CAAA,IAAI;EAC9E,SAAS,aAAa,oBAAC,QAAD;GAAM,WAAU;GAAyB,UAAA;EAAY,CAAA,IAAI;EAC/E,SACC,oBAAC,QAAD;GAAM,WAAWA,6BAAO;GAAM,eAAY;GACxC,UAAA,oBAAC,UAAD,EAAU,MAAM,GAAK,CAAA;EACjB,CAAA,IACJ;EACH,UACC,oBAAC,OAAD;GACE,KAAK;GACL,IAAI;GACJ,MAAK;GACL,eAAY;GACZ,QAAQ,CAAC;GACT,WAAWA,6BAAO;GAClB,kBAAgB;GAEf,UAAA;EACE,CAAA,IACH;CACJ,EAAA,CAAA;CAGJ,MAAM,SAAS;EACb,WAAW,GAAGA,6BAAO,MAAM,CAAC,UAAUA,6BAAO,OAAO,SAAS;EAC7D,iBAAiB,cAAc,KAAA;EAC/B,iBAAiB,YAAY,KAAA;EAC7B,eAAe,UAAU,KAAA;EACzB,eAAe;EACf,cAAc,SAAS,KAAA;EACvB,cAAc,aAAa,KAAA,IAAY;EACvC,oBAAoB,UAAU,YAAY,KAAA;EAC1C,WAAW;EACX,eAAe,UAAmC;GAChD,YAAY;GACZ,eAAe,KAAK;EACtB;EACA,eAAe,UAAmC;GAChD,aAAa;GACb,eAAe,KAAK;EACtB;EACA,UAAU,UAAmC;GAC3C,YAAY;GACZ,UAAU,KAAK;EACjB;EACA,SAAS,UAAmC;GAC1C,aAAa;GACb,SAAS,KAAK;EAChB;EACA,GAAG;CACL;CAEA,IAAI,QACF,OACE,oBAAC,OAAD;EACE,KAAK,UAAU,KAAK,QAAQ;EAC5B,MAAK;EACL,UAAU,KAAK,iBAAiB,QAAQ,IAAI;EAC5C,iBAAe;EACf,iBAAe,YAAY,KAAA;EAC3B,SAAS;EACT,GAAI;EACJ,WAAW;EAEV,UAAA;CACE,CAAA;CAIT,IAAI,SACF,OACE,oBAAC,UAAD;EACO;EACL,MAAK;EACL,gBAAc;EACd,iBAAe,YAAY,KAAA;EAC3B,SAAS;EACT,GAAI;EAEH,UAAA;CACK,CAAA;CAIZ,OAGE,oBAAC,OAAD;EAAU;EAA4B,UAAU,UAAU,IAAI,KAAA;EAAW,GAAI;EAC1E,UAAA;CACE,CAAA;AAET,CAAC"}
@@ -0,0 +1,7 @@
1
+ import type { AccordionProps } from "./Accordion.types";
2
+ /**
3
+ * Collapsible sections following the WAI-ARIA accordion pattern: each header is
4
+ * a button inside a heading; ↑ / ↓ / Home / End move between headers.
5
+ */
6
+ export declare const Accordion: import("react").ForwardRefExoticComponent<AccordionProps & import("react").RefAttributes<HTMLDivElement>>;
7
+ //# sourceMappingURL=Accordion.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Accordion.d.ts","sourceRoot":"","sources":["../../../../src/components/layout/Accordion/Accordion.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAIxD;;;GAGG;AACH,eAAO,MAAM,SAAS,2GA0FpB,CAAC"}
@@ -0,0 +1,95 @@
1
+ import { cx } from "../../../utils/cx.js";
2
+ import { useControllableState } from "../../../hooks/useControllableState.js";
3
+ import Accordion_module_default from "./Accordion.module.js";
4
+ import { createElement, forwardRef, useId, useRef } from "react";
5
+ import { ChevronDownIcon } from "@malilion/block-ui-icons";
6
+ import { jsx, jsxs } from "react/jsx-runtime";
7
+ //#region src/components/layout/Accordion/Accordion.tsx
8
+ var EMPTY = [];
9
+ /**
10
+ * Collapsible sections following the WAI-ARIA accordion pattern: each header is
11
+ * a button inside a heading; ↑ / ↓ / Home / End move between headers.
12
+ */
13
+ var Accordion = forwardRef(function Accordion({ items, type = "single", value, defaultValue = EMPTY, onValueChange, headingLevel = 3, className, ...rest }, ref) {
14
+ const baseId = useId();
15
+ const [open, setOpen] = useControllableState({
16
+ value,
17
+ defaultValue,
18
+ onChange: onValueChange
19
+ });
20
+ const triggers = useRef(/* @__PURE__ */ new Map());
21
+ const enabled = items.filter((item) => !item.disabled);
22
+ const toggle = (id) => {
23
+ const isOpen = open.includes(id);
24
+ if (type === "single") setOpen(isOpen ? [] : [id]);
25
+ else setOpen(isOpen ? open.filter((openId) => openId !== id) : [...open, id]);
26
+ };
27
+ const onKeyDown = (event) => {
28
+ const index = enabled.findIndex((item) => triggers.current.get(item.id) === document.activeElement);
29
+ if (index < 0) return;
30
+ let next = null;
31
+ if (event.key === "ArrowDown") next = (index + 1) % enabled.length;
32
+ else if (event.key === "ArrowUp") next = (index - 1 + enabled.length) % enabled.length;
33
+ else if (event.key === "Home") next = 0;
34
+ else if (event.key === "End") next = enabled.length - 1;
35
+ if (next === null) return;
36
+ event.preventDefault();
37
+ const target = enabled[next];
38
+ if (target) triggers.current.get(target.id)?.focus();
39
+ };
40
+ return /* @__PURE__ */ jsx("div", {
41
+ ref,
42
+ className: cx(Accordion_module_default.accordion, className),
43
+ ...rest,
44
+ children: items.map((item) => {
45
+ const isOpen = open.includes(item.id);
46
+ const triggerId = `${baseId}-trigger-${item.id}`;
47
+ const panelId = `${baseId}-panel-${item.id}`;
48
+ return /* @__PURE__ */ jsxs("div", {
49
+ className: Accordion_module_default.item,
50
+ "data-open": isOpen || void 0,
51
+ children: [createElement(`h${headingLevel}`, { className: Accordion_module_default.heading }, /* @__PURE__ */ jsxs("button", {
52
+ ref: (element) => {
53
+ if (element) triggers.current.set(item.id, element);
54
+ else triggers.current.delete(item.id);
55
+ },
56
+ id: triggerId,
57
+ type: "button",
58
+ "aria-expanded": isOpen,
59
+ "aria-controls": panelId,
60
+ disabled: item.disabled,
61
+ className: Accordion_module_default.trigger,
62
+ onClick: () => toggle(item.id),
63
+ onKeyDown,
64
+ children: [
65
+ item.icon ? /* @__PURE__ */ jsx("span", {
66
+ className: Accordion_module_default.icon,
67
+ "aria-hidden": "true",
68
+ children: item.icon
69
+ }) : null,
70
+ /* @__PURE__ */ jsx("span", {
71
+ className: Accordion_module_default.title,
72
+ children: item.title
73
+ }),
74
+ /* @__PURE__ */ jsx("span", {
75
+ className: Accordion_module_default.chevron,
76
+ "aria-hidden": "true",
77
+ children: /* @__PURE__ */ jsx(ChevronDownIcon, { size: 16 })
78
+ })
79
+ ]
80
+ })), /* @__PURE__ */ jsx("div", {
81
+ id: panelId,
82
+ role: "region",
83
+ "aria-labelledby": triggerId,
84
+ hidden: !isOpen,
85
+ className: Accordion_module_default.panel,
86
+ children: item.content
87
+ })]
88
+ }, item.id);
89
+ })
90
+ });
91
+ });
92
+ //#endregion
93
+ export { Accordion };
94
+
95
+ //# sourceMappingURL=Accordion.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Accordion.js","names":["styles"],"sources":["../../../../src/components/layout/Accordion/Accordion.tsx"],"sourcesContent":["import { ChevronDownIcon } from \"@malilion/block-ui-icons\";\nimport { createElement, forwardRef, useId, useRef, type KeyboardEvent } from \"react\";\nimport { useControllableState } from \"../../../hooks/useControllableState\";\nimport { cx } from \"../../../utils/cx\";\nimport styles from \"./Accordion.module.css\";\nimport type { AccordionProps } from \"./Accordion.types\";\n\nconst EMPTY: string[] = [];\n\n/**\n * Collapsible sections following the WAI-ARIA accordion pattern: each header is\n * a button inside a heading; ↑ / ↓ / Home / End move between headers.\n */\nexport const Accordion = forwardRef<HTMLDivElement, AccordionProps>(function Accordion(\n {\n items,\n type = \"single\",\n value,\n defaultValue = EMPTY,\n onValueChange,\n headingLevel = 3,\n className,\n ...rest\n },\n ref,\n) {\n const baseId = useId();\n const [open, setOpen] = useControllableState({ value, defaultValue, onChange: onValueChange });\n const triggers = useRef(new Map<string, HTMLButtonElement>());\n const enabled = items.filter((item) => !item.disabled);\n\n const toggle = (id: string) => {\n const isOpen = open.includes(id);\n if (type === \"single\") setOpen(isOpen ? [] : [id]);\n else setOpen(isOpen ? open.filter((openId) => openId !== id) : [...open, id]);\n };\n\n const onKeyDown = (event: KeyboardEvent<HTMLButtonElement>) => {\n const index = enabled.findIndex(\n (item) => triggers.current.get(item.id) === document.activeElement,\n );\n if (index < 0) return;\n let next: number | null = null;\n if (event.key === \"ArrowDown\") next = (index + 1) % enabled.length;\n else if (event.key === \"ArrowUp\") next = (index - 1 + enabled.length) % enabled.length;\n else if (event.key === \"Home\") next = 0;\n else if (event.key === \"End\") next = enabled.length - 1;\n if (next === null) return;\n event.preventDefault();\n const target = enabled[next];\n if (target) triggers.current.get(target.id)?.focus();\n };\n\n return (\n <div ref={ref} className={cx(styles.accordion, className)} {...rest}>\n {items.map((item) => {\n const isOpen = open.includes(item.id);\n const triggerId = `${baseId}-trigger-${item.id}`;\n const panelId = `${baseId}-panel-${item.id}`;\n return (\n <div key={item.id} className={styles.item} data-open={isOpen || undefined}>\n {createElement(\n `h${headingLevel}`,\n { className: styles.heading },\n <button\n ref={(element) => {\n if (element) triggers.current.set(item.id, element);\n else triggers.current.delete(item.id);\n }}\n id={triggerId}\n type=\"button\"\n aria-expanded={isOpen}\n aria-controls={panelId}\n disabled={item.disabled}\n className={styles.trigger}\n onClick={() => toggle(item.id)}\n onKeyDown={onKeyDown}\n >\n {item.icon ? (\n <span className={styles.icon} aria-hidden=\"true\">\n {item.icon}\n </span>\n ) : null}\n <span className={styles.title}>{item.title}</span>\n <span className={styles.chevron} aria-hidden=\"true\">\n <ChevronDownIcon size={16} />\n </span>\n </button>,\n )}\n <div\n id={panelId}\n role=\"region\"\n aria-labelledby={triggerId}\n hidden={!isOpen}\n className={styles.panel}\n >\n {item.content}\n </div>\n </div>\n );\n })}\n </div>\n );\n});\n"],"mappings":";;;;;;;AAOA,IAAM,QAAkB,CAAC;;;;;AAMzB,IAAa,YAAY,WAA2C,SAAS,UAC3E,EACE,OACA,OAAO,UACP,OACA,eAAe,OACf,eACA,eAAe,GACf,WACA,GAAG,QAEL,KACA;CACA,MAAM,SAAS,MAAM;CACrB,MAAM,CAAC,MAAM,WAAW,qBAAqB;EAAE;EAAO;EAAc,UAAU;CAAc,CAAC;CAC7F,MAAM,WAAW,uBAAO,IAAI,IAA+B,CAAC;CAC5D,MAAM,UAAU,MAAM,QAAQ,SAAS,CAAC,KAAK,QAAQ;CAErD,MAAM,UAAU,OAAe;EAC7B,MAAM,SAAS,KAAK,SAAS,EAAE;EAC/B,IAAI,SAAS,UAAU,QAAQ,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC;OAC5C,QAAQ,SAAS,KAAK,QAAQ,WAAW,WAAW,EAAE,IAAI,CAAC,GAAG,MAAM,EAAE,CAAC;CAC9E;CAEA,MAAM,aAAa,UAA4C;EAC7D,MAAM,QAAQ,QAAQ,WACnB,SAAS,SAAS,QAAQ,IAAI,KAAK,EAAE,MAAM,SAAS,aACvD;EACA,IAAI,QAAQ,GAAG;EACf,IAAI,OAAsB;EAC1B,IAAI,MAAM,QAAQ,aAAa,QAAQ,QAAQ,KAAK,QAAQ;OACvD,IAAI,MAAM,QAAQ,WAAW,QAAQ,QAAQ,IAAI,QAAQ,UAAU,QAAQ;OAC3E,IAAI,MAAM,QAAQ,QAAQ,OAAO;OACjC,IAAI,MAAM,QAAQ,OAAO,OAAO,QAAQ,SAAS;EACtD,IAAI,SAAS,MAAM;EACnB,MAAM,eAAe;EACrB,MAAM,SAAS,QAAQ;EACvB,IAAI,QAAQ,SAAS,QAAQ,IAAI,OAAO,EAAE,CAAC,EAAE,MAAM;CACrD;CAEA,OACE,oBAAC,OAAD;EAAU;EAAK,WAAW,GAAGA,yBAAO,WAAW,SAAS;EAAG,GAAI;EAC5D,UAAA,MAAM,KAAK,SAAS;GACnB,MAAM,SAAS,KAAK,SAAS,KAAK,EAAE;GACpC,MAAM,YAAY,GAAG,OAAO,WAAW,KAAK;GAC5C,MAAM,UAAU,GAAG,OAAO,SAAS,KAAK;GACxC,OACE,qBAAC,OAAD;IAAmB,WAAWA,yBAAO;IAAM,aAAW,UAAU,KAAA;IAAhE,UAAA,CACG,cACC,IAAI,gBACJ,EAAE,WAAWA,yBAAO,QAAQ,GAC5B,qBAAC,UAAD;KACE,MAAM,YAAY;MAChB,IAAI,SAAS,SAAS,QAAQ,IAAI,KAAK,IAAI,OAAO;WAC7C,SAAS,QAAQ,OAAO,KAAK,EAAE;KACtC;KACA,IAAI;KACJ,MAAK;KACL,iBAAe;KACf,iBAAe;KACf,UAAU,KAAK;KACf,WAAWA,yBAAO;KAClB,eAAe,OAAO,KAAK,EAAE;KAClB;KAZb,UAAA;MAcG,KAAK,OACJ,oBAAC,QAAD;OAAM,WAAWA,yBAAO;OAAM,eAAY;OACvC,UAAA,KAAK;MACF,CAAA,IACJ;MACJ,oBAAC,QAAD;OAAM,WAAWA,yBAAO;OAAQ,UAAA,KAAK;MAAY,CAAA;MACjD,oBAAC,QAAD;OAAM,WAAWA,yBAAO;OAAS,eAAY;OAC3C,UAAA,oBAAC,iBAAD,EAAiB,MAAM,GAAK,CAAA;MACxB,CAAA;KACA;IACV,CAAA,CAAA,GACA,oBAAC,OAAD;KACE,IAAI;KACJ,MAAK;KACL,mBAAiB;KACjB,QAAQ,CAAC;KACT,WAAWA,yBAAO;KAEjB,UAAA,KAAK;IACH,CAAA,CACF;GAtCK,GAAA,KAAK,EAsCV;EAET,CAAC;CACE,CAAA;AAET,CAAC"}
@@ -0,0 +1,23 @@
1
+ //#region src/components/layout/Accordion/Accordion.module.css
2
+ var accordion = "block-accordion-u7KUD";
3
+ var item = "block-item-Anf9u";
4
+ var heading = "block-heading-YzlrO";
5
+ var trigger = "block-trigger-r-rVI";
6
+ var icon = "block-icon-ja4pL";
7
+ var title = "block-title-xq19m";
8
+ var chevron = "block-chevron-hKGfT";
9
+ var panel = "block-panel-wcIDx";
10
+ var Accordion_module_default = {
11
+ accordion,
12
+ item,
13
+ heading,
14
+ trigger,
15
+ icon,
16
+ title,
17
+ chevron,
18
+ panel
19
+ };
20
+ //#endregion
21
+ export { accordion, chevron, Accordion_module_default as default, heading, icon, item, panel, title, trigger };
22
+
23
+ //# sourceMappingURL=Accordion.module.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Accordion.module.js","names":[],"sources":["../../../../src/components/layout/Accordion/Accordion.module.css"],"sourcesContent":[".accordion {\n display: flex;\n flex-direction: column;\n gap: var(--block-space-1);\n width: 100%;\n}\n\n.item {\n background-color: var(--block-surface);\n border: var(--block-border-width) solid var(--block-border);\n box-shadow: var(--block-shadow-bevel);\n}\n\n.heading {\n margin: 0;\n}\n\n.trigger {\n display: flex;\n align-items: center;\n gap: var(--block-space-2);\n width: 100%;\n min-height: 44px;\n padding: var(--block-space-2) var(--block-space-3);\n font-family: var(--block-font-display);\n font-size: var(--block-font-size-sm);\n font-weight: var(--block-font-weight-bold);\n letter-spacing: var(--block-letter-spacing-wide);\n text-align: left;\n text-transform: uppercase;\n color: var(--block-text);\n background-color: var(--block-surface-header);\n border: 0;\n cursor: pointer;\n}\n\n.trigger:hover:not(:disabled) {\n background-color: var(--block-slot-hover);\n}\n\n.trigger:focus-visible {\n outline: 3px solid var(--block-focus);\n outline-offset: -3px;\n}\n\n.trigger:disabled {\n cursor: not-allowed;\n opacity: 0.5;\n}\n\n.icon {\n display: inline-flex;\n flex-shrink: 0;\n}\n\n.title {\n flex: 1;\n min-width: 0;\n overflow-wrap: anywhere;\n}\n\n.chevron {\n display: inline-flex;\n flex-shrink: 0;\n transition: transform var(--block-duration-instant) var(--block-ease-step);\n}\n\n.item[data-open] .chevron {\n transform: rotate(180deg);\n}\n\n.panel {\n padding: var(--block-space-3);\n border-top: var(--block-border-width-thin) solid var(--block-border);\n color: var(--block-text);\n}\n\n.panel[hidden] {\n display: none;\n}\n"],"mappings":""}
@@ -0,0 +1,21 @@
1
+ import type { HTMLAttributes, ReactNode } from "react";
2
+ export interface AccordionItem {
3
+ id: string;
4
+ title: ReactNode;
5
+ content: ReactNode;
6
+ icon?: ReactNode;
7
+ disabled?: boolean;
8
+ }
9
+ export interface AccordionProps extends Omit<HTMLAttributes<HTMLDivElement>, "defaultValue"> {
10
+ items: AccordionItem[];
11
+ /** `single` keeps at most one section open. @default "single" */
12
+ type?: "single" | "multiple";
13
+ /** Controlled open section ids. */
14
+ value?: string[];
15
+ /** Initially open section ids. @default [] */
16
+ defaultValue?: string[];
17
+ onValueChange?: (open: string[]) => void;
18
+ /** Heading level wrapping each trigger. @default 3 */
19
+ headingLevel?: 2 | 3 | 4 | 5 | 6;
20
+ }
21
+ //# sourceMappingURL=Accordion.types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Accordion.types.d.ts","sourceRoot":"","sources":["../../../../src/components/layout/Accordion/Accordion.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvD,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,SAAS,CAAC;IACjB,OAAO,EAAE,SAAS,CAAC;IACnB,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,cAAe,SAAQ,IAAI,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,cAAc,CAAC;IAC1F,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,iEAAiE;IACjE,IAAI,CAAC,EAAE,QAAQ,GAAG,UAAU,CAAC;IAC7B,mCAAmC;IACnC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,8CAA8C;IAC9C,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IACzC,sDAAsD;IACtD,YAAY,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;CAClC"}
@@ -0,0 +1,3 @@
1
+ export { Accordion } from "./Accordion";
2
+ export type { AccordionItem, AccordionProps } from "./Accordion.types";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/layout/Accordion/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { BlockContainerProps } from "./BlockContainer.types";
2
+ /** Centres content at a readable max width with 16px side gutters. */
3
+ export declare const BlockContainer: import("react").ForwardRefExoticComponent<BlockContainerProps & import("react").RefAttributes<HTMLElement>>;
4
+ //# sourceMappingURL=BlockContainer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BlockContainer.d.ts","sourceRoot":"","sources":["../../../../src/components/layout/BlockContainer/BlockContainer.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAElE,sEAAsE;AACtE,eAAO,MAAM,cAAc,6GAezB,CAAC"}
@@ -0,0 +1,18 @@
1
+ import { cx } from "../../../utils/cx.js";
2
+ import BlockContainer_module_default from "./BlockContainer.module.js";
3
+ import { createElement, forwardRef } from "react";
4
+ //#region src/components/layout/BlockContainer/BlockContainer.tsx
5
+ /** Centres content at a readable max width with 16px side gutters. */
6
+ var BlockContainer = forwardRef(function BlockContainer({ size = "lg", flush = false, as = "div", className, children, ...rest }, ref) {
7
+ return createElement(as, {
8
+ ref,
9
+ "data-size": size,
10
+ "data-flush": flush || void 0,
11
+ className: cx(BlockContainer_module_default.container, className),
12
+ ...rest
13
+ }, children);
14
+ });
15
+ //#endregion
16
+ export { BlockContainer };
17
+
18
+ //# sourceMappingURL=BlockContainer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BlockContainer.js","names":["styles"],"sources":["../../../../src/components/layout/BlockContainer/BlockContainer.tsx"],"sourcesContent":["import { createElement, forwardRef } from \"react\";\nimport { cx } from \"../../../utils/cx\";\nimport styles from \"./BlockContainer.module.css\";\nimport type { BlockContainerProps } from \"./BlockContainer.types\";\n\n/** Centres content at a readable max width with 16px side gutters. */\nexport const BlockContainer = forwardRef<HTMLElement, BlockContainerProps>(function BlockContainer(\n { size = \"lg\", flush = false, as = \"div\", className, children, ...rest },\n ref,\n) {\n return createElement(\n as,\n {\n ref,\n \"data-size\": size,\n \"data-flush\": flush || undefined,\n className: cx(styles.container, className),\n ...rest,\n },\n children,\n );\n});\n"],"mappings":";;;;;AAMA,IAAa,iBAAiB,WAA6C,SAAS,eAClF,EAAE,OAAO,MAAM,QAAQ,OAAO,KAAK,OAAO,WAAW,UAAU,GAAG,QAClE,KACA;CACA,OAAO,cACL,IACA;EACE;EACA,aAAa;EACb,cAAc,SAAS,KAAA;EACvB,WAAW,GAAGA,8BAAO,WAAW,SAAS;EACzC,GAAG;CACL,GACA,QACF;AACF,CAAC"}
@@ -0,0 +1,7 @@
1
+ //#region src/components/layout/BlockContainer/BlockContainer.module.css
2
+ var container = "block-container-UJuv-";
3
+ var BlockContainer_module_default = { container };
4
+ //#endregion
5
+ export { container, BlockContainer_module_default as default };
6
+
7
+ //# sourceMappingURL=BlockContainer.module.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BlockContainer.module.js","names":[],"sources":["../../../../src/components/layout/BlockContainer/BlockContainer.module.css"],"sourcesContent":[".container {\n box-sizing: border-box;\n width: 100%;\n max-width: 1024px;\n margin-inline: auto;\n padding-inline: var(--block-space-4);\n}\n\n.container[data-size=\"sm\"] {\n max-width: 640px;\n}\n\n.container[data-size=\"md\"] {\n max-width: 768px;\n}\n\n.container[data-size=\"xl\"] {\n max-width: 1280px;\n}\n\n.container[data-size=\"full\"] {\n max-width: none;\n}\n\n.container[data-flush] {\n padding-inline: 0;\n}\n"],"mappings":""}
@@ -0,0 +1,11 @@
1
+ import type { HTMLAttributes, ReactNode } from "react";
2
+ export interface BlockContainerProps extends HTMLAttributes<HTMLElement> {
3
+ /** Max width: 640 / 768 / 1024 / 1280 px (the breakpoints) or `full`. @default "lg" */
4
+ size?: "sm" | "md" | "lg" | "xl" | "full";
5
+ /** Remove the 16px side gutters. */
6
+ flush?: boolean;
7
+ /** Render as a different element. @default "div" */
8
+ as?: "div" | "main" | "section" | "article" | "header" | "footer";
9
+ children?: ReactNode;
10
+ }
11
+ //# sourceMappingURL=BlockContainer.types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BlockContainer.types.d.ts","sourceRoot":"","sources":["../../../../src/components/layout/BlockContainer/BlockContainer.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvD,MAAM,WAAW,mBAAoB,SAAQ,cAAc,CAAC,WAAW,CAAC;IACtE,uFAAuF;IACvF,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,MAAM,CAAC;IAC1C,oCAAoC;IACpC,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,oDAAoD;IACpD,EAAE,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,GAAG,QAAQ,CAAC;IAClE,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB"}
@@ -0,0 +1,3 @@
1
+ export { BlockContainer } from "./BlockContainer";
2
+ export type { BlockContainerProps } from "./BlockContainer.types";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/layout/BlockContainer/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,YAAY,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { BlockGridProps } from "./BlockGrid.types";
2
+ /** Two-dimensional grid: fixed equal columns, or auto-fill with a minimum item width. */
3
+ export declare const BlockGrid: import("react").ForwardRefExoticComponent<BlockGridProps & import("react").RefAttributes<HTMLElement>>;
4
+ //# sourceMappingURL=BlockGrid.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BlockGrid.d.ts","sourceRoot":"","sources":["../../../../src/components/layout/BlockGrid/BlockGrid.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAExD,yFAAyF;AACzF,eAAO,MAAM,SAAS,wGAgCpB,CAAC"}