@malilion/block-ui-react 0.4.0 → 0.5.1

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 (99) 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/Kbd/Kbd.d.ts +4 -0
  27. package/dist/components/display/Kbd/Kbd.d.ts.map +1 -0
  28. package/dist/components/display/Kbd/Kbd.js +33 -0
  29. package/dist/components/display/Kbd/Kbd.js.map +1 -0
  30. package/dist/components/display/Kbd/Kbd.module.js +13 -0
  31. package/dist/components/display/Kbd/Kbd.module.js.map +1 -0
  32. package/dist/components/display/Kbd/Kbd.types.d.ts +9 -0
  33. package/dist/components/display/Kbd/Kbd.types.d.ts.map +1 -0
  34. package/dist/components/display/Kbd/index.d.ts +3 -0
  35. package/dist/components/display/Kbd/index.d.ts.map +1 -0
  36. package/dist/components/display/Popover/Popover.d.ts +8 -0
  37. package/dist/components/display/Popover/Popover.d.ts.map +1 -0
  38. package/dist/components/display/Popover/Popover.js +101 -0
  39. package/dist/components/display/Popover/Popover.js.map +1 -0
  40. package/dist/components/display/Popover/Popover.module.js +15 -0
  41. package/dist/components/display/Popover/Popover.module.js.map +1 -0
  42. package/dist/components/display/Popover/Popover.types.d.ts +26 -0
  43. package/dist/components/display/Popover/Popover.types.d.ts.map +1 -0
  44. package/dist/components/display/Popover/index.d.ts +3 -0
  45. package/dist/components/display/Popover/index.d.ts.map +1 -0
  46. package/dist/components/display/SkillTree/SkillTree.js +2 -2
  47. package/dist/components/display/SkillTree/SkillTree.js.map +1 -1
  48. package/dist/components/display/index.d.ts +2 -0
  49. package/dist/components/display/index.d.ts.map +1 -1
  50. package/dist/components/feedback/ConfirmDialog/ConfirmDialog.js +2 -2
  51. package/dist/components/feedback/ConfirmDialog/ConfirmDialog.js.map +1 -1
  52. package/dist/components/inventory/InventorySlot/InventorySlot.js +2 -2
  53. package/dist/components/inventory/InventorySlot/InventorySlot.js.map +1 -1
  54. package/dist/components/layout/BlockContainer/BlockContainer.d.ts +4 -0
  55. package/dist/components/layout/BlockContainer/BlockContainer.d.ts.map +1 -0
  56. package/dist/components/layout/BlockContainer/BlockContainer.js +18 -0
  57. package/dist/components/layout/BlockContainer/BlockContainer.js.map +1 -0
  58. package/dist/components/layout/BlockContainer/BlockContainer.module.js +7 -0
  59. package/dist/components/layout/BlockContainer/BlockContainer.module.js.map +1 -0
  60. package/dist/components/layout/BlockContainer/BlockContainer.types.d.ts +11 -0
  61. package/dist/components/layout/BlockContainer/BlockContainer.types.d.ts.map +1 -0
  62. package/dist/components/layout/BlockContainer/index.d.ts +3 -0
  63. package/dist/components/layout/BlockContainer/index.d.ts.map +1 -0
  64. package/dist/components/layout/BlockGrid/BlockGrid.d.ts +4 -0
  65. package/dist/components/layout/BlockGrid/BlockGrid.d.ts.map +1 -0
  66. package/dist/components/layout/BlockGrid/BlockGrid.js +25 -0
  67. package/dist/components/layout/BlockGrid/BlockGrid.js.map +1 -0
  68. package/dist/components/layout/BlockGrid/BlockGrid.module.js +7 -0
  69. package/dist/components/layout/BlockGrid/BlockGrid.module.js.map +1 -0
  70. package/dist/components/layout/BlockGrid/BlockGrid.types.d.ts +16 -0
  71. package/dist/components/layout/BlockGrid/BlockGrid.types.d.ts.map +1 -0
  72. package/dist/components/layout/BlockGrid/index.d.ts +3 -0
  73. package/dist/components/layout/BlockGrid/index.d.ts.map +1 -0
  74. package/dist/components/layout/index.d.ts +2 -0
  75. package/dist/components/layout/index.d.ts.map +1 -1
  76. package/dist/components/navigation/BlockSidebar/BlockSidebar.js +3 -3
  77. package/dist/components/navigation/BlockSidebar/BlockSidebar.js.map +1 -1
  78. package/dist/components/navigation/BlockStepper/BlockStepper.d.ts +8 -0
  79. package/dist/components/navigation/BlockStepper/BlockStepper.d.ts.map +1 -0
  80. package/dist/components/navigation/BlockStepper/BlockStepper.js +66 -0
  81. package/dist/components/navigation/BlockStepper/BlockStepper.js.map +1 -0
  82. package/dist/components/navigation/BlockStepper/BlockStepper.module.js +23 -0
  83. package/dist/components/navigation/BlockStepper/BlockStepper.module.js.map +1 -0
  84. package/dist/components/navigation/BlockStepper/BlockStepper.types.d.ts +19 -0
  85. package/dist/components/navigation/BlockStepper/BlockStepper.types.d.ts.map +1 -0
  86. package/dist/components/navigation/BlockStepper/index.d.ts +3 -0
  87. package/dist/components/navigation/BlockStepper/index.d.ts.map +1 -0
  88. package/dist/components/navigation/Breadcrumb/Breadcrumb.js +2 -2
  89. package/dist/components/navigation/Breadcrumb/Breadcrumb.js.map +1 -1
  90. package/dist/components/navigation/HotbarNavigation/HotbarNavigation.js +3 -3
  91. package/dist/components/navigation/HotbarNavigation/HotbarNavigation.js.map +1 -1
  92. package/dist/components/navigation/index.d.ts +1 -0
  93. package/dist/components/navigation/index.d.ts.map +1 -1
  94. package/dist/hooks/useAnchoredPosition.d.ts +3 -1
  95. package/dist/hooks/useAnchoredPosition.d.ts.map +1 -1
  96. package/dist/hooks/useAnchoredPosition.js.map +1 -1
  97. package/dist/index.js +7 -1
  98. package/dist/styles.css +281 -0
  99. package/package.json +4 -4
@@ -1,4 +1,5 @@
1
1
  export * from "./BlockButton";
2
2
  export * from "./IconButton";
3
3
  export * from "./BlockMenu";
4
+ export * from "./ContextMenu";
4
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/actions/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/actions/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC"}
@@ -8,7 +8,7 @@ import RecipeBook_module_default from "./RecipeBook.module.js";
8
8
  import { deriveCategories, filterRecipes } from "./RecipeBook.utils.js";
9
9
  import { forwardRef, useId, useState } from "react";
10
10
  import { ArrowIcon, SearchIcon } from "@malilion/block-ui-icons";
11
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
11
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
12
12
  //#region src/components/crafting/RecipeBook/RecipeBook.tsx
13
13
  /**
14
14
  * Recipe book: search, category filters and a "craftable only" switch over a
@@ -104,7 +104,7 @@ var RecipeBook = forwardRef(function RecipeBook({ recipes, categories, value, de
104
104
  id: titleId,
105
105
  className: RecipeBook_module_default.title,
106
106
  children: selected ? selected.name : "Select a recipe"
107
- }), selected ? /* @__PURE__ */ jsxs(Fragment, { children: [
107
+ }), selected ? /* @__PURE__ */ jsxs(Fragment$1, { children: [
108
108
  /* @__PURE__ */ jsxs("div", {
109
109
  className: RecipeBook_module_default.preview,
110
110
  children: [
@@ -1 +1 @@
1
- {"version":3,"file":"RecipeBook.js","names":["styles","_Fragment"],"sources":["../../../../src/components/crafting/RecipeBook/RecipeBook.tsx"],"sourcesContent":["import { ArrowIcon, SearchIcon } from \"@malilion/block-ui-icons\";\nimport { forwardRef, useId, useState } from \"react\";\nimport { useControllableState } from \"../../../hooks/useControllableState\";\nimport { cx } from \"../../../utils/cx\";\nimport { BlockButton } from \"../../actions/BlockButton/BlockButton\";\nimport { BlockInput } from \"../../forms/BlockInput/BlockInput\";\nimport { BlockToggle } from \"../../forms/BlockToggle/BlockToggle\";\nimport { InventorySlot } from \"../../inventory/InventorySlot/InventorySlot\";\nimport styles from \"./RecipeBook.module.css\";\nimport type { RecipeBookProps } from \"./RecipeBook.types\";\nimport { ALL, deriveCategories, filterRecipes } from \"./RecipeBook.utils\";\n\n/**\n * Recipe book: search, category filters and a \"craftable only\" switch over a\n * grid of recipes, with the selected recipe's 3 × 3 pattern and result.\n */\nexport const RecipeBook = forwardRef<HTMLDivElement, RecipeBookProps>(function RecipeBook(\n {\n recipes,\n categories,\n value,\n defaultValue,\n onValueChange,\n onCraft,\n defaultCraftableOnly = false,\n label = \"Recipe book\",\n className,\n ...rest\n },\n ref,\n) {\n const titleId = useId();\n const [query, setQuery] = useState(\"\");\n const [category, setCategory] = useState(ALL);\n const [craftableOnly, setCraftableOnly] = useState(defaultCraftableOnly);\n const [selectedId, setSelectedId] = useControllableState({\n value,\n defaultValue: defaultValue ?? \"\",\n onChange: onValueChange,\n });\n\n const filters = [{ id: ALL, label: \"All\" }, ...(categories ?? deriveCategories(recipes))];\n const visible = filterRecipes(recipes, { query, category, craftableOnly });\n const selected = recipes.find((recipe) => recipe.id === selectedId);\n const pattern = Array.from({ length: 9 }, (_, i) => selected?.ingredients?.[i] ?? null);\n\n return (\n <div ref={ref} role=\"group\" aria-label={label} className={cx(styles.book, className)} {...rest}>\n <div className={styles.browser}>\n <BlockInput\n type=\"search\"\n label=\"Search recipes\"\n value={query}\n onChange={(event) => setQuery(event.target.value)}\n startIcon={<SearchIcon size={16} />}\n />\n <div className={styles.filters}>\n <div role=\"group\" aria-label=\"Categories\" className={styles.categories}>\n {filters.map((filter) => (\n <button\n key={filter.id}\n type=\"button\"\n aria-pressed={category === filter.id}\n className={styles.category}\n onClick={() => setCategory(filter.id)}\n >\n {\"icon\" in filter && filter.icon ? (\n <span className={styles.categoryIcon} aria-hidden=\"true\">\n {filter.icon}\n </span>\n ) : null}\n {filter.label}\n </button>\n ))}\n </div>\n <BlockToggle\n size=\"sm\"\n label=\"Craftable only\"\n checked={craftableOnly}\n onCheckedChange={setCraftableOnly}\n />\n </div>\n <p className={styles.count} role=\"status\">\n {visible.length === 1 ? \"1 recipe\" : `${visible.length} recipes`}\n </p>\n {visible.length > 0 ? (\n <ul className={styles.grid} aria-label=\"Recipes\">\n {visible.map((recipe) => (\n <li key={recipe.id}>\n <InventorySlot\n size=\"md\"\n selected={recipe.id === selectedId}\n label={\n recipe.craftable === false\n ? `${recipe.name} (missing ingredients)`\n : recipe.name\n }\n data-craftable={recipe.craftable === false ? \"false\" : undefined}\n className={styles.recipe}\n onClick={() => setSelectedId(recipe.id)}\n >\n {recipe.result}\n </InventorySlot>\n </li>\n ))}\n </ul>\n ) : (\n <p className={styles.empty}>No recipes match.</p>\n )}\n </div>\n <section className={styles.detail} aria-labelledby={titleId}>\n <h3 id={titleId} className={styles.title}>\n {selected ? selected.name : \"Select a recipe\"}\n </h3>\n {selected ? (\n <>\n <div className={styles.preview}>\n <div role=\"group\" aria-label=\"Pattern\" className={styles.pattern}>\n {pattern.map((cell, index) => (\n <InventorySlot key={index} size=\"sm\">\n {cell}\n </InventorySlot>\n ))}\n </div>\n <span className={styles.arrow} aria-hidden=\"true\">\n <ArrowIcon size={24} />\n </span>\n <InventorySlot size=\"lg\" label={`Makes ${selected.name}`}>\n {selected.result}\n </InventorySlot>\n </div>\n {selected.craftable === false ? (\n <p className={styles.missing}>Missing ingredients</p>\n ) : null}\n {onCraft ? (\n <BlockButton\n variant=\"grass\"\n disabled={selected.craftable === false}\n onClick={() => onCraft(selected.id)}\n >\n Craft\n </BlockButton>\n ) : null}\n </>\n ) : null}\n </section>\n </div>\n );\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"RecipeBook.js","names":["styles","_Fragment"],"sources":["../../../../src/components/crafting/RecipeBook/RecipeBook.tsx"],"sourcesContent":["import { ArrowIcon, SearchIcon } from \"@malilion/block-ui-icons\";\nimport { forwardRef, useId, useState } from \"react\";\nimport { useControllableState } from \"../../../hooks/useControllableState\";\nimport { cx } from \"../../../utils/cx\";\nimport { BlockButton } from \"../../actions/BlockButton/BlockButton\";\nimport { BlockInput } from \"../../forms/BlockInput/BlockInput\";\nimport { BlockToggle } from \"../../forms/BlockToggle/BlockToggle\";\nimport { InventorySlot } from \"../../inventory/InventorySlot/InventorySlot\";\nimport styles from \"./RecipeBook.module.css\";\nimport type { RecipeBookProps } from \"./RecipeBook.types\";\nimport { ALL, deriveCategories, filterRecipes } from \"./RecipeBook.utils\";\n\n/**\n * Recipe book: search, category filters and a \"craftable only\" switch over a\n * grid of recipes, with the selected recipe's 3 × 3 pattern and result.\n */\nexport const RecipeBook = forwardRef<HTMLDivElement, RecipeBookProps>(function RecipeBook(\n {\n recipes,\n categories,\n value,\n defaultValue,\n onValueChange,\n onCraft,\n defaultCraftableOnly = false,\n label = \"Recipe book\",\n className,\n ...rest\n },\n ref,\n) {\n const titleId = useId();\n const [query, setQuery] = useState(\"\");\n const [category, setCategory] = useState(ALL);\n const [craftableOnly, setCraftableOnly] = useState(defaultCraftableOnly);\n const [selectedId, setSelectedId] = useControllableState({\n value,\n defaultValue: defaultValue ?? \"\",\n onChange: onValueChange,\n });\n\n const filters = [{ id: ALL, label: \"All\" }, ...(categories ?? deriveCategories(recipes))];\n const visible = filterRecipes(recipes, { query, category, craftableOnly });\n const selected = recipes.find((recipe) => recipe.id === selectedId);\n const pattern = Array.from({ length: 9 }, (_, i) => selected?.ingredients?.[i] ?? null);\n\n return (\n <div ref={ref} role=\"group\" aria-label={label} className={cx(styles.book, className)} {...rest}>\n <div className={styles.browser}>\n <BlockInput\n type=\"search\"\n label=\"Search recipes\"\n value={query}\n onChange={(event) => setQuery(event.target.value)}\n startIcon={<SearchIcon size={16} />}\n />\n <div className={styles.filters}>\n <div role=\"group\" aria-label=\"Categories\" className={styles.categories}>\n {filters.map((filter) => (\n <button\n key={filter.id}\n type=\"button\"\n aria-pressed={category === filter.id}\n className={styles.category}\n onClick={() => setCategory(filter.id)}\n >\n {\"icon\" in filter && filter.icon ? (\n <span className={styles.categoryIcon} aria-hidden=\"true\">\n {filter.icon}\n </span>\n ) : null}\n {filter.label}\n </button>\n ))}\n </div>\n <BlockToggle\n size=\"sm\"\n label=\"Craftable only\"\n checked={craftableOnly}\n onCheckedChange={setCraftableOnly}\n />\n </div>\n <p className={styles.count} role=\"status\">\n {visible.length === 1 ? \"1 recipe\" : `${visible.length} recipes`}\n </p>\n {visible.length > 0 ? (\n <ul className={styles.grid} aria-label=\"Recipes\">\n {visible.map((recipe) => (\n <li key={recipe.id}>\n <InventorySlot\n size=\"md\"\n selected={recipe.id === selectedId}\n label={\n recipe.craftable === false\n ? `${recipe.name} (missing ingredients)`\n : recipe.name\n }\n data-craftable={recipe.craftable === false ? \"false\" : undefined}\n className={styles.recipe}\n onClick={() => setSelectedId(recipe.id)}\n >\n {recipe.result}\n </InventorySlot>\n </li>\n ))}\n </ul>\n ) : (\n <p className={styles.empty}>No recipes match.</p>\n )}\n </div>\n <section className={styles.detail} aria-labelledby={titleId}>\n <h3 id={titleId} className={styles.title}>\n {selected ? selected.name : \"Select a recipe\"}\n </h3>\n {selected ? (\n <>\n <div className={styles.preview}>\n <div role=\"group\" aria-label=\"Pattern\" className={styles.pattern}>\n {pattern.map((cell, index) => (\n <InventorySlot key={index} size=\"sm\">\n {cell}\n </InventorySlot>\n ))}\n </div>\n <span className={styles.arrow} aria-hidden=\"true\">\n <ArrowIcon size={24} />\n </span>\n <InventorySlot size=\"lg\" label={`Makes ${selected.name}`}>\n {selected.result}\n </InventorySlot>\n </div>\n {selected.craftable === false ? (\n <p className={styles.missing}>Missing ingredients</p>\n ) : null}\n {onCraft ? (\n <BlockButton\n variant=\"grass\"\n disabled={selected.craftable === false}\n onClick={() => onCraft(selected.id)}\n >\n Craft\n </BlockButton>\n ) : null}\n </>\n ) : null}\n </section>\n </div>\n );\n});\n"],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { KbdProps } from "./Kbd.types";
2
+ /** Pixel keycap for keyboard hints ("Press E to open your inventory"). Combinations use nested `<kbd>`s. */
3
+ export declare const Kbd: import("react").ForwardRefExoticComponent<KbdProps & import("react").RefAttributes<HTMLElement>>;
4
+ //# sourceMappingURL=Kbd.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Kbd.d.ts","sourceRoot":"","sources":["../../../../src/components/display/Kbd/Kbd.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE5C,4GAA4G;AAC5G,eAAO,MAAM,GAAG,kGAyBd,CAAC"}
@@ -0,0 +1,33 @@
1
+ import { cx } from "../../../utils/cx.js";
2
+ import Kbd_module_default from "./Kbd.module.js";
3
+ import { Fragment, forwardRef } from "react";
4
+ import { jsx, jsxs } from "react/jsx-runtime";
5
+ //#region src/components/display/Kbd/Kbd.tsx
6
+ /** Pixel keycap for keyboard hints ("Press E to open your inventory"). Combinations use nested `<kbd>`s. */
7
+ var Kbd = forwardRef(function Kbd({ keys, children, size = "md", className, ...rest }, ref) {
8
+ if (keys && keys.length > 1) return /* @__PURE__ */ jsx("kbd", {
9
+ ref,
10
+ "data-size": size,
11
+ className: cx(Kbd_module_default.combo, className),
12
+ ...rest,
13
+ children: keys.map((key, index) => /* @__PURE__ */ jsxs(Fragment, { children: [index > 0 ? /* @__PURE__ */ jsx("span", {
14
+ className: Kbd_module_default.plus,
15
+ "aria-hidden": "true",
16
+ children: "+"
17
+ }) : null, /* @__PURE__ */ jsx("kbd", {
18
+ className: Kbd_module_default.key,
19
+ children: key
20
+ })] }, `${key}-${index}`))
21
+ });
22
+ return /* @__PURE__ */ jsx("kbd", {
23
+ ref,
24
+ "data-size": size,
25
+ className: cx(Kbd_module_default.key, className),
26
+ ...rest,
27
+ children: keys?.[0] ?? children
28
+ });
29
+ });
30
+ //#endregion
31
+ export { Kbd };
32
+
33
+ //# sourceMappingURL=Kbd.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Kbd.js","names":["styles"],"sources":["../../../../src/components/display/Kbd/Kbd.tsx"],"sourcesContent":["import { Fragment, forwardRef } from \"react\";\nimport { cx } from \"../../../utils/cx\";\nimport styles from \"./Kbd.module.css\";\nimport type { KbdProps } from \"./Kbd.types\";\n\n/** Pixel keycap for keyboard hints (\"Press E to open your inventory\"). Combinations use nested `<kbd>`s. */\nexport const Kbd = forwardRef<HTMLElement, KbdProps>(function Kbd(\n { keys, children, size = \"md\", className, ...rest },\n ref,\n) {\n if (keys && keys.length > 1) {\n return (\n <kbd ref={ref} data-size={size} className={cx(styles.combo, className)} {...rest}>\n {keys.map((key, index) => (\n <Fragment key={`${key}-${index}`}>\n {index > 0 ? (\n <span className={styles.plus} aria-hidden=\"true\">\n +\n </span>\n ) : null}\n <kbd className={styles.key}>{key}</kbd>\n </Fragment>\n ))}\n </kbd>\n );\n }\n return (\n <kbd ref={ref} data-size={size} className={cx(styles.key, className)} {...rest}>\n {keys?.[0] ?? children}\n </kbd>\n );\n});\n"],"mappings":";;;;;;AAMA,IAAa,MAAM,WAAkC,SAAS,IAC5D,EAAE,MAAM,UAAU,OAAO,MAAM,WAAW,GAAG,QAC7C,KACA;CACA,IAAI,QAAQ,KAAK,SAAS,GACxB,OACE,oBAAC,OAAD;EAAU;EAAK,aAAW;EAAM,WAAW,GAAGA,mBAAO,OAAO,SAAS;EAAG,GAAI;EACzE,UAAA,KAAK,KAAK,KAAK,UACd,qBAAC,UAAD,EAAA,UAAA,CACG,QAAQ,IACP,oBAAC,QAAD;GAAM,WAAWA,mBAAO;GAAM,eAAY;GAAO,UAAA;EAE3C,CAAA,IACJ,MACJ,oBAAC,OAAD;GAAK,WAAWA,mBAAO;GAAM,UAAA;EAAS,CAAA,CAC9B,EAAA,GAPK,GAAG,IAAI,GAAG,OAOf,CACX;CACE,CAAA;CAGT,OACE,oBAAC,OAAD;EAAU;EAAK,aAAW;EAAM,WAAW,GAAGA,mBAAO,KAAK,SAAS;EAAG,GAAI;EACvE,UAAA,OAAO,MAAM;CACX,CAAA;AAET,CAAC"}
@@ -0,0 +1,13 @@
1
+ //#region src/components/display/Kbd/Kbd.module.css
2
+ var key = "block-key-FLsly";
3
+ var combo = "block-combo-xCqO-";
4
+ var plus = "block-plus-OaCLf";
5
+ var Kbd_module_default = {
6
+ key,
7
+ combo,
8
+ plus
9
+ };
10
+ //#endregion
11
+ export { combo, Kbd_module_default as default, key, plus };
12
+
13
+ //# sourceMappingURL=Kbd.module.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Kbd.module.js","names":[],"sources":["../../../../src/components/display/Kbd/Kbd.module.css"],"sourcesContent":[".key {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 28px;\n min-height: 28px;\n padding: 0 var(--block-space-1);\n font-family: var(--block-font-display);\n font-size: var(--block-font-size-xs);\n font-weight: var(--block-font-weight-bold);\n line-height: 1;\n text-transform: uppercase;\n white-space: nowrap;\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:\n inset 2px 2px 0 var(--block-shadow-bevel-light),\n inset -2px -4px 0 var(--block-shadow-bevel-dark);\n padding-bottom: 2px;\n vertical-align: middle;\n}\n\n.key[data-size=\"sm\"],\n[data-size=\"sm\"] > .key {\n min-width: 22px;\n min-height: 22px;\n font-size: 0.625rem;\n}\n\n.combo {\n display: inline-flex;\n align-items: center;\n gap: var(--block-space-1);\n font: inherit;\n vertical-align: middle;\n}\n\n.plus {\n font-family: var(--block-font-display);\n font-size: var(--block-font-size-xs);\n color: var(--block-text-muted);\n}\n"],"mappings":""}
@@ -0,0 +1,9 @@
1
+ import type { HTMLAttributes, ReactNode } from "react";
2
+ export interface KbdProps extends HTMLAttributes<HTMLElement> {
3
+ /** A single key (`children`) or a combination, e.g. `["Ctrl", "Shift", "C"]`. */
4
+ keys?: string[];
5
+ children?: ReactNode;
6
+ /** @default "md" */
7
+ size?: "sm" | "md";
8
+ }
9
+ //# sourceMappingURL=Kbd.types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Kbd.types.d.ts","sourceRoot":"","sources":["../../../../src/components/display/Kbd/Kbd.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvD,MAAM,WAAW,QAAS,SAAQ,cAAc,CAAC,WAAW,CAAC;IAC3D,iFAAiF;IACjF,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,oBAAoB;IACpB,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;CACpB"}
@@ -0,0 +1,3 @@
1
+ export { Kbd } from "./Kbd";
2
+ export type { KbdProps } from "./Kbd.types";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/display/Kbd/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AAC5B,YAAY,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,8 @@
1
+ import type { PopoverProps } from "./Popover.types";
2
+ /**
3
+ * Click-to-open floating panel for interactive content (a non-modal dialog).
4
+ * Focus moves into it when it opens; Escape, an outside click or tabbing out
5
+ * closes it, and Escape returns focus to the trigger.
6
+ */
7
+ export declare const Popover: import("react").ForwardRefExoticComponent<PopoverProps & import("react").RefAttributes<HTMLSpanElement>>;
8
+ //# sourceMappingURL=Popover.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Popover.d.ts","sourceRoot":"","sources":["../../../../src/components/display/Popover/Popover.tsx"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAEpD;;;;GAIG;AACH,eAAO,MAAM,OAAO,0GAgHlB,CAAC"}
@@ -0,0 +1,101 @@
1
+ import { cx } from "../../../utils/cx.js";
2
+ import { usePortalContainer } from "../../../provider/context.js";
3
+ import { useAnchoredPosition } from "../../../hooks/useAnchoredPosition.js";
4
+ import { mergeRefs } from "../../../utils/refs.js";
5
+ import { useControllableState } from "../../../hooks/useControllableState.js";
6
+ import { getFocusable } from "../../../utils/dom.js";
7
+ import Popover_module_default from "./Popover.module.js";
8
+ import { cloneElement, forwardRef, useEffect, useId, useRef } from "react";
9
+ import { createPortal } from "react-dom";
10
+ import { jsx, jsxs } from "react/jsx-runtime";
11
+ //#region src/components/display/Popover/Popover.tsx
12
+ /**
13
+ * Click-to-open floating panel for interactive content (a non-modal dialog).
14
+ * Focus moves into it when it opens; Escape, an outside click or tabbing out
15
+ * closes it, and Escape returns focus to the trigger.
16
+ */
17
+ var Popover = forwardRef(function Popover({ content, children, title, label, placement = "bottom", align = "center", open, defaultOpen = false, onOpenChange, className, ...rest }, ref) {
18
+ const baseId = useId();
19
+ const panelId = `${baseId}-panel`;
20
+ const titleId = `${baseId}-title`;
21
+ const container = usePortalContainer();
22
+ const anchorRef = useRef(null);
23
+ const panelRef = useRef(null);
24
+ const [isOpen, setOpen] = useControllableState({
25
+ value: open,
26
+ defaultValue: defaultOpen,
27
+ onChange: onOpenChange
28
+ });
29
+ const position = useAnchoredPosition(anchorRef, panelRef, isOpen, {
30
+ side: placement,
31
+ align
32
+ });
33
+ const isInside = (node) => Boolean(node && (anchorRef.current?.contains(node) || panelRef.current?.contains(node)));
34
+ const triggerElement = () => anchorRef.current?.querySelector("button, [role='button'], a[href]");
35
+ const close = (returnFocus) => {
36
+ setOpen(false);
37
+ if (returnFocus) triggerElement()?.focus();
38
+ };
39
+ useEffect(() => {
40
+ if (!isOpen || !position) return;
41
+ const panel = panelRef.current;
42
+ if (panel && !panel.contains(document.activeElement)) (getFocusable(panel)[0] ?? panel).focus();
43
+ }, [isOpen, position !== null]);
44
+ useEffect(() => {
45
+ if (!isOpen) return;
46
+ const onPointerDown = (event) => {
47
+ if (!isInside(event.target)) close(false);
48
+ };
49
+ document.addEventListener("pointerdown", onPointerDown);
50
+ return () => document.removeEventListener("pointerdown", onPointerDown);
51
+ }, [isOpen]);
52
+ const onPanelKeyDown = (event) => {
53
+ if (event.key === "Escape") {
54
+ event.stopPropagation();
55
+ close(true);
56
+ }
57
+ };
58
+ const onPanelBlur = (event) => {
59
+ const next = event.relatedTarget;
60
+ if (next && !isInside(next)) close(false);
61
+ };
62
+ const trigger = cloneElement(children, {
63
+ "aria-haspopup": "dialog",
64
+ "aria-expanded": isOpen,
65
+ "aria-controls": isOpen ? panelId : void 0,
66
+ onClick: (event) => {
67
+ children.props.onClick?.(event);
68
+ setOpen(!isOpen);
69
+ }
70
+ });
71
+ return /* @__PURE__ */ jsxs("span", {
72
+ ref: mergeRefs(ref, anchorRef),
73
+ className: cx(Popover_module_default.anchor, className),
74
+ ...rest,
75
+ children: [trigger, container && isOpen ? createPortal(/* @__PURE__ */ jsxs("div", {
76
+ ref: panelRef,
77
+ id: panelId,
78
+ role: "dialog",
79
+ "aria-labelledby": title ? titleId : void 0,
80
+ "aria-label": title ? void 0 : label,
81
+ tabIndex: -1,
82
+ "data-placement": position?.side ?? placement,
83
+ className: Popover_module_default.panel,
84
+ style: position?.style,
85
+ onKeyDown: onPanelKeyDown,
86
+ onBlur: onPanelBlur,
87
+ children: [title ? /* @__PURE__ */ jsx("p", {
88
+ id: titleId,
89
+ className: Popover_module_default.title,
90
+ children: title
91
+ }) : null, /* @__PURE__ */ jsx("div", {
92
+ className: Popover_module_default.body,
93
+ children: content
94
+ })]
95
+ }), container) : null]
96
+ });
97
+ });
98
+ //#endregion
99
+ export { Popover };
100
+
101
+ //# sourceMappingURL=Popover.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Popover.js","names":["styles"],"sources":["../../../../src/components/display/Popover/Popover.tsx"],"sourcesContent":["import {\n cloneElement,\n forwardRef,\n useEffect,\n useId,\n useRef,\n type FocusEvent,\n type KeyboardEvent,\n} from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { useAnchoredPosition } from \"../../../hooks/useAnchoredPosition\";\nimport { useControllableState } from \"../../../hooks/useControllableState\";\nimport { usePortalContainer } from \"../../../provider/context\";\nimport { cx } from \"../../../utils/cx\";\nimport { getFocusable } from \"../../../utils/dom\";\nimport { mergeRefs } from \"../../../utils/refs\";\nimport styles from \"./Popover.module.css\";\nimport type { PopoverProps } from \"./Popover.types\";\n\n/**\n * Click-to-open floating panel for interactive content (a non-modal dialog).\n * Focus moves into it when it opens; Escape, an outside click or tabbing out\n * closes it, and Escape returns focus to the trigger.\n */\nexport const Popover = forwardRef<HTMLSpanElement, PopoverProps>(function Popover(\n {\n content,\n children,\n title,\n label,\n placement = \"bottom\",\n align = \"center\",\n open,\n defaultOpen = false,\n onOpenChange,\n className,\n ...rest\n },\n ref,\n) {\n const baseId = useId();\n const panelId = `${baseId}-panel`;\n const titleId = `${baseId}-title`;\n const container = usePortalContainer();\n const anchorRef = useRef<HTMLSpanElement>(null);\n const panelRef = useRef<HTMLDivElement>(null);\n const [isOpen, setOpen] = useControllableState({\n value: open,\n defaultValue: defaultOpen,\n onChange: onOpenChange,\n });\n const position = useAnchoredPosition(anchorRef, panelRef, isOpen, { side: placement, align });\n\n const isInside = (node: Node | null) =>\n Boolean(node && (anchorRef.current?.contains(node) || panelRef.current?.contains(node)));\n\n const triggerElement = () =>\n anchorRef.current?.querySelector<HTMLElement>(\"button, [role='button'], a[href]\");\n\n const close = (returnFocus: boolean) => {\n setOpen(false);\n if (returnFocus) triggerElement()?.focus();\n };\n\n // Move focus into the panel once it is open and positioned.\n useEffect(() => {\n if (!isOpen || !position) return;\n const panel = panelRef.current;\n if (panel && !panel.contains(document.activeElement)) (getFocusable(panel)[0] ?? panel).focus();\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [isOpen, position !== null]);\n\n useEffect(() => {\n if (!isOpen) return;\n const onPointerDown = (event: PointerEvent) => {\n if (!isInside(event.target as Node)) close(false);\n };\n document.addEventListener(\"pointerdown\", onPointerDown);\n return () => document.removeEventListener(\"pointerdown\", onPointerDown);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [isOpen]);\n\n const onPanelKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {\n if (event.key === \"Escape\") {\n event.stopPropagation();\n close(true);\n }\n };\n\n const onPanelBlur = (event: FocusEvent<HTMLDivElement>) => {\n const next = event.relatedTarget as Node | null;\n if (next && !isInside(next)) close(false);\n };\n\n const trigger = cloneElement(children, {\n \"aria-haspopup\": \"dialog\",\n \"aria-expanded\": isOpen,\n \"aria-controls\": isOpen ? panelId : undefined,\n onClick: (event: never) => {\n children.props.onClick?.(event);\n setOpen(!isOpen);\n },\n });\n\n return (\n <span ref={mergeRefs(ref, anchorRef)} className={cx(styles.anchor, className)} {...rest}>\n {trigger}\n {container && isOpen\n ? createPortal(\n // A dialog closes on Escape and when focus leaves it (WAI-ARIA dialog pattern).\n // eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions\n <div\n ref={panelRef}\n id={panelId}\n role=\"dialog\"\n aria-labelledby={title ? titleId : undefined}\n aria-label={title ? undefined : label}\n tabIndex={-1}\n data-placement={position?.side ?? placement}\n className={styles.panel}\n style={position?.style}\n onKeyDown={onPanelKeyDown}\n onBlur={onPanelBlur}\n >\n {title ? (\n <p id={titleId} className={styles.title}>\n {title}\n </p>\n ) : null}\n <div className={styles.body}>{content}</div>\n </div>,\n container,\n )\n : null}\n </span>\n );\n});\n"],"mappings":";;;;;;;;;;;;;;;;AAwBA,IAAa,UAAU,WAA0C,SAAS,QACxE,EACE,SACA,UACA,OACA,OACA,YAAY,UACZ,QAAQ,UACR,MACA,cAAc,OACd,cACA,WACA,GAAG,QAEL,KACA;CACA,MAAM,SAAS,MAAM;CACrB,MAAM,UAAU,GAAG,OAAO;CAC1B,MAAM,UAAU,GAAG,OAAO;CAC1B,MAAM,YAAY,mBAAmB;CACrC,MAAM,YAAY,OAAwB,IAAI;CAC9C,MAAM,WAAW,OAAuB,IAAI;CAC5C,MAAM,CAAC,QAAQ,WAAW,qBAAqB;EAC7C,OAAO;EACP,cAAc;EACd,UAAU;CACZ,CAAC;CACD,MAAM,WAAW,oBAAoB,WAAW,UAAU,QAAQ;EAAE,MAAM;EAAW;CAAM,CAAC;CAE5F,MAAM,YAAY,SAChB,QAAQ,SAAS,UAAU,SAAS,SAAS,IAAI,KAAK,SAAS,SAAS,SAAS,IAAI,EAAE;CAEzF,MAAM,uBACJ,UAAU,SAAS,cAA2B,kCAAkC;CAElF,MAAM,SAAS,gBAAyB;EACtC,QAAQ,KAAK;EACb,IAAI,aAAa,eAAe,CAAC,EAAE,MAAM;CAC3C;CAGA,gBAAgB;EACd,IAAI,CAAC,UAAU,CAAC,UAAU;EAC1B,MAAM,QAAQ,SAAS;EACvB,IAAI,SAAS,CAAC,MAAM,SAAS,SAAS,aAAa,GAAG,CAAC,aAAa,KAAK,CAAC,CAAC,MAAM,MAAA,CAAO,MAAM;CAEhG,GAAG,CAAC,QAAQ,aAAa,IAAI,CAAC;CAE9B,gBAAgB;EACd,IAAI,CAAC,QAAQ;EACb,MAAM,iBAAiB,UAAwB;GAC7C,IAAI,CAAC,SAAS,MAAM,MAAc,GAAG,MAAM,KAAK;EAClD;EACA,SAAS,iBAAiB,eAAe,aAAa;EACtD,aAAa,SAAS,oBAAoB,eAAe,aAAa;CAExE,GAAG,CAAC,MAAM,CAAC;CAEX,MAAM,kBAAkB,UAAyC;EAC/D,IAAI,MAAM,QAAQ,UAAU;GAC1B,MAAM,gBAAgB;GACtB,MAAM,IAAI;EACZ;CACF;CAEA,MAAM,eAAe,UAAsC;EACzD,MAAM,OAAO,MAAM;EACnB,IAAI,QAAQ,CAAC,SAAS,IAAI,GAAG,MAAM,KAAK;CAC1C;CAEA,MAAM,UAAU,aAAa,UAAU;EACrC,iBAAiB;EACjB,iBAAiB;EACjB,iBAAiB,SAAS,UAAU,KAAA;EACpC,UAAU,UAAiB;GACzB,SAAS,MAAM,UAAU,KAAK;GAC9B,QAAQ,CAAC,MAAM;EACjB;CACF,CAAC;CAED,OACE,qBAAC,QAAD;EAAM,KAAK,UAAU,KAAK,SAAS;EAAG,WAAW,GAAGA,uBAAO,QAAQ,SAAS;EAAG,GAAI;EAAnF,UAAA,CACG,SACA,aAAa,SACV,aAGE,qBAAC,OAAD;GACE,KAAK;GACL,IAAI;GACJ,MAAK;GACL,mBAAiB,QAAQ,UAAU,KAAA;GACnC,cAAY,QAAQ,KAAA,IAAY;GAChC,UAAU;GACV,kBAAgB,UAAU,QAAQ;GAClC,WAAWA,uBAAO;GAClB,OAAO,UAAU;GACjB,WAAW;GACX,QAAQ;GAXV,UAAA,CAaG,QACC,oBAAC,KAAD;IAAG,IAAI;IAAS,WAAWA,uBAAO;IAC/B,UAAA;GACA,CAAA,IACD,MACJ,oBAAC,OAAD;IAAK,WAAWA,uBAAO;IAAO,UAAA;GAAa,CAAA,CACxC;EACL,CAAA,GAAA,SACF,IACA,IACA;;AAEV,CAAC"}
@@ -0,0 +1,15 @@
1
+ //#region src/components/display/Popover/Popover.module.css
2
+ var anchor = "block-anchor-Hc2xy";
3
+ var panel = "block-panel-hIYiA";
4
+ var title = "block-title-0MX0a";
5
+ var body = "block-body-K6cco";
6
+ var Popover_module_default = {
7
+ anchor,
8
+ panel,
9
+ title,
10
+ body
11
+ };
12
+ //#endregion
13
+ export { anchor, body, Popover_module_default as default, panel, title };
14
+
15
+ //# sourceMappingURL=Popover.module.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Popover.module.js","names":[],"sources":["../../../../src/components/display/Popover/Popover.module.css"],"sourcesContent":[".anchor {\n display: inline-flex;\n vertical-align: middle;\n}\n\n.panel {\n position: fixed;\n top: var(--block-float-y, 0);\n left: var(--block-float-x, 0);\n z-index: var(--block-z-dropdown);\n display: flex;\n flex-direction: column;\n gap: var(--block-space-2);\n width: max-content;\n min-width: 200px;\n max-width: min(360px, calc(100vw - var(--block-space-4)));\n max-height: calc(100vh - var(--block-space-4));\n padding: var(--block-space-3);\n overflow-y: auto;\n color: var(--block-text);\n background-color: var(--block-surface);\n border: var(--block-border-width) solid var(--block-border);\n box-shadow: var(--block-shadow-bevel), var(--block-shadow-drop);\n}\n\n.panel:focus-visible {\n outline: 3px solid var(--block-focus);\n outline-offset: 2px;\n}\n\n.title {\n margin: 0;\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-transform: uppercase;\n text-shadow: var(--block-shadow-text);\n}\n\n.body {\n font-size: var(--block-font-size-sm);\n}\n"],"mappings":""}
@@ -0,0 +1,26 @@
1
+ import type { HTMLAttributes, ReactElement, ReactNode } from "react";
2
+ export interface PopoverProps extends Omit<HTMLAttributes<HTMLSpanElement>, "content" | "title"> {
3
+ /** Panel content — may contain buttons, links and form fields. */
4
+ content: ReactNode;
5
+ /** A single button that toggles the popover. It receives `aria-expanded` / `aria-controls`. */
6
+ children: ReactElement<{
7
+ "aria-expanded"?: boolean;
8
+ "aria-controls"?: string;
9
+ "aria-haspopup"?: "dialog";
10
+ onClick?: (event: never) => void;
11
+ }>;
12
+ /** Heading of the panel and its accessible name. */
13
+ title?: ReactNode;
14
+ /** Accessible name when there is no `title`. */
15
+ label?: string;
16
+ /** Preferred side; flips when there is no room. @default "bottom" */
17
+ placement?: "top" | "bottom" | "left" | "right";
18
+ /** Alignment along the trigger edge. @default "center" */
19
+ align?: "start" | "center" | "end";
20
+ /** Controlled open state. */
21
+ open?: boolean;
22
+ /** Initial open state when uncontrolled. @default false */
23
+ defaultOpen?: boolean;
24
+ onOpenChange?: (open: boolean) => void;
25
+ }
26
+ //# sourceMappingURL=Popover.types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Popover.types.d.ts","sourceRoot":"","sources":["../../../../src/components/display/Popover/Popover.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAErE,MAAM,WAAW,YAAa,SAAQ,IAAI,CAAC,cAAc,CAAC,eAAe,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC;IAC9F,kEAAkE;IAClE,OAAO,EAAE,SAAS,CAAC;IACnB,+FAA+F;IAC/F,QAAQ,EAAE,YAAY,CAAC;QACrB,eAAe,CAAC,EAAE,OAAO,CAAC;QAC1B,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,eAAe,CAAC,EAAE,QAAQ,CAAC;QAC3B,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;KAClC,CAAC,CAAC;IACH,oDAAoD;IACpD,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,gDAAgD;IAChD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qEAAqE;IACrE,SAAS,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;IAChD,0DAA0D;IAC1D,KAAK,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAC;IACnC,6BAA6B;IAC7B,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,2DAA2D;IAC3D,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;CACxC"}
@@ -0,0 +1,3 @@
1
+ export { Popover } from "./Popover";
2
+ export type { PopoverProps } from "./Popover.types";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/display/Popover/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,YAAY,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC"}
@@ -3,7 +3,7 @@ import { BlockButton } from "../../actions/BlockButton/BlockButton.js";
3
3
  import SkillTree_module_default from "./SkillTree.module.js";
4
4
  import { gridSize, skillState } from "./SkillTree.utils.js";
5
5
  import { forwardRef, useId, useState } from "react";
6
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
6
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
7
7
  //#region src/components/display/SkillTree/SkillTree.tsx
8
8
  var STATE_TEXT = {
9
9
  locked: "locked",
@@ -99,7 +99,7 @@ var SkillTree = forwardRef(function SkillTree({ skills, unlocked, points, onUnlo
99
99
  className: SkillTree_module_default.detail,
100
100
  "aria-live": "polite",
101
101
  "aria-label": "Skill details",
102
- children: selected ? /* @__PURE__ */ jsxs(Fragment, { children: [
102
+ children: selected ? /* @__PURE__ */ jsxs(Fragment$1, { children: [
103
103
  /* @__PURE__ */ jsxs("p", {
104
104
  className: SkillTree_module_default.name,
105
105
  children: [
@@ -1 +1 @@
1
- {"version":3,"file":"SkillTree.js","names":["styles","_Fragment"],"sources":["../../../../src/components/display/SkillTree/SkillTree.tsx"],"sourcesContent":["import { forwardRef, useId, useState, type CSSProperties } from \"react\";\nimport { cx } from \"../../../utils/cx\";\nimport { BlockButton } from \"../../actions/BlockButton/BlockButton\";\nimport styles from \"./SkillTree.module.css\";\nimport type { SkillTreeProps } from \"./SkillTree.types\";\nimport { gridSize, skillState } from \"./SkillTree.utils\";\n\nconst STATE_TEXT = { locked: \"locked\", available: \"available\", unlocked: \"unlocked\" } as const;\n\n/**\n * Skill tree: nodes on a grid joined to their prerequisites. Unlocked skills\n * glow, available ones can be bought with skill points, locked ones show what\n * they need. Selecting a node shows its details and Unlock button.\n */\nexport const SkillTree = forwardRef<HTMLDivElement, SkillTreeProps>(function SkillTree(\n { skills, unlocked, points, onUnlock, defaultValue, label = \"Skill tree\", className, ...rest },\n ref,\n) {\n const detailId = useId();\n const [selectedId, setSelectedId] = useState(defaultValue ?? skills[0]?.id ?? \"\");\n const unlockedSet = new Set(unlocked);\n const byId = new Map(skills.map((skill) => [skill.id, skill]));\n const { rows, columns } = gridSize(skills);\n const selected = byId.get(selectedId);\n const selectedState = selected ? skillState(selected, unlockedSet) : undefined;\n const selectedCost = selected?.cost ?? 1;\n const affordable = points === undefined || points >= selectedCost;\n const missing =\n selected && selectedState === \"locked\"\n ? (selected.requires ?? [])\n .filter((id) => !unlockedSet.has(id))\n .map((id) => byId.get(id)?.label ?? id)\n : [];\n\n // Connector centres in grid units (0.5 = middle of the first cell).\n const edges = skills.flatMap((skill) =>\n (skill.requires ?? []).flatMap((id) => {\n const from = byId.get(id);\n return from\n ? [{ key: `${id}-${skill.id}`, from, to: skill, lit: unlockedSet.has(skill.id) }]\n : [];\n }),\n );\n\n return (\n <div ref={ref} role=\"group\" aria-label={label} className={cx(styles.tree, className)} {...rest}>\n {points !== undefined ? (\n <p className={styles.points}>\n Skill points: <strong>{points}</strong>\n </p>\n ) : null}\n <div\n className={styles.board}\n style={{ \"--block-skill-rows\": rows, \"--block-skill-columns\": columns } as CSSProperties}\n >\n <svg\n className={styles.lines}\n viewBox={`0 0 ${columns} ${rows}`}\n preserveAspectRatio=\"none\"\n aria-hidden=\"true\"\n >\n {edges.map(({ key, from, to, lit }) => (\n <line\n key={key}\n x1={from.column - 0.5}\n y1={from.row - 0.5}\n x2={to.column - 0.5}\n y2={to.row - 0.5}\n data-lit={lit || undefined}\n vectorEffect=\"non-scaling-stroke\"\n />\n ))}\n </svg>\n <ul className={styles.nodes} aria-label=\"Skills\">\n {skills.map((skill) => {\n const state = skillState(skill, unlockedSet);\n return (\n <li\n key={skill.id}\n className={styles.cell}\n style={\n {\n \"--block-skill-row\": skill.row,\n \"--block-skill-col\": skill.column,\n } as CSSProperties\n }\n >\n <button\n type=\"button\"\n className={styles.node}\n data-state={state}\n aria-pressed={skill.id === selectedId}\n aria-controls={detailId}\n aria-label={`${skill.label}, ${STATE_TEXT[state]}`}\n onClick={() => setSelectedId(skill.id)}\n >\n <span className={styles.icon} aria-hidden=\"true\">\n {skill.icon}\n </span>\n </button>\n </li>\n );\n })}\n </ul>\n </div>\n <section\n id={detailId}\n className={styles.detail}\n aria-live=\"polite\"\n aria-label=\"Skill details\"\n >\n {selected ? (\n <>\n <p className={styles.name}>\n {selected.label}{\" \"}\n <span className={styles.state} data-state={selectedState}>\n {selectedState}\n </span>\n </p>\n {selected.description ? (\n <p className={styles.description}>{selected.description}</p>\n ) : null}\n {missing.length > 0 ? (\n <p className={styles.requires}>Requires: {missing.join(\", \")}</p>\n ) : null}\n {selectedState === \"available\" && onUnlock ? (\n <BlockButton\n variant=\"emerald\"\n size=\"sm\"\n disabled={!affordable}\n onClick={() => onUnlock(selected.id)}\n >\n {affordable\n ? `Unlock (${selectedCost} ${selectedCost === 1 ? \"point\" : \"points\"})`\n : `Needs ${selectedCost} ${selectedCost === 1 ? \"point\" : \"points\"}`}\n </BlockButton>\n ) : null}\n </>\n ) : null}\n </section>\n </div>\n );\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"SkillTree.js","names":["styles","_Fragment"],"sources":["../../../../src/components/display/SkillTree/SkillTree.tsx"],"sourcesContent":["import { forwardRef, useId, useState, type CSSProperties } from \"react\";\nimport { cx } from \"../../../utils/cx\";\nimport { BlockButton } from \"../../actions/BlockButton/BlockButton\";\nimport styles from \"./SkillTree.module.css\";\nimport type { SkillTreeProps } from \"./SkillTree.types\";\nimport { gridSize, skillState } from \"./SkillTree.utils\";\n\nconst STATE_TEXT = { locked: \"locked\", available: \"available\", unlocked: \"unlocked\" } as const;\n\n/**\n * Skill tree: nodes on a grid joined to their prerequisites. Unlocked skills\n * glow, available ones can be bought with skill points, locked ones show what\n * they need. Selecting a node shows its details and Unlock button.\n */\nexport const SkillTree = forwardRef<HTMLDivElement, SkillTreeProps>(function SkillTree(\n { skills, unlocked, points, onUnlock, defaultValue, label = \"Skill tree\", className, ...rest },\n ref,\n) {\n const detailId = useId();\n const [selectedId, setSelectedId] = useState(defaultValue ?? skills[0]?.id ?? \"\");\n const unlockedSet = new Set(unlocked);\n const byId = new Map(skills.map((skill) => [skill.id, skill]));\n const { rows, columns } = gridSize(skills);\n const selected = byId.get(selectedId);\n const selectedState = selected ? skillState(selected, unlockedSet) : undefined;\n const selectedCost = selected?.cost ?? 1;\n const affordable = points === undefined || points >= selectedCost;\n const missing =\n selected && selectedState === \"locked\"\n ? (selected.requires ?? [])\n .filter((id) => !unlockedSet.has(id))\n .map((id) => byId.get(id)?.label ?? id)\n : [];\n\n // Connector centres in grid units (0.5 = middle of the first cell).\n const edges = skills.flatMap((skill) =>\n (skill.requires ?? []).flatMap((id) => {\n const from = byId.get(id);\n return from\n ? [{ key: `${id}-${skill.id}`, from, to: skill, lit: unlockedSet.has(skill.id) }]\n : [];\n }),\n );\n\n return (\n <div ref={ref} role=\"group\" aria-label={label} className={cx(styles.tree, className)} {...rest}>\n {points !== undefined ? (\n <p className={styles.points}>\n Skill points: <strong>{points}</strong>\n </p>\n ) : null}\n <div\n className={styles.board}\n style={{ \"--block-skill-rows\": rows, \"--block-skill-columns\": columns } as CSSProperties}\n >\n <svg\n className={styles.lines}\n viewBox={`0 0 ${columns} ${rows}`}\n preserveAspectRatio=\"none\"\n aria-hidden=\"true\"\n >\n {edges.map(({ key, from, to, lit }) => (\n <line\n key={key}\n x1={from.column - 0.5}\n y1={from.row - 0.5}\n x2={to.column - 0.5}\n y2={to.row - 0.5}\n data-lit={lit || undefined}\n vectorEffect=\"non-scaling-stroke\"\n />\n ))}\n </svg>\n <ul className={styles.nodes} aria-label=\"Skills\">\n {skills.map((skill) => {\n const state = skillState(skill, unlockedSet);\n return (\n <li\n key={skill.id}\n className={styles.cell}\n style={\n {\n \"--block-skill-row\": skill.row,\n \"--block-skill-col\": skill.column,\n } as CSSProperties\n }\n >\n <button\n type=\"button\"\n className={styles.node}\n data-state={state}\n aria-pressed={skill.id === selectedId}\n aria-controls={detailId}\n aria-label={`${skill.label}, ${STATE_TEXT[state]}`}\n onClick={() => setSelectedId(skill.id)}\n >\n <span className={styles.icon} aria-hidden=\"true\">\n {skill.icon}\n </span>\n </button>\n </li>\n );\n })}\n </ul>\n </div>\n <section\n id={detailId}\n className={styles.detail}\n aria-live=\"polite\"\n aria-label=\"Skill details\"\n >\n {selected ? (\n <>\n <p className={styles.name}>\n {selected.label}{\" \"}\n <span className={styles.state} data-state={selectedState}>\n {selectedState}\n </span>\n </p>\n {selected.description ? (\n <p className={styles.description}>{selected.description}</p>\n ) : null}\n {missing.length > 0 ? (\n <p className={styles.requires}>Requires: {missing.join(\", \")}</p>\n ) : null}\n {selectedState === \"available\" && onUnlock ? (\n <BlockButton\n variant=\"emerald\"\n size=\"sm\"\n disabled={!affordable}\n onClick={() => onUnlock(selected.id)}\n >\n {affordable\n ? `Unlock (${selectedCost} ${selectedCost === 1 ? \"point\" : \"points\"})`\n : `Needs ${selectedCost} ${selectedCost === 1 ? \"point\" : \"points\"}`}\n </BlockButton>\n ) : null}\n </>\n ) : null}\n </section>\n </div>\n );\n});\n"],"mappings":"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"}
@@ -3,4 +3,6 @@ export * from "./BlockTooltip";
3
3
  export * from "./BlockTable";
4
4
  export * from "./SkillTree";
5
5
  export * from "./Avatar";
6
+ export * from "./Popover";
7
+ export * from "./Kbd";
6
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/display/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,UAAU,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/display/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,OAAO,CAAC"}
@@ -1,7 +1,7 @@
1
1
  import { BlockButton } from "../../actions/BlockButton/BlockButton.js";
2
2
  import { BlockModal } from "../Modal/BlockModal.js";
3
3
  import { useRef } from "react";
4
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
4
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
5
5
  //#region src/components/feedback/ConfirmDialog/ConfirmDialog.tsx
6
6
  /**
7
7
  * Confirmation built on `BlockModal` with `role="alertdialog"`. For the
@@ -25,7 +25,7 @@ function ConfirmDialog({ open, title, description, confirmText = "Confirm", canc
25
25
  initialFocusRef: danger ? cancelRef : confirmRef,
26
26
  className,
27
27
  "data-variant": variant,
28
- footer: /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(BlockButton, {
28
+ footer: /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(BlockButton, {
29
29
  ref: cancelRef,
30
30
  variant: "stone",
31
31
  onClick: onCancel,
@@ -1 +1 @@
1
- {"version":3,"file":"ConfirmDialog.js","names":["_Fragment"],"sources":["../../../../src/components/feedback/ConfirmDialog/ConfirmDialog.tsx"],"sourcesContent":["import { useRef } from \"react\";\nimport { BlockButton } from \"../../actions/BlockButton/BlockButton\";\nimport { BlockModal } from \"../Modal/BlockModal\";\nimport type { ConfirmDialogProps } from \"./ConfirmDialog.types\";\n\n/**\n * Confirmation built on `BlockModal` with `role=\"alertdialog\"`. For the\n * `danger` variant the safe action (Cancel) receives focus first.\n */\nexport function ConfirmDialog({\n open,\n title,\n description,\n confirmText = \"Confirm\",\n cancelText = \"Cancel\",\n variant = \"default\",\n onConfirm,\n onCancel,\n loading = false,\n icon,\n children,\n className,\n}: ConfirmDialogProps) {\n const cancelRef = useRef<HTMLButtonElement>(null);\n const confirmRef = useRef<HTMLButtonElement>(null);\n const danger = variant === \"danger\";\n\n return (\n <BlockModal\n open={open}\n onClose={onCancel}\n title={title}\n description={description}\n role=\"alertdialog\"\n size=\"sm\"\n icon={icon}\n hideCloseButton\n closeOnOverlayClick={!loading}\n closeOnEscape={!loading}\n initialFocusRef={danger ? cancelRef : confirmRef}\n className={className}\n data-variant={variant}\n footer={\n <>\n <BlockButton ref={cancelRef} variant=\"stone\" onClick={onCancel} disabled={loading}>\n {cancelText}\n </BlockButton>\n <BlockButton\n ref={confirmRef}\n variant={danger ? \"redstone\" : \"grass\"}\n onClick={onConfirm}\n loading={loading}\n >\n {confirmText}\n </BlockButton>\n </>\n }\n >\n {children}\n </BlockModal>\n );\n}\n"],"mappings":";;;;;;;;;AASA,SAAgB,cAAc,EAC5B,MACA,OACA,aACA,cAAc,WACd,aAAa,UACb,UAAU,WACV,WACA,UACA,UAAU,OACV,MACA,UACA,aACqB;CACrB,MAAM,YAAY,OAA0B,IAAI;CAChD,MAAM,aAAa,OAA0B,IAAI;CACjD,MAAM,SAAS,YAAY;CAE3B,OACE,oBAAC,YAAD;EACQ;EACN,SAAS;EACF;EACM;EACb,MAAK;EACL,MAAK;EACC;EACN,iBAAA;EACA,qBAAqB,CAAC;EACtB,eAAe,CAAC;EAChB,iBAAiB,SAAS,YAAY;EAC3B;EACX,gBAAc;EACd,QACE,qBAAAA,UAAA,EAAA,UAAA,CACE,oBAAC,aAAD;GAAa,KAAK;GAAW,SAAQ;GAAQ,SAAS;GAAU,UAAU;GACvE,UAAA;EACU,CAAA,GACb,oBAAC,aAAD;GACE,KAAK;GACL,SAAS,SAAS,aAAa;GAC/B,SAAS;GACA;GAER,UAAA;EACU,CAAA,CACb,EAAA,CAAA;EAGH;CACS,CAAA;AAEhB"}
1
+ {"version":3,"file":"ConfirmDialog.js","names":["_Fragment"],"sources":["../../../../src/components/feedback/ConfirmDialog/ConfirmDialog.tsx"],"sourcesContent":["import { useRef } from \"react\";\nimport { BlockButton } from \"../../actions/BlockButton/BlockButton\";\nimport { BlockModal } from \"../Modal/BlockModal\";\nimport type { ConfirmDialogProps } from \"./ConfirmDialog.types\";\n\n/**\n * Confirmation built on `BlockModal` with `role=\"alertdialog\"`. For the\n * `danger` variant the safe action (Cancel) receives focus first.\n */\nexport function ConfirmDialog({\n open,\n title,\n description,\n confirmText = \"Confirm\",\n cancelText = \"Cancel\",\n variant = \"default\",\n onConfirm,\n onCancel,\n loading = false,\n icon,\n children,\n className,\n}: ConfirmDialogProps) {\n const cancelRef = useRef<HTMLButtonElement>(null);\n const confirmRef = useRef<HTMLButtonElement>(null);\n const danger = variant === \"danger\";\n\n return (\n <BlockModal\n open={open}\n onClose={onCancel}\n title={title}\n description={description}\n role=\"alertdialog\"\n size=\"sm\"\n icon={icon}\n hideCloseButton\n closeOnOverlayClick={!loading}\n closeOnEscape={!loading}\n initialFocusRef={danger ? cancelRef : confirmRef}\n className={className}\n data-variant={variant}\n footer={\n <>\n <BlockButton ref={cancelRef} variant=\"stone\" onClick={onCancel} disabled={loading}>\n {cancelText}\n </BlockButton>\n <BlockButton\n ref={confirmRef}\n variant={danger ? \"redstone\" : \"grass\"}\n onClick={onConfirm}\n loading={loading}\n >\n {confirmText}\n </BlockButton>\n </>\n }\n >\n {children}\n </BlockModal>\n );\n}\n"],"mappings":";;;;;;;;;AASA,SAAgB,cAAc,EAC5B,MACA,OACA,aACA,cAAc,WACd,aAAa,UACb,UAAU,WACV,WACA,UACA,UAAU,OACV,MACA,UACA,aACqB;CACrB,MAAM,YAAY,OAA0B,IAAI;CAChD,MAAM,aAAa,OAA0B,IAAI;CACjD,MAAM,SAAS,YAAY;CAE3B,OACE,oBAAC,YAAD;EACQ;EACN,SAAS;EACF;EACM;EACb,MAAK;EACL,MAAK;EACC;EACN,iBAAA;EACA,qBAAqB,CAAC;EACtB,eAAe,CAAC;EAChB,iBAAiB,SAAS,YAAY;EAC3B;EACX,gBAAc;EACd,QACE,qBAAAA,YAAA,EAAA,UAAA,CACE,oBAAC,aAAD;GAAa,KAAK;GAAW,SAAQ;GAAQ,SAAS;GAAU,UAAU;GACvE,UAAA;EACU,CAAA,GACb,oBAAC,aAAD;GACE,KAAK;GACL,SAAS,SAAS,aAAa;GAC/B,SAAS;GACA;GAER,UAAA;EACU,CAAA,CACb,EAAA,CAAA;EAGH;CACS,CAAA;AAEhB"}
@@ -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":"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"}
@@ -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