@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.
- package/dist/components/actions/BlockMenu/BlockMenu.d.ts.map +1 -1
- package/dist/components/actions/BlockMenu/BlockMenu.js +17 -117
- package/dist/components/actions/BlockMenu/BlockMenu.js.map +1 -1
- package/dist/components/actions/BlockMenu/BlockMenu.utils.d.ts +2 -0
- package/dist/components/actions/BlockMenu/BlockMenu.utils.d.ts.map +1 -1
- package/dist/components/actions/BlockMenu/BlockMenu.utils.js +5 -1
- package/dist/components/actions/BlockMenu/BlockMenu.utils.js.map +1 -1
- package/dist/components/actions/BlockMenu/MenuList.d.ts +28 -0
- package/dist/components/actions/BlockMenu/MenuList.d.ts.map +1 -0
- package/dist/components/actions/BlockMenu/MenuList.js +134 -0
- package/dist/components/actions/BlockMenu/MenuList.js.map +1 -0
- package/dist/components/actions/ContextMenu/ContextMenu.d.ts +8 -0
- package/dist/components/actions/ContextMenu/ContextMenu.d.ts.map +1 -0
- package/dist/components/actions/ContextMenu/ContextMenu.js +100 -0
- package/dist/components/actions/ContextMenu/ContextMenu.js.map +1 -0
- package/dist/components/actions/ContextMenu/ContextMenu.module.js +7 -0
- package/dist/components/actions/ContextMenu/ContextMenu.module.js.map +1 -0
- package/dist/components/actions/ContextMenu/ContextMenu.types.d.ts +15 -0
- package/dist/components/actions/ContextMenu/ContextMenu.types.d.ts.map +1 -0
- package/dist/components/actions/ContextMenu/index.d.ts +3 -0
- package/dist/components/actions/ContextMenu/index.d.ts.map +1 -0
- package/dist/components/actions/index.d.ts +1 -0
- package/dist/components/actions/index.d.ts.map +1 -1
- package/dist/components/crafting/RecipeBook/RecipeBook.js +2 -2
- package/dist/components/crafting/RecipeBook/RecipeBook.js.map +1 -1
- package/dist/components/display/Avatar/Avatar.d.ts +7 -0
- package/dist/components/display/Avatar/Avatar.d.ts.map +1 -0
- package/dist/components/display/Avatar/Avatar.js +53 -0
- package/dist/components/display/Avatar/Avatar.js.map +1 -0
- package/dist/components/display/Avatar/Avatar.module.js +17 -0
- package/dist/components/display/Avatar/Avatar.module.js.map +1 -0
- package/dist/components/display/Avatar/Avatar.types.d.ts +18 -0
- package/dist/components/display/Avatar/Avatar.types.d.ts.map +1 -0
- package/dist/components/display/Avatar/Avatar.types.js +11 -0
- package/dist/components/display/Avatar/Avatar.types.js.map +1 -0
- package/dist/components/display/Avatar/Avatar.utils.d.ts +7 -0
- package/dist/components/display/Avatar/Avatar.utils.d.ts.map +1 -0
- package/dist/components/display/Avatar/Avatar.utils.js +28 -0
- package/dist/components/display/Avatar/Avatar.utils.js.map +1 -0
- package/dist/components/display/Avatar/index.d.ts +5 -0
- package/dist/components/display/Avatar/index.d.ts.map +1 -0
- package/dist/components/display/Kbd/Kbd.d.ts +4 -0
- package/dist/components/display/Kbd/Kbd.d.ts.map +1 -0
- package/dist/components/display/Kbd/Kbd.js +33 -0
- package/dist/components/display/Kbd/Kbd.js.map +1 -0
- package/dist/components/display/Kbd/Kbd.module.js +13 -0
- package/dist/components/display/Kbd/Kbd.module.js.map +1 -0
- package/dist/components/display/Kbd/Kbd.types.d.ts +9 -0
- package/dist/components/display/Kbd/Kbd.types.d.ts.map +1 -0
- package/dist/components/display/Kbd/index.d.ts +3 -0
- package/dist/components/display/Kbd/index.d.ts.map +1 -0
- package/dist/components/display/Popover/Popover.d.ts +8 -0
- package/dist/components/display/Popover/Popover.d.ts.map +1 -0
- package/dist/components/display/Popover/Popover.js +101 -0
- package/dist/components/display/Popover/Popover.js.map +1 -0
- package/dist/components/display/Popover/Popover.module.js +15 -0
- package/dist/components/display/Popover/Popover.module.js.map +1 -0
- package/dist/components/display/Popover/Popover.types.d.ts +26 -0
- package/dist/components/display/Popover/Popover.types.d.ts.map +1 -0
- package/dist/components/display/Popover/index.d.ts +3 -0
- package/dist/components/display/Popover/index.d.ts.map +1 -0
- package/dist/components/display/SkillTree/SkillTree.js +2 -2
- package/dist/components/display/SkillTree/SkillTree.js.map +1 -1
- package/dist/components/display/index.d.ts +3 -0
- package/dist/components/display/index.d.ts.map +1 -1
- package/dist/components/feedback/ConfirmDialog/ConfirmDialog.js +2 -2
- package/dist/components/feedback/ConfirmDialog/ConfirmDialog.js.map +1 -1
- package/dist/components/feedback/Drawer/Drawer.d.ts +7 -0
- package/dist/components/feedback/Drawer/Drawer.d.ts.map +1 -0
- package/dist/components/feedback/Drawer/Drawer.js +78 -0
- package/dist/components/feedback/Drawer/Drawer.js.map +1 -0
- package/dist/components/feedback/Drawer/Drawer.module.js +25 -0
- package/dist/components/feedback/Drawer/Drawer.module.js.map +1 -0
- package/dist/components/feedback/Drawer/Drawer.types.d.ts +21 -0
- package/dist/components/feedback/Drawer/Drawer.types.d.ts.map +1 -0
- package/dist/components/feedback/Drawer/index.d.ts +3 -0
- package/dist/components/feedback/Drawer/index.d.ts.map +1 -0
- package/dist/components/feedback/EmptyState/EmptyState.d.ts +4 -0
- package/dist/components/feedback/EmptyState/EmptyState.d.ts.map +1 -0
- package/dist/components/feedback/EmptyState/EmptyState.js +35 -0
- package/dist/components/feedback/EmptyState/EmptyState.js.map +1 -0
- package/dist/components/feedback/EmptyState/EmptyState.module.js +17 -0
- package/dist/components/feedback/EmptyState/EmptyState.module.js.map +1 -0
- package/dist/components/feedback/EmptyState/EmptyState.types.d.ts +14 -0
- package/dist/components/feedback/EmptyState/EmptyState.types.d.ts.map +1 -0
- package/dist/components/feedback/EmptyState/index.d.ts +3 -0
- package/dist/components/feedback/EmptyState/index.d.ts.map +1 -0
- package/dist/components/feedback/Modal/BlockModal.d.ts.map +1 -1
- package/dist/components/feedback/Modal/BlockModal.js +3 -12
- package/dist/components/feedback/Modal/BlockModal.js.map +1 -1
- package/dist/components/feedback/Skeleton/Skeleton.d.ts +7 -0
- package/dist/components/feedback/Skeleton/Skeleton.d.ts.map +1 -0
- package/dist/components/feedback/Skeleton/Skeleton.js +33 -0
- package/dist/components/feedback/Skeleton/Skeleton.js.map +1 -0
- package/dist/components/feedback/Skeleton/Skeleton.module.js +12 -0
- package/dist/components/feedback/Skeleton/Skeleton.module.js.map +1 -0
- package/dist/components/feedback/Skeleton/Skeleton.types.d.ts +14 -0
- package/dist/components/feedback/Skeleton/Skeleton.types.d.ts.map +1 -0
- package/dist/components/feedback/Skeleton/Skeleton.types.js +11 -0
- package/dist/components/feedback/Skeleton/Skeleton.types.js.map +1 -0
- package/dist/components/feedback/Skeleton/index.d.ts +4 -0
- package/dist/components/feedback/Skeleton/index.d.ts.map +1 -0
- package/dist/components/feedback/index.d.ts +3 -0
- package/dist/components/feedback/index.d.ts.map +1 -1
- package/dist/components/forms/NumberInput/NumberInput.d.ts +8 -0
- package/dist/components/forms/NumberInput/NumberInput.d.ts.map +1 -0
- package/dist/components/forms/NumberInput/NumberInput.js +122 -0
- package/dist/components/forms/NumberInput/NumberInput.js.map +1 -0
- package/dist/components/forms/NumberInput/NumberInput.module.js +13 -0
- package/dist/components/forms/NumberInput/NumberInput.module.js.map +1 -0
- package/dist/components/forms/NumberInput/NumberInput.types.d.ts +20 -0
- package/dist/components/forms/NumberInput/NumberInput.types.d.ts.map +1 -0
- package/dist/components/forms/NumberInput/NumberInput.utils.d.ts +11 -0
- package/dist/components/forms/NumberInput/NumberInput.utils.d.ts.map +1 -0
- package/dist/components/forms/NumberInput/NumberInput.utils.js +26 -0
- package/dist/components/forms/NumberInput/NumberInput.utils.js.map +1 -0
- package/dist/components/forms/NumberInput/index.d.ts +3 -0
- package/dist/components/forms/NumberInput/index.d.ts.map +1 -0
- package/dist/components/forms/index.d.ts +1 -0
- package/dist/components/forms/index.d.ts.map +1 -1
- package/dist/components/inventory/InventorySlot/InventorySlot.js +2 -2
- package/dist/components/inventory/InventorySlot/InventorySlot.js.map +1 -1
- package/dist/components/layout/Accordion/Accordion.d.ts +7 -0
- package/dist/components/layout/Accordion/Accordion.d.ts.map +1 -0
- package/dist/components/layout/Accordion/Accordion.js +95 -0
- package/dist/components/layout/Accordion/Accordion.js.map +1 -0
- package/dist/components/layout/Accordion/Accordion.module.js +23 -0
- package/dist/components/layout/Accordion/Accordion.module.js.map +1 -0
- package/dist/components/layout/Accordion/Accordion.types.d.ts +21 -0
- package/dist/components/layout/Accordion/Accordion.types.d.ts.map +1 -0
- package/dist/components/layout/Accordion/index.d.ts +3 -0
- package/dist/components/layout/Accordion/index.d.ts.map +1 -0
- package/dist/components/layout/BlockContainer/BlockContainer.d.ts +4 -0
- package/dist/components/layout/BlockContainer/BlockContainer.d.ts.map +1 -0
- package/dist/components/layout/BlockContainer/BlockContainer.js +18 -0
- package/dist/components/layout/BlockContainer/BlockContainer.js.map +1 -0
- package/dist/components/layout/BlockContainer/BlockContainer.module.js +7 -0
- package/dist/components/layout/BlockContainer/BlockContainer.module.js.map +1 -0
- package/dist/components/layout/BlockContainer/BlockContainer.types.d.ts +11 -0
- package/dist/components/layout/BlockContainer/BlockContainer.types.d.ts.map +1 -0
- package/dist/components/layout/BlockContainer/index.d.ts +3 -0
- package/dist/components/layout/BlockContainer/index.d.ts.map +1 -0
- package/dist/components/layout/BlockGrid/BlockGrid.d.ts +4 -0
- package/dist/components/layout/BlockGrid/BlockGrid.d.ts.map +1 -0
- package/dist/components/layout/BlockGrid/BlockGrid.js +25 -0
- package/dist/components/layout/BlockGrid/BlockGrid.js.map +1 -0
- package/dist/components/layout/BlockGrid/BlockGrid.module.js +7 -0
- package/dist/components/layout/BlockGrid/BlockGrid.module.js.map +1 -0
- package/dist/components/layout/BlockGrid/BlockGrid.types.d.ts +16 -0
- package/dist/components/layout/BlockGrid/BlockGrid.types.d.ts.map +1 -0
- package/dist/components/layout/BlockGrid/index.d.ts +3 -0
- package/dist/components/layout/BlockGrid/index.d.ts.map +1 -0
- package/dist/components/layout/index.d.ts +3 -0
- package/dist/components/layout/index.d.ts.map +1 -1
- package/dist/components/navigation/BlockSidebar/BlockSidebar.js +3 -3
- package/dist/components/navigation/BlockSidebar/BlockSidebar.js.map +1 -1
- package/dist/components/navigation/BlockStepper/BlockStepper.d.ts +8 -0
- package/dist/components/navigation/BlockStepper/BlockStepper.d.ts.map +1 -0
- package/dist/components/navigation/BlockStepper/BlockStepper.js +66 -0
- package/dist/components/navigation/BlockStepper/BlockStepper.js.map +1 -0
- package/dist/components/navigation/BlockStepper/BlockStepper.module.js +23 -0
- package/dist/components/navigation/BlockStepper/BlockStepper.module.js.map +1 -0
- package/dist/components/navigation/BlockStepper/BlockStepper.types.d.ts +19 -0
- package/dist/components/navigation/BlockStepper/BlockStepper.types.d.ts.map +1 -0
- package/dist/components/navigation/BlockStepper/index.d.ts +3 -0
- package/dist/components/navigation/BlockStepper/index.d.ts.map +1 -0
- package/dist/components/navigation/Breadcrumb/Breadcrumb.js +2 -2
- package/dist/components/navigation/Breadcrumb/Breadcrumb.js.map +1 -1
- package/dist/components/navigation/HotbarNavigation/HotbarNavigation.js +3 -3
- package/dist/components/navigation/HotbarNavigation/HotbarNavigation.js.map +1 -1
- package/dist/components/navigation/index.d.ts +1 -0
- package/dist/components/navigation/index.d.ts.map +1 -1
- package/dist/hooks/useAnchoredPosition.d.ts +3 -1
- package/dist/hooks/useAnchoredPosition.d.ts.map +1 -1
- package/dist/hooks/useAnchoredPosition.js.map +1 -1
- package/dist/hooks/useScrollLock.d.ts +6 -0
- package/dist/hooks/useScrollLock.d.ts.map +1 -0
- package/dist/hooks/useScrollLock.js +26 -0
- package/dist/hooks/useScrollLock.js.map +1 -0
- package/dist/index.js +16 -1
- package/dist/styles.css +776 -36
- package/package.json +4 -4
|
@@ -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":";;;;;;;;;;;;;;;;AAgBA,IAAa,aAAa,WAA4C,SAAS,WAC7E,EACE,SACA,YACA,OACA,cACA,eACA,SACA,uBAAuB,OACvB,QAAQ,eACR,WACA,GAAG,QAEL,KACA;CACA,MAAM,UAAU,MAAM;CACtB,MAAM,CAAC,OAAO,YAAY,SAAS,EAAE;CACrC,MAAM,CAAC,UAAU,eAAe,SAAA,KAAY;CAC5C,MAAM,CAAC,eAAe,oBAAoB,SAAS,oBAAoB;CACvE,MAAM,CAAC,YAAY,iBAAiB,qBAAqB;EACvD;EACA,cAAc,gBAAgB;EAC9B,UAAU;CACZ,CAAC;CAED,MAAM,UAAU,CAAC;EAAE,IAAA;EAAS,OAAO;CAAM,GAAG,GAAI,cAAc,iBAAiB,OAAO,CAAE;CACxF,MAAM,UAAU,cAAc,SAAS;EAAE;EAAO;EAAU;CAAc,CAAC;CACzE,MAAM,WAAW,QAAQ,MAAM,WAAW,OAAO,OAAO,UAAU;CAClE,MAAM,UAAU,MAAM,KAAK,EAAE,QAAQ,EAAE,IAAI,GAAG,MAAM,UAAU,cAAc,MAAM,IAAI;CAEtF,OACE,qBAAC,OAAD;EAAU;EAAK,MAAK;EAAQ,cAAY;EAAO,WAAW,GAAGA,0BAAO,MAAM,SAAS;EAAG,GAAI;EAA1F,UAAA,CACE,qBAAC,OAAD;GAAK,WAAWA,0BAAO;GAAvB,UAAA;IACE,oBAAC,YAAD;KACE,MAAK;KACL,OAAM;KACN,OAAO;KACP,WAAW,UAAU,SAAS,MAAM,OAAO,KAAK;KAChD,WAAW,oBAAC,YAAD,EAAY,MAAM,GAAK,CAAA;IACnC,CAAA;IACD,qBAAC,OAAD;KAAK,WAAWA,0BAAO;KAAvB,UAAA,CACE,oBAAC,OAAD;MAAK,MAAK;MAAQ,cAAW;MAAa,WAAWA,0BAAO;MACzD,UAAA,QAAQ,KAAK,WACZ,qBAAC,UAAD;OAEE,MAAK;OACL,gBAAc,aAAa,OAAO;OAClC,WAAWA,0BAAO;OAClB,eAAe,YAAY,OAAO,EAAE;OALtC,UAAA,CAOG,UAAU,UAAU,OAAO,OAC1B,oBAAC,QAAD;QAAM,WAAWA,0BAAO;QAAc,eAAY;QAC/C,UAAA,OAAO;OACJ,CAAA,IACJ,MACH,OAAO,KACF;MAZD,GAAA,OAAO,EAYN,CACT;KACE,CAAA,GACL,oBAAC,aAAD;MACE,MAAK;MACL,OAAM;MACN,SAAS;MACT,iBAAiB;KAClB,CAAA,CACE;;IACL,oBAAC,KAAD;KAAG,WAAWA,0BAAO;KAAO,MAAK;KAC9B,UAAA,QAAQ,WAAW,IAAI,aAAa,GAAG,QAAQ,OAAO;IACtD,CAAA;IACF,QAAQ,SAAS,IAChB,oBAAC,MAAD;KAAI,WAAWA,0BAAO;KAAM,cAAW;KACpC,UAAA,QAAQ,KAAK,WACZ,oBAAC,MAAD,EAAA,UACE,oBAAC,eAAD;MACE,MAAK;MACL,UAAU,OAAO,OAAO;MACxB,OACE,OAAO,cAAc,QACjB,GAAG,OAAO,KAAK,0BACf,OAAO;MAEb,kBAAgB,OAAO,cAAc,QAAQ,UAAU,KAAA;MACvD,WAAWA,0BAAO;MAClB,eAAe,cAAc,OAAO,EAAE;MAErC,UAAA,OAAO;KACK,CAAA,EACb,GAfK,OAAO,EAeZ,CACL;IACC,CAAA,IAEJ,oBAAC,KAAD;KAAG,WAAWA,0BAAO;KAAO,UAAA;IAAoB,CAAA;GAE/C;EACL,CAAA,GAAA,qBAAC,WAAD;GAAS,WAAWA,0BAAO;GAAQ,mBAAiB;GAApD,UAAA,CACE,oBAAC,MAAD;IAAI,IAAI;IAAS,WAAWA,0BAAO;IAChC,UAAA,WAAW,SAAS,OAAO;GAC1B,CAAA,GACH,WACC,qBAAAC,
|
|
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":";;;;;;;;;;;;;;;;AAgBA,IAAa,aAAa,WAA4C,SAAS,WAC7E,EACE,SACA,YACA,OACA,cACA,eACA,SACA,uBAAuB,OACvB,QAAQ,eACR,WACA,GAAG,QAEL,KACA;CACA,MAAM,UAAU,MAAM;CACtB,MAAM,CAAC,OAAO,YAAY,SAAS,EAAE;CACrC,MAAM,CAAC,UAAU,eAAe,SAAA,KAAY;CAC5C,MAAM,CAAC,eAAe,oBAAoB,SAAS,oBAAoB;CACvE,MAAM,CAAC,YAAY,iBAAiB,qBAAqB;EACvD;EACA,cAAc,gBAAgB;EAC9B,UAAU;CACZ,CAAC;CAED,MAAM,UAAU,CAAC;EAAE,IAAA;EAAS,OAAO;CAAM,GAAG,GAAI,cAAc,iBAAiB,OAAO,CAAE;CACxF,MAAM,UAAU,cAAc,SAAS;EAAE;EAAO;EAAU;CAAc,CAAC;CACzE,MAAM,WAAW,QAAQ,MAAM,WAAW,OAAO,OAAO,UAAU;CAClE,MAAM,UAAU,MAAM,KAAK,EAAE,QAAQ,EAAE,IAAI,GAAG,MAAM,UAAU,cAAc,MAAM,IAAI;CAEtF,OACE,qBAAC,OAAD;EAAU;EAAK,MAAK;EAAQ,cAAY;EAAO,WAAW,GAAGA,0BAAO,MAAM,SAAS;EAAG,GAAI;EAA1F,UAAA,CACE,qBAAC,OAAD;GAAK,WAAWA,0BAAO;GAAvB,UAAA;IACE,oBAAC,YAAD;KACE,MAAK;KACL,OAAM;KACN,OAAO;KACP,WAAW,UAAU,SAAS,MAAM,OAAO,KAAK;KAChD,WAAW,oBAAC,YAAD,EAAY,MAAM,GAAK,CAAA;IACnC,CAAA;IACD,qBAAC,OAAD;KAAK,WAAWA,0BAAO;KAAvB,UAAA,CACE,oBAAC,OAAD;MAAK,MAAK;MAAQ,cAAW;MAAa,WAAWA,0BAAO;MACzD,UAAA,QAAQ,KAAK,WACZ,qBAAC,UAAD;OAEE,MAAK;OACL,gBAAc,aAAa,OAAO;OAClC,WAAWA,0BAAO;OAClB,eAAe,YAAY,OAAO,EAAE;OALtC,UAAA,CAOG,UAAU,UAAU,OAAO,OAC1B,oBAAC,QAAD;QAAM,WAAWA,0BAAO;QAAc,eAAY;QAC/C,UAAA,OAAO;OACJ,CAAA,IACJ,MACH,OAAO,KACF;MAZD,GAAA,OAAO,EAYN,CACT;KACE,CAAA,GACL,oBAAC,aAAD;MACE,MAAK;MACL,OAAM;MACN,SAAS;MACT,iBAAiB;KAClB,CAAA,CACE;;IACL,oBAAC,KAAD;KAAG,WAAWA,0BAAO;KAAO,MAAK;KAC9B,UAAA,QAAQ,WAAW,IAAI,aAAa,GAAG,QAAQ,OAAO;IACtD,CAAA;IACF,QAAQ,SAAS,IAChB,oBAAC,MAAD;KAAI,WAAWA,0BAAO;KAAM,cAAW;KACpC,UAAA,QAAQ,KAAK,WACZ,oBAAC,MAAD,EAAA,UACE,oBAAC,eAAD;MACE,MAAK;MACL,UAAU,OAAO,OAAO;MACxB,OACE,OAAO,cAAc,QACjB,GAAG,OAAO,KAAK,0BACf,OAAO;MAEb,kBAAgB,OAAO,cAAc,QAAQ,UAAU,KAAA;MACvD,WAAWA,0BAAO;MAClB,eAAe,cAAc,OAAO,EAAE;MAErC,UAAA,OAAO;KACK,CAAA,EACb,GAfK,OAAO,EAeZ,CACL;IACC,CAAA,IAEJ,oBAAC,KAAD;KAAG,WAAWA,0BAAO;KAAO,UAAA;IAAoB,CAAA;GAE/C;EACL,CAAA,GAAA,qBAAC,WAAD;GAAS,WAAWA,0BAAO;GAAQ,mBAAiB;GAApD,UAAA,CACE,oBAAC,MAAD;IAAI,IAAI;IAAS,WAAWA,0BAAO;IAChC,UAAA,WAAW,SAAS,OAAO;GAC1B,CAAA,GACH,WACC,qBAAAC,YAAA,EAAA,UAAA;IACE,qBAAC,OAAD;KAAK,WAAWD,0BAAO;KAAvB,UAAA;MACE,oBAAC,OAAD;OAAK,MAAK;OAAQ,cAAW;OAAU,WAAWA,0BAAO;OACtD,UAAA,QAAQ,KAAK,MAAM,UAClB,oBAAC,eAAD;QAA2B,MAAK;QAC7B,UAAA;OACY,GAFK,KAEL,CAChB;MACE,CAAA;MACL,oBAAC,QAAD;OAAM,WAAWA,0BAAO;OAAO,eAAY;OACzC,UAAA,oBAAC,WAAD,EAAW,MAAM,GAAK,CAAA;MAClB,CAAA;MACN,oBAAC,eAAD;OAAe,MAAK;OAAK,OAAO,SAAS,SAAS;OAC/C,UAAA,SAAS;MACG,CAAA;KACZ;;IACJ,SAAS,cAAc,QACtB,oBAAC,KAAD;KAAG,WAAWA,0BAAO;KAAS,UAAA;IAAsB,CAAA,IAClD;IACH,UACC,oBAAC,aAAD;KACE,SAAQ;KACR,UAAU,SAAS,cAAc;KACjC,eAAe,QAAQ,SAAS,EAAE;KACnC,UAAA;IAEY,CAAA,IACX;GACJ,EAAA,CAAA,IACA,IACG;EACN,CAAA,CAAA;;AAET,CAAC"}
|
|
@@ -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 @@
|
|
|
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 @@
|
|
|
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 @@
|
|
|
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 @@
|
|
|
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: [
|