@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
@@ -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,7 @@
1
+ import type { AvatarProps } from "./Avatar.types";
2
+ /**
3
+ * Square block avatar: a pixelated face image, or initials on a material
4
+ * colour picked from the name, with an optional presence dot.
5
+ */
6
+ export declare const Avatar: import("react").ForwardRefExoticComponent<AvatarProps & import("react").RefAttributes<HTMLSpanElement>>;
7
+ //# sourceMappingURL=Avatar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Avatar.d.ts","sourceRoot":"","sources":["../../../../src/components/display/Avatar/Avatar.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAKlD;;;GAGG;AACH,eAAO,MAAM,MAAM,yGAiCjB,CAAC"}
@@ -0,0 +1,53 @@
1
+ import { cx } from "../../../utils/cx.js";
2
+ import Avatar_module_default from "./Avatar.module.js";
3
+ import { avatarMaterial, initials } from "./Avatar.utils.js";
4
+ import { forwardRef, useState } from "react";
5
+ import { jsx, jsxs } from "react/jsx-runtime";
6
+ //#region src/components/display/Avatar/Avatar.tsx
7
+ var STATUS_TEXT = {
8
+ online: "online",
9
+ away: "away",
10
+ busy: "busy",
11
+ offline: "offline"
12
+ };
13
+ /**
14
+ * Square block avatar: a pixelated face image, or initials on a material
15
+ * colour picked from the name, with an optional presence dot.
16
+ */
17
+ var Avatar = forwardRef(function Avatar({ name, src, icon, size = "md", status, decorative = false, className, ...rest }, ref) {
18
+ const [failedSrc, setFailedSrc] = useState(void 0);
19
+ const showImage = src !== void 0 && src !== failedSrc;
20
+ const accessibleName = status ? `${name} (${STATUS_TEXT[status]})` : name;
21
+ return /* @__PURE__ */ jsxs("span", {
22
+ ref,
23
+ role: decorative ? void 0 : "img",
24
+ "aria-label": decorative ? void 0 : accessibleName,
25
+ "aria-hidden": decorative || void 0,
26
+ "data-size": size,
27
+ "data-material": avatarMaterial(name),
28
+ className: cx(Avatar_module_default.avatar, className),
29
+ ...rest,
30
+ children: [showImage ? /* @__PURE__ */ jsx("img", {
31
+ src,
32
+ alt: "",
33
+ className: Avatar_module_default.image,
34
+ onError: () => setFailedSrc(src)
35
+ }) : icon ? /* @__PURE__ */ jsx("span", {
36
+ className: Avatar_module_default.icon,
37
+ "aria-hidden": "true",
38
+ children: icon
39
+ }) : /* @__PURE__ */ jsx("span", {
40
+ className: Avatar_module_default.initials,
41
+ "aria-hidden": "true",
42
+ children: initials(name)
43
+ }), status ? /* @__PURE__ */ jsx("span", {
44
+ className: Avatar_module_default.status,
45
+ "data-status": status,
46
+ "aria-hidden": "true"
47
+ }) : null]
48
+ });
49
+ });
50
+ //#endregion
51
+ export { Avatar };
52
+
53
+ //# sourceMappingURL=Avatar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Avatar.js","names":["styles"],"sources":["../../../../src/components/display/Avatar/Avatar.tsx"],"sourcesContent":["import { forwardRef, useState } from \"react\";\nimport { cx } from \"../../../utils/cx\";\nimport styles from \"./Avatar.module.css\";\nimport type { AvatarProps } from \"./Avatar.types\";\nimport { avatarMaterial, initials } from \"./Avatar.utils\";\n\nconst STATUS_TEXT = { online: \"online\", away: \"away\", busy: \"busy\", offline: \"offline\" } as const;\n\n/**\n * Square block avatar: a pixelated face image, or initials on a material\n * colour picked from the name, with an optional presence dot.\n */\nexport const Avatar = forwardRef<HTMLSpanElement, AvatarProps>(function Avatar(\n { name, src, icon, size = \"md\", status, decorative = false, className, ...rest },\n ref,\n) {\n const [failedSrc, setFailedSrc] = useState<string | undefined>(undefined);\n const showImage = src !== undefined && src !== failedSrc;\n const accessibleName = status ? `${name} (${STATUS_TEXT[status]})` : name;\n\n return (\n <span\n ref={ref}\n role={decorative ? undefined : \"img\"}\n aria-label={decorative ? undefined : accessibleName}\n aria-hidden={decorative || undefined}\n data-size={size}\n data-material={avatarMaterial(name)}\n className={cx(styles.avatar, className)}\n {...rest}\n >\n {showImage ? (\n <img src={src} alt=\"\" className={styles.image} onError={() => setFailedSrc(src)} />\n ) : icon ? (\n <span className={styles.icon} aria-hidden=\"true\">\n {icon}\n </span>\n ) : (\n <span className={styles.initials} aria-hidden=\"true\">\n {initials(name)}\n </span>\n )}\n {status ? <span className={styles.status} data-status={status} aria-hidden=\"true\" /> : null}\n </span>\n );\n});\n"],"mappings":";;;;;;AAMA,IAAM,cAAc;CAAE,QAAQ;CAAU,MAAM;CAAQ,MAAM;CAAQ,SAAS;AAAU;;;;;AAMvF,IAAa,SAAS,WAAyC,SAAS,OACtE,EAAE,MAAM,KAAK,MAAM,OAAO,MAAM,QAAQ,aAAa,OAAO,WAAW,GAAG,QAC1E,KACA;CACA,MAAM,CAAC,WAAW,gBAAgB,SAA6B,KAAA,CAAS;CACxE,MAAM,YAAY,QAAQ,KAAA,KAAa,QAAQ;CAC/C,MAAM,iBAAiB,SAAS,GAAG,KAAK,IAAI,YAAY,QAAQ,KAAK;CAErE,OACE,qBAAC,QAAD;EACO;EACL,MAAM,aAAa,KAAA,IAAY;EAC/B,cAAY,aAAa,KAAA,IAAY;EACrC,eAAa,cAAc,KAAA;EAC3B,aAAW;EACX,iBAAe,eAAe,IAAI;EAClC,WAAW,GAAGA,sBAAO,QAAQ,SAAS;EACtC,GAAI;EARN,UAAA,CAUG,YACC,oBAAC,OAAD;GAAU;GAAK,KAAI;GAAG,WAAWA,sBAAO;GAAO,eAAe,aAAa,GAAG;EAAI,CAAA,IAChF,OACF,oBAAC,QAAD;GAAM,WAAWA,sBAAO;GAAM,eAAY;GACvC,UAAA;EACG,CAAA,IAEN,oBAAC,QAAD;GAAM,WAAWA,sBAAO;GAAU,eAAY;GAC3C,UAAA,SAAS,IAAI;EACV,CAAA,GAEP,SAAS,oBAAC,QAAD;GAAM,WAAWA,sBAAO;GAAQ,eAAa;GAAQ,eAAY;EAAQ,CAAA,IAAI,IACnF;;AAEV,CAAC"}
@@ -0,0 +1,17 @@
1
+ //#region src/components/display/Avatar/Avatar.module.css
2
+ var avatar = "block-avatar-9s1vM";
3
+ var image = "block-image-RZELF";
4
+ var icon = "block-icon-mq-FE";
5
+ var initials = "block-initials-siD-X";
6
+ var status = "block-status-CalIQ";
7
+ var Avatar_module_default = {
8
+ avatar,
9
+ image,
10
+ icon,
11
+ initials,
12
+ status
13
+ };
14
+ //#endregion
15
+ export { avatar, Avatar_module_default as default, icon, image, initials, status };
16
+
17
+ //# sourceMappingURL=Avatar.module.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Avatar.module.js","names":[],"sources":["../../../../src/components/display/Avatar/Avatar.module.css"],"sourcesContent":[".avatar {\n --avatar-size: 32px;\n position: relative;\n display: inline-flex;\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n width: var(--avatar-size);\n height: var(--avatar-size);\n color: var(--block-mat-on);\n background-color: var(--block-mat);\n border: var(--block-border-width-thin) solid var(--block-black);\n box-shadow:\n inset 2px 2px 0 var(--block-mat-light),\n inset -2px -2px 0 var(--block-mat-dark);\n vertical-align: middle;\n}\n\n.avatar[data-size=\"sm\"] {\n --avatar-size: 24px;\n}\n\n.avatar[data-size=\"lg\"] {\n --avatar-size: 48px;\n}\n\n.avatar[data-size=\"xl\"] {\n --avatar-size: 64px;\n}\n\n.image {\n width: 100%;\n height: 100%;\n object-fit: cover;\n image-rendering: pixelated;\n}\n\n.icon {\n display: inline-flex;\n width: 70%;\n height: 70%;\n}\n\n.icon > * {\n width: 100%;\n height: 100%;\n}\n\n.initials {\n font-family: var(--block-font-display);\n font-size: calc(var(--avatar-size) * 0.4);\n font-weight: var(--block-font-weight-bold);\n line-height: 1;\n text-shadow: var(--block-mat-text-shadow, none);\n}\n\n/* Presence dot, a quarter of the avatar, in the bottom-right corner. */\n.status {\n position: absolute;\n right: -3px;\n bottom: -3px;\n width: max(8px, calc(var(--avatar-size) * 0.3));\n height: max(8px, calc(var(--avatar-size) * 0.3));\n border: 2px solid var(--block-black);\n}\n\n.status[data-status=\"online\"] {\n background-color: var(--block-emerald-light);\n}\n\n.status[data-status=\"away\"] {\n background-color: var(--block-gold-light);\n}\n\n.status[data-status=\"busy\"] {\n background-color: var(--block-redstone-light);\n}\n\n.status[data-status=\"offline\"] {\n background-color: var(--block-stone);\n}\n"],"mappings":""}
@@ -0,0 +1,18 @@
1
+ import type { HTMLAttributes, ReactNode } from "react";
2
+ export declare const avatarStatuses: readonly ["online", "away", "busy", "offline"];
3
+ export type AvatarStatus = (typeof avatarStatuses)[number];
4
+ export interface AvatarProps extends HTMLAttributes<HTMLSpanElement> {
5
+ /** Player name: the accessible name and the source of the initials. */
6
+ name: string;
7
+ /** Skin / face image URL (rendered pixelated). Falls back to initials if it fails. */
8
+ src?: string;
9
+ /** Icon instead of initials when there is no image. */
10
+ icon?: ReactNode;
11
+ /** 24 / 32 / 48 / 64 px. @default "md" */
12
+ size?: "sm" | "md" | "lg" | "xl";
13
+ /** Presence dot in the corner. */
14
+ status?: AvatarStatus;
15
+ /** Hide from assistive technology when the name is already shown next to it. */
16
+ decorative?: boolean;
17
+ }
18
+ //# sourceMappingURL=Avatar.types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Avatar.types.d.ts","sourceRoot":"","sources":["../../../../src/components/display/Avatar/Avatar.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvD,eAAO,MAAM,cAAc,gDAAiD,CAAC;AAE7E,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC;AAE3D,MAAM,WAAW,WAAY,SAAQ,cAAc,CAAC,eAAe,CAAC;IAClE,uEAAuE;IACvE,IAAI,EAAE,MAAM,CAAC;IACb,sFAAsF;IACtF,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,uDAAuD;IACvD,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,0CAA0C;IAC1C,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IACjC,kCAAkC;IAClC,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,gFAAgF;IAChF,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB"}
@@ -0,0 +1,11 @@
1
+ //#region src/components/display/Avatar/Avatar.types.ts
2
+ var avatarStatuses = [
3
+ "online",
4
+ "away",
5
+ "busy",
6
+ "offline"
7
+ ];
8
+ //#endregion
9
+ export { avatarStatuses };
10
+
11
+ //# sourceMappingURL=Avatar.types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Avatar.types.js","names":[],"sources":["../../../../src/components/display/Avatar/Avatar.types.ts"],"sourcesContent":["import type { HTMLAttributes, ReactNode } from \"react\";\n\nexport const avatarStatuses = [\"online\", \"away\", \"busy\", \"offline\"] as const;\n\nexport type AvatarStatus = (typeof avatarStatuses)[number];\n\nexport interface AvatarProps extends HTMLAttributes<HTMLSpanElement> {\n /** Player name: the accessible name and the source of the initials. */\n name: string;\n /** Skin / face image URL (rendered pixelated). Falls back to initials if it fails. */\n src?: string;\n /** Icon instead of initials when there is no image. */\n icon?: ReactNode;\n /** 24 / 32 / 48 / 64 px. @default \"md\" */\n size?: \"sm\" | \"md\" | \"lg\" | \"xl\";\n /** Presence dot in the corner. */\n status?: AvatarStatus;\n /** Hide from assistive technology when the name is already shown next to it. */\n decorative?: boolean;\n}\n"],"mappings":";AAEA,IAAa,iBAAiB;CAAC;CAAU;CAAQ;CAAQ;AAAS"}
@@ -0,0 +1,7 @@
1
+ /** Up to two letters: first letters of the first two words, or the first two characters. */
2
+ export declare function initials(name: string): string;
3
+ declare const MATERIALS: readonly ["grass", "diamond", "gold", "redstone", "amethyst", "emerald", "wood", "water"];
4
+ /** Stable block material for a name, so a player always gets the same colour. */
5
+ export declare function avatarMaterial(name: string): (typeof MATERIALS)[number];
6
+ export {};
7
+ //# sourceMappingURL=Avatar.utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Avatar.utils.d.ts","sourceRoot":"","sources":["../../../../src/components/display/Avatar/Avatar.utils.ts"],"names":[],"mappings":"AAAA,4FAA4F;AAC5F,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAS7C;AAED,QAAA,MAAM,SAAS,2FASL,CAAC;AAEX,iFAAiF;AACjF,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,CAAC,OAAO,SAAS,CAAC,CAAC,MAAM,CAAC,CAIvE"}
@@ -0,0 +1,28 @@
1
+ //#region src/components/display/Avatar/Avatar.utils.ts
2
+ /** Up to two letters: first letters of the first two words, or the first two characters. */
3
+ function initials(name) {
4
+ const words = name.replace(/[_\-.]+/g, " ").trim().split(/\s+/).filter(Boolean);
5
+ if (words.length === 0) return "?";
6
+ if (words.length === 1) return (words[0] ?? "").slice(0, 2).toUpperCase();
7
+ return `${words[0]?.[0] ?? ""}${words[1]?.[0] ?? ""}`.toUpperCase();
8
+ }
9
+ var MATERIALS = [
10
+ "grass",
11
+ "diamond",
12
+ "gold",
13
+ "redstone",
14
+ "amethyst",
15
+ "emerald",
16
+ "wood",
17
+ "water"
18
+ ];
19
+ /** Stable block material for a name, so a player always gets the same colour. */
20
+ function avatarMaterial(name) {
21
+ let hash = 0;
22
+ for (const char of name) hash = hash * 31 + char.charCodeAt(0) >>> 0;
23
+ return MATERIALS[hash % MATERIALS.length] ?? "grass";
24
+ }
25
+ //#endregion
26
+ export { avatarMaterial, initials };
27
+
28
+ //# sourceMappingURL=Avatar.utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Avatar.utils.js","names":[],"sources":["../../../../src/components/display/Avatar/Avatar.utils.ts"],"sourcesContent":["/** Up to two letters: first letters of the first two words, or the first two characters. */\nexport function initials(name: string): string {\n const words = name\n .replace(/[_\\-.]+/g, \" \")\n .trim()\n .split(/\\s+/)\n .filter(Boolean);\n if (words.length === 0) return \"?\";\n if (words.length === 1) return (words[0] ?? \"\").slice(0, 2).toUpperCase();\n return `${words[0]?.[0] ?? \"\"}${words[1]?.[0] ?? \"\"}`.toUpperCase();\n}\n\nconst MATERIALS = [\n \"grass\",\n \"diamond\",\n \"gold\",\n \"redstone\",\n \"amethyst\",\n \"emerald\",\n \"wood\",\n \"water\",\n] as const;\n\n/** Stable block material for a name, so a player always gets the same colour. */\nexport function avatarMaterial(name: string): (typeof MATERIALS)[number] {\n let hash = 0;\n for (const char of name) hash = (hash * 31 + char.charCodeAt(0)) >>> 0;\n return MATERIALS[hash % MATERIALS.length] ?? \"grass\";\n}\n"],"mappings":";;AACA,SAAgB,SAAS,MAAsB;CAC7C,MAAM,QAAQ,KACX,QAAQ,YAAY,GAAG,CAAC,CACxB,KAAK,CAAC,CACN,MAAM,KAAK,CAAC,CACZ,OAAO,OAAO;CACjB,IAAI,MAAM,WAAW,GAAG,OAAO;CAC/B,IAAI,MAAM,WAAW,GAAG,QAAQ,MAAM,MAAM,GAAA,CAAI,MAAM,GAAG,CAAC,CAAC,CAAC,YAAY;CACxE,OAAO,GAAG,MAAM,EAAE,GAAG,MAAM,KAAK,MAAM,EAAE,GAAG,MAAM,KAAK,YAAY;AACpE;AAEA,IAAM,YAAY;CAChB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;;AAGA,SAAgB,eAAe,MAA0C;CACvE,IAAI,OAAO;CACX,KAAK,MAAM,QAAQ,MAAM,OAAQ,OAAO,KAAK,KAAK,WAAW,CAAC,MAAO;CACrE,OAAO,UAAU,OAAO,UAAU,WAAW;AAC/C"}
@@ -0,0 +1,5 @@
1
+ export { Avatar } from "./Avatar";
2
+ export { avatarStatuses } from "./Avatar.types";
3
+ export { initials } from "./Avatar.utils";
4
+ export type { AvatarProps, AvatarStatus } from "./Avatar.types";
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/display/Avatar/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC"}
@@ -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: [