@bitrise/bitkit-v2 0.3.455-beta.2866 → 0.3.456
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/BitkitColorButton/BitkitColorButton.d.ts +2 -0
- package/dist/components/BitkitColorButton/BitkitColorButton.js +25 -16
- package/dist/components/BitkitColorButton/BitkitColorButton.js.map +1 -1
- package/dist/components/BitkitCombobox/BitkitCombobox.d.ts +16 -0
- package/dist/components/BitkitCombobox/BitkitCombobox.js +30 -9
- package/dist/components/BitkitCombobox/BitkitCombobox.js.map +1 -1
- package/dist/components/BitkitNoteCard/BitkitNoteCard.js +3 -1
- package/dist/components/BitkitNoteCard/BitkitNoteCard.js.map +1 -1
- package/dist/components/BitkitRibbon/BitkitRibbon.js +3 -1
- package/dist/components/BitkitRibbon/BitkitRibbon.js.map +1 -1
- package/dist/components/BitkitSelect/BitkitSelect.js +2 -3
- package/dist/components/BitkitSelect/BitkitSelect.js.map +1 -1
- package/dist/components/BitkitSelectMenu/BitkitSelectMenu.d.ts +1 -4
- package/dist/components/BitkitSelectMenu/BitkitSelectMenu.js +3 -4
- package/dist/components/BitkitSelectMenu/BitkitSelectMenu.js.map +1 -1
- package/dist/components/BitkitToast/BitkitToaster.js +3 -1
- package/dist/components/BitkitToast/BitkitToaster.js.map +1 -1
- package/dist/components/common/NotificationContent.js +3 -1
- package/dist/components/common/NotificationContent.js.map +1 -1
- package/dist/components/common/notificationMaps.d.ts +3 -0
- package/dist/components/common/notificationMaps.js.map +1 -1
- package/dist/theme/slot-recipes/PageSidebar.recipe.d.ts +1 -1
- package/dist/theme/slot-recipes/index.d.ts +1 -1
- package/package.json +1 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { HTMLChakraProps, RecipeProps } from '@chakra-ui/react/styled-system';
|
|
2
|
+
import { BitkitIconComponent } from '../../icons';
|
|
2
3
|
type ColorButtonOmitted = 'as' | 'colorPalette' | 'size' | 'type';
|
|
3
4
|
interface BitkitColorButtonCommonProps extends RecipeProps<'colorButton'> {
|
|
4
5
|
/**
|
|
@@ -11,6 +12,7 @@ interface BitkitColorButtonCommonProps extends RecipeProps<'colorButton'> {
|
|
|
11
12
|
* element they were on the moment they press it.
|
|
12
13
|
*/
|
|
13
14
|
state?: 'disabled' | 'loading';
|
|
15
|
+
suffixIcon?: BitkitIconComponent;
|
|
14
16
|
}
|
|
15
17
|
export interface BitkitColorButtonAsButtonProps extends BitkitColorButtonCommonProps, Omit<HTMLChakraProps<'button'>, ColorButtonOmitted | 'disabled'> {
|
|
16
18
|
href?: undefined;
|
|
@@ -9,26 +9,35 @@ var { withContext } = createRecipeContext({ recipe: colorButtonRecipe });
|
|
|
9
9
|
var StyledButton = withContext("button");
|
|
10
10
|
var StyledAnchor = withContext("a");
|
|
11
11
|
var BitkitColorButton = forwardRef((props, ref) => {
|
|
12
|
-
const { children, size, state } = props;
|
|
12
|
+
const { children, size, state, suffixIcon: SuffixIcon } = props;
|
|
13
13
|
const isDisabled = state === "disabled";
|
|
14
14
|
const isLoading = state === "loading";
|
|
15
15
|
const isInactive = isDisabled || isLoading;
|
|
16
16
|
const spinnerSize = size === "lg" ? "20" : "16";
|
|
17
|
-
const content = /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
17
|
+
const content = /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
18
|
+
isLoading ? /* @__PURE__ */ jsx(Text, {
|
|
19
|
+
as: "span",
|
|
20
|
+
opacity: "0",
|
|
21
|
+
userSelect: "none",
|
|
22
|
+
children
|
|
23
|
+
}) : children,
|
|
24
|
+
SuffixIcon && /* @__PURE__ */ jsx(SuffixIcon, {
|
|
25
|
+
marginInlineEnd: "-4",
|
|
26
|
+
marginInlineStart: "8",
|
|
27
|
+
opacity: isLoading ? 0 : void 0,
|
|
28
|
+
size: (size ?? "sm") === "lg" ? "24" : "16"
|
|
29
|
+
}),
|
|
30
|
+
isLoading && /* @__PURE__ */ jsx(Spinner, {
|
|
31
|
+
position: "absolute",
|
|
32
|
+
insetBlock: "0",
|
|
33
|
+
insetInline: "0",
|
|
34
|
+
margin: "auto",
|
|
35
|
+
height: spinnerSize,
|
|
36
|
+
width: spinnerSize
|
|
37
|
+
})
|
|
38
|
+
] });
|
|
30
39
|
if (props.href !== void 0) {
|
|
31
|
-
const { children: _children, href, isExternal, onClick, onKeyDown, rel, state: _state, target, ...anchorRest } = props;
|
|
40
|
+
const { children: _children, href, isExternal, onClick, onKeyDown, rel, state: _state, suffixIcon: _suffixIcon, target, ...anchorRest } = props;
|
|
32
41
|
const effectiveTarget = isExternal ? "_blank" : target;
|
|
33
42
|
const effectiveRel = isExternal ? rel ? `${rel} noreferrer noopener` : "noreferrer noopener" : rel;
|
|
34
43
|
const handleClick = isInactive ? (e) => {
|
|
@@ -55,7 +64,7 @@ var BitkitColorButton = forwardRef((props, ref) => {
|
|
|
55
64
|
children: content
|
|
56
65
|
});
|
|
57
66
|
}
|
|
58
|
-
const { children: _children, onClick, state: _state, ...buttonRest } = props;
|
|
67
|
+
const { children: _children, onClick, state: _state, suffixIcon: _suffixIcon, ...buttonRest } = props;
|
|
59
68
|
const handleClick = isInactive ? (e) => {
|
|
60
69
|
e.preventDefault();
|
|
61
70
|
e.stopPropagation();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BitkitColorButton.js","names":[],"sources":["../../../lib/components/BitkitColorButton/BitkitColorButton.tsx"],"sourcesContent":["import { Spinner } from '@chakra-ui/react/spinner';\nimport { createRecipeContext, type HTMLChakraProps, type RecipeProps } from '@chakra-ui/react/styled-system';\nimport { Text } from '@chakra-ui/react/text';\nimport { forwardRef, type KeyboardEvent, type MouseEvent, type Ref } from 'react';\n\nimport colorButtonRecipe from '../../theme/recipes/ColorButton.recipe';\n\ntype ColorButtonOmitted = 'as' | 'colorPalette' | 'size' | 'type';\n\ntype StyledButtonProps = Omit<HTMLChakraProps<'button'>, 'colorPalette' | 'size'> & RecipeProps<'colorButton'>;\ntype StyledAnchorProps = Omit<HTMLChakraProps<'a'>, 'colorPalette' | 'size'> & RecipeProps<'colorButton'>;\n\ninterface BitkitColorButtonCommonProps extends RecipeProps<'colorButton'> {\n /**\n * Disabled or loading state of the control. Both are honoured in anchor mode too.\n *\n * Note that `'loading'` behaves differently here than on `BitkitButton`, which delegates to\n * Chakra's `Button loading` and therefore also *disables* and dims the control. This one stays\n * focusable and undimmed: the label keeps its width, a spinner is overlaid on top of it, and\n * activation is blocked by a click guard — so a keyboard or screen reader user doesn't lose the\n * element they were on the moment they press it.\n */\n state?: 'disabled' | 'loading';\n}\n\nexport interface BitkitColorButtonAsButtonProps\n extends BitkitColorButtonCommonProps, Omit<HTMLChakraProps<'button'>, ColorButtonOmitted | 'disabled'> {\n href?: undefined;\n isExternal?: undefined;\n}\n\nexport interface BitkitColorButtonAsAnchorProps\n extends BitkitColorButtonCommonProps, Omit<HTMLChakraProps<'a'>, ColorButtonOmitted> {\n href: string;\n isExternal?: boolean;\n}\n\nexport type BitkitColorButtonProps = BitkitColorButtonAsButtonProps | BitkitColorButtonAsAnchorProps;\n\nconst { withContext } = createRecipeContext({ recipe: colorButtonRecipe });\n\nconst StyledButton = withContext<HTMLButtonElement, StyledButtonProps>('button');\n\nconst StyledAnchor = withContext<HTMLAnchorElement, StyledAnchorProps>('a');\n\nconst BitkitColorButton = forwardRef<HTMLButtonElement | HTMLAnchorElement, BitkitColorButtonProps>((props, ref) => {\n const { children, size, state } = props;\n const isDisabled = state === 'disabled';\n const isLoading = state === 'loading';\n const isInactive = isDisabled || isLoading;\n const spinnerSize = size === 'lg' ? '20' : '16';\n\n const content = (\n <>\n {isLoading ? (\n // `opacity: 0` rather than `visibility: hidden`: both reserve the label's width so the\n // button doesn't collapse, but `visibility: hidden` also drops it from the accessibility\n // tree — leaving a focusable control with no accessible name.\n <Text as=\"span\" opacity=\"0\" userSelect=\"none\">\n {children}\n </Text>\n ) : (\n children\n )}\n {isLoading && (\n <Spinner\n position=\"absolute\"\n insetBlock=\"0\"\n insetInline=\"0\"\n margin=\"auto\"\n height={spinnerSize}\n width={spinnerSize}\n />\n )}\n </>\n );\n\n // --- Anchor mode (href) ---\n if (props.href !== undefined) {\n const {\n children: _children,\n href,\n isExternal,\n onClick,\n onKeyDown,\n rel,\n state: _state,\n target,\n ...anchorRest\n } = props;\n const effectiveTarget = isExternal ? '_blank' : target;\n const effectiveRel = isExternal ? (rel ? `${rel} noreferrer noopener` : 'noreferrer noopener') : rel;\n const handleClick = isInactive\n ? (e: MouseEvent<HTMLAnchorElement>) => {\n e.preventDefault();\n e.stopPropagation();\n }\n : onClick;\n // What actually blocks keyboard activation in both states is `handleClick`: neither state\n // removes the anchor from the tab order (`loading` never sets `disabled`, and a disabled anchor\n // is only `aria-disabled`), and Enter/Space dispatch a click, which the guard above swallows.\n // The keydown guard is the extra layer an anchor needs, since it never gets the native\n // `disabled` treatment a button does.\n const handleKeyDown = isInactive\n ? (e: KeyboardEvent<HTMLAnchorElement>) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n e.stopPropagation();\n }\n }\n : onKeyDown;\n\n return (\n <StyledAnchor\n ref={ref as Ref<HTMLAnchorElement>}\n {...anchorRest}\n aria-disabled={isDisabled || undefined}\n data-disabled={isDisabled || undefined}\n // `data-loading` (not `aria-busy`) — it's the styling hook the recipe's `_loading` condition\n // reads, without claiming ARIA semantics that aren't announced anyway. Announcing the state\n // change to assistive tech needs a live region inside the component; that's its own change.\n data-loading={isLoading || undefined}\n // The `href` is kept while loading — so the link stays focusable and keeps its accessible\n // name, with activation still blocked by the guards above — and dropped only when disabled,\n // to take the anchor out of the tab order and make it fully inert.\n href={isDisabled ? undefined : href}\n onClick={handleClick}\n onKeyDown={handleKeyDown}\n rel={effectiveRel}\n target={effectiveTarget}\n >\n {content}\n </StyledAnchor>\n );\n }\n\n // --- Button mode (default) ---\n const { children: _children, onClick, state: _state, ...buttonRest } = props;\n const handleClick = isInactive\n ? (e: MouseEvent<HTMLButtonElement>) => {\n e.preventDefault();\n e.stopPropagation();\n }\n : onClick;\n\n return (\n <StyledButton\n ref={ref as Ref<HTMLButtonElement>}\n {...buttonRest}\n data-loading={isLoading || undefined}\n disabled={isDisabled}\n onClick={handleClick}\n type=\"button\"\n >\n {content}\n </StyledButton>\n );\n});\n\nBitkitColorButton.displayName = 'BitkitColorButton';\n\nexport default BitkitColorButton;\n"],"mappings":";;;;;;;
|
|
1
|
+
{"version":3,"file":"BitkitColorButton.js","names":[],"sources":["../../../lib/components/BitkitColorButton/BitkitColorButton.tsx"],"sourcesContent":["import { Spinner } from '@chakra-ui/react/spinner';\nimport { createRecipeContext, type HTMLChakraProps, type RecipeProps } from '@chakra-ui/react/styled-system';\nimport { Text } from '@chakra-ui/react/text';\nimport { forwardRef, type KeyboardEvent, type MouseEvent, type Ref } from 'react';\n\nimport { type BitkitIconComponent } from '../../icons';\nimport colorButtonRecipe from '../../theme/recipes/ColorButton.recipe';\n\ntype ColorButtonOmitted = 'as' | 'colorPalette' | 'size' | 'type';\n\ntype StyledButtonProps = Omit<HTMLChakraProps<'button'>, 'colorPalette' | 'size'> & RecipeProps<'colorButton'>;\ntype StyledAnchorProps = Omit<HTMLChakraProps<'a'>, 'colorPalette' | 'size'> & RecipeProps<'colorButton'>;\n\ninterface BitkitColorButtonCommonProps extends RecipeProps<'colorButton'> {\n /**\n * Disabled or loading state of the control. Both are honoured in anchor mode too.\n *\n * Note that `'loading'` behaves differently here than on `BitkitButton`, which delegates to\n * Chakra's `Button loading` and therefore also *disables* and dims the control. This one stays\n * focusable and undimmed: the label keeps its width, a spinner is overlaid on top of it, and\n * activation is blocked by a click guard — so a keyboard or screen reader user doesn't lose the\n * element they were on the moment they press it.\n */\n state?: 'disabled' | 'loading';\n suffixIcon?: BitkitIconComponent;\n}\n\nexport interface BitkitColorButtonAsButtonProps\n extends BitkitColorButtonCommonProps, Omit<HTMLChakraProps<'button'>, ColorButtonOmitted | 'disabled'> {\n href?: undefined;\n isExternal?: undefined;\n}\n\nexport interface BitkitColorButtonAsAnchorProps\n extends BitkitColorButtonCommonProps, Omit<HTMLChakraProps<'a'>, ColorButtonOmitted> {\n href: string;\n isExternal?: boolean;\n}\n\nexport type BitkitColorButtonProps = BitkitColorButtonAsButtonProps | BitkitColorButtonAsAnchorProps;\n\nconst { withContext } = createRecipeContext({ recipe: colorButtonRecipe });\n\nconst StyledButton = withContext<HTMLButtonElement, StyledButtonProps>('button');\n\nconst StyledAnchor = withContext<HTMLAnchorElement, StyledAnchorProps>('a');\n\nconst BitkitColorButton = forwardRef<HTMLButtonElement | HTMLAnchorElement, BitkitColorButtonProps>((props, ref) => {\n const { children, size, state, suffixIcon: SuffixIcon } = props;\n const isDisabled = state === 'disabled';\n const isLoading = state === 'loading';\n const isInactive = isDisabled || isLoading;\n const spinnerSize = size === 'lg' ? '20' : '16';\n // Keep the fallback equal to `defaultVariants.size` in ColorButton.recipe.ts.\n const iconSize = (size ?? 'sm') === 'lg' ? '24' : '16';\n\n const content = (\n <>\n {isLoading ? (\n // `opacity: 0` rather than `visibility: hidden`: both reserve the label's width so the\n // button doesn't collapse, but `visibility: hidden` also drops it from the accessibility\n // tree — leaving a focusable control with no accessible name.\n <Text as=\"span\" opacity=\"0\" userSelect=\"none\">\n {children}\n </Text>\n ) : (\n children\n )}\n {SuffixIcon && (\n <SuffixIcon marginInlineEnd=\"-4\" marginInlineStart=\"8\" opacity={isLoading ? 0 : undefined} size={iconSize} />\n )}\n {isLoading && (\n <Spinner\n position=\"absolute\"\n insetBlock=\"0\"\n insetInline=\"0\"\n margin=\"auto\"\n height={spinnerSize}\n width={spinnerSize}\n />\n )}\n </>\n );\n\n // --- Anchor mode (href) ---\n if (props.href !== undefined) {\n const {\n children: _children,\n href,\n isExternal,\n onClick,\n onKeyDown,\n rel,\n state: _state,\n suffixIcon: _suffixIcon,\n target,\n ...anchorRest\n } = props;\n const effectiveTarget = isExternal ? '_blank' : target;\n const effectiveRel = isExternal ? (rel ? `${rel} noreferrer noopener` : 'noreferrer noopener') : rel;\n const handleClick = isInactive\n ? (e: MouseEvent<HTMLAnchorElement>) => {\n e.preventDefault();\n e.stopPropagation();\n }\n : onClick;\n // What actually blocks keyboard activation in both states is `handleClick`: neither state\n // removes the anchor from the tab order (`loading` never sets `disabled`, and a disabled anchor\n // is only `aria-disabled`), and Enter/Space dispatch a click, which the guard above swallows.\n // The keydown guard is the extra layer an anchor needs, since it never gets the native\n // `disabled` treatment a button does.\n const handleKeyDown = isInactive\n ? (e: KeyboardEvent<HTMLAnchorElement>) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n e.stopPropagation();\n }\n }\n : onKeyDown;\n\n return (\n <StyledAnchor\n ref={ref as Ref<HTMLAnchorElement>}\n {...anchorRest}\n aria-disabled={isDisabled || undefined}\n data-disabled={isDisabled || undefined}\n // `data-loading` (not `aria-busy`) — it's the styling hook the recipe's `_loading` condition\n // reads, without claiming ARIA semantics that aren't announced anyway. Announcing the state\n // change to assistive tech needs a live region inside the component; that's its own change.\n data-loading={isLoading || undefined}\n // The `href` is kept while loading — so the link stays focusable and keeps its accessible\n // name, with activation still blocked by the guards above — and dropped only when disabled,\n // to take the anchor out of the tab order and make it fully inert.\n href={isDisabled ? undefined : href}\n onClick={handleClick}\n onKeyDown={handleKeyDown}\n rel={effectiveRel}\n target={effectiveTarget}\n >\n {content}\n </StyledAnchor>\n );\n }\n\n // --- Button mode (default) ---\n const { children: _children, onClick, state: _state, suffixIcon: _suffixIcon, ...buttonRest } = props;\n const handleClick = isInactive\n ? (e: MouseEvent<HTMLButtonElement>) => {\n e.preventDefault();\n e.stopPropagation();\n }\n : onClick;\n\n return (\n <StyledButton\n ref={ref as Ref<HTMLButtonElement>}\n {...buttonRest}\n data-loading={isLoading || undefined}\n disabled={isDisabled}\n onClick={handleClick}\n type=\"button\"\n >\n {content}\n </StyledButton>\n );\n});\n\nBitkitColorButton.displayName = 'BitkitColorButton';\n\nexport default BitkitColorButton;\n"],"mappings":";;;;;;;AAyCA,IAAM,EAAE,gBAAgB,oBAAoB,EAAE,QAAQ,kBAAkB,CAAC;AAEzE,IAAM,eAAe,YAAkD,QAAQ;AAE/E,IAAM,eAAe,YAAkD,GAAG;AAE1E,IAAM,oBAAoB,YAA2E,OAAO,QAAQ;CAClH,MAAM,EAAE,UAAU,MAAM,OAAO,YAAY,eAAe;CAC1D,MAAM,aAAa,UAAU;CAC7B,MAAM,YAAY,UAAU;CAC5B,MAAM,aAAa,cAAc;CACjC,MAAM,cAAc,SAAS,OAAO,OAAO;CAI3C,MAAM,UACJ,qBAAA,YAAA,EAAA,UAAA;EACG,YAIC,oBAAC,MAAD;GAAM,IAAG;GAAO,SAAQ;GAAI,YAAW;GACpC;EACG,CAAA,IAEN;EAED,cACC,oBAAC,YAAD;GAAY,iBAAgB;GAAK,mBAAkB;GAAI,SAAS,YAAY,IAAI,KAAA;GAAW,OAf/E,QAAQ,UAAU,OAAO,OAAO;EAegE,CAAA;EAE7G,aACC,oBAAC,SAAD;GACE,UAAS;GACT,YAAW;GACX,aAAY;GACZ,QAAO;GACP,QAAQ;GACR,OAAO;EACR,CAAA;CAEH,EAAA,CAAA;CAIJ,IAAI,MAAM,SAAS,KAAA,GAAW;EAC5B,MAAM,EACJ,UAAU,WACV,MACA,YACA,SACA,WACA,KACA,OAAO,QACP,YAAY,aACZ,QACA,GAAG,eACD;EACJ,MAAM,kBAAkB,aAAa,WAAW;EAChD,MAAM,eAAe,aAAc,MAAM,GAAG,IAAI,wBAAwB,wBAAyB;EACjG,MAAM,cAAc,cACf,MAAqC;GACpC,EAAE,eAAe;GACjB,EAAE,gBAAgB;EACpB,IACA;EAMJ,MAAM,gBAAgB,cACjB,MAAwC;GACvC,IAAI,EAAE,QAAQ,WAAW,EAAE,QAAQ,KAAK;IACtC,EAAE,eAAe;IACjB,EAAE,gBAAgB;GACpB;EACF,IACA;EAEJ,OACE,oBAAC,cAAD;GACO;GACL,GAAI;GACJ,iBAAe,cAAc,KAAA;GAC7B,iBAAe,cAAc,KAAA;GAI7B,gBAAc,aAAa,KAAA;GAI3B,MAAM,aAAa,KAAA,IAAY;GAC/B,SAAS;GACT,WAAW;GACX,KAAK;GACL,QAAQ;GAEP,UAAA;EACW,CAAA;CAElB;CAGA,MAAM,EAAE,UAAU,WAAW,SAAS,OAAO,QAAQ,YAAY,aAAa,GAAG,eAAe;CAChG,MAAM,cAAc,cACf,MAAqC;EACpC,EAAE,eAAe;EACjB,EAAE,gBAAgB;CACpB,IACA;CAEJ,OACE,oBAAC,cAAD;EACO;EACL,GAAI;EACJ,gBAAc,aAAa,KAAA;EAC3B,UAAU;EACV,SAAS;EACT,MAAK;EAEJ,UAAA;CACW,CAAA;AAElB,CAAC;AAED,kBAAkB,cAAc"}
|
|
@@ -15,6 +15,14 @@ export type BitkitComboboxProps = Omit<BitkitFieldProps, 'children' | 'onChange'
|
|
|
15
15
|
disablePortal?: boolean;
|
|
16
16
|
isLoading?: boolean;
|
|
17
17
|
items: Array<BitkitSelectMenuItemProps>;
|
|
18
|
+
/**
|
|
19
|
+
* Fires with the text the user types, and with `''` once the input is cleared or filled in from a
|
|
20
|
+
* selection. When provided, the combobox stops filtering `items` itself: pass only the options
|
|
21
|
+
* that match, for example the results of a server-side search. Keep the selected option in
|
|
22
|
+
* `items`, since its label is looked up there. With `allowCustomValue`, text committed before its
|
|
23
|
+
* results arrive is matched only against the current `items`.
|
|
24
|
+
*/
|
|
25
|
+
onSearchChange?: (searchText: string) => void;
|
|
18
26
|
onValueChange?: (newVal: string | undefined) => void;
|
|
19
27
|
placeholder?: string;
|
|
20
28
|
size?: 'md' | 'lg';
|
|
@@ -34,6 +42,14 @@ declare const _default: import('react').ForwardRefExoticComponent<Omit<BitkitFie
|
|
|
34
42
|
disablePortal?: boolean;
|
|
35
43
|
isLoading?: boolean;
|
|
36
44
|
items: Array<BitkitSelectMenuItemProps>;
|
|
45
|
+
/**
|
|
46
|
+
* Fires with the text the user types, and with `''` once the input is cleared or filled in from a
|
|
47
|
+
* selection. When provided, the combobox stops filtering `items` itself: pass only the options
|
|
48
|
+
* that match, for example the results of a server-side search. Keep the selected option in
|
|
49
|
+
* `items`, since its label is looked up there. With `allowCustomValue`, text committed before its
|
|
50
|
+
* results arrive is matched only against the current `items`.
|
|
51
|
+
*/
|
|
52
|
+
onSearchChange?: (searchText: string) => void;
|
|
37
53
|
onValueChange?: (newVal: string | undefined) => void;
|
|
38
54
|
placeholder?: string;
|
|
39
55
|
size?: "md" | "lg";
|
|
@@ -7,7 +7,7 @@ import BitkitCloseButton from "../BitkitCloseButton/BitkitCloseButton.js";
|
|
|
7
7
|
import BitkitSelectMenuAction from "../BitkitSelectMenu/BitkitSelectMenuAction.js";
|
|
8
8
|
import BitkitSelectMenu from "../BitkitSelectMenu/BitkitSelectMenu.js";
|
|
9
9
|
import BitkitField from "../BitkitField/BitkitField.js";
|
|
10
|
-
import { forwardRef, useContext, useRef, useState } from "react";
|
|
10
|
+
import { forwardRef, useContext, useEffect, useRef, useState } from "react";
|
|
11
11
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
12
12
|
import { Portal } from "@chakra-ui/react/portal";
|
|
13
13
|
import { createListCollection } from "@chakra-ui/react/collection";
|
|
@@ -19,9 +19,10 @@ import { useFilter } from "@chakra-ui/react/locale";
|
|
|
19
19
|
* A text input that filters a list of options as you type. The menu opens on focus with every
|
|
20
20
|
* option, narrows to the ones matching the input, and shows an empty state when nothing matches.
|
|
21
21
|
* Unlike BitkitSelect's menu it has no separate search field — the input itself is the filter.
|
|
22
|
+
* Pass `onSearchChange` to do the filtering yourself, for example against a server-side search.
|
|
22
23
|
*/
|
|
23
24
|
var BitkitCombobox = forwardRef((props, ref) => {
|
|
24
|
-
const { allowCustomValue, children, comboboxProps, defaultValue, disablePortal, emptyHelperText, emptyLabel, isLoading, items, onScrolledToBottom, onValueChange, placeholder = "Enter value or select", size = "lg", state, value, ...fieldProps } = props;
|
|
25
|
+
const { allowCustomValue, children, comboboxProps, defaultValue, disablePortal, emptyHelperText, emptyLabel, isLoading, items, onScrolledToBottom, onSearchChange, onValueChange, placeholder = "Enter value or select", size = "lg", state, value, ...fieldProps } = props;
|
|
25
26
|
return /* @__PURE__ */ jsx(BitkitField, {
|
|
26
27
|
ref,
|
|
27
28
|
state,
|
|
@@ -38,6 +39,7 @@ var BitkitCombobox = forwardRef((props, ref) => {
|
|
|
38
39
|
isLoading,
|
|
39
40
|
items,
|
|
40
41
|
onScrolledToBottom,
|
|
42
|
+
onSearchChange,
|
|
41
43
|
onValueChange,
|
|
42
44
|
placeholder,
|
|
43
45
|
size,
|
|
@@ -49,16 +51,17 @@ var BitkitCombobox = forwardRef((props, ref) => {
|
|
|
49
51
|
});
|
|
50
52
|
BitkitCombobox.displayName = "BitkitCombobox";
|
|
51
53
|
/** Rendered inside BitkitField so it can read the field's ids and wire them to the input. */
|
|
52
|
-
var ComboboxControl = ({ allowCustomValue, children, comboboxProps, defaultValue, disablePortal, emptyHelperText, emptyLabel, hasWarning, isInvalid, isLoading, items, onScrolledToBottom, onValueChange, placeholder, size, state, value }) => {
|
|
54
|
+
var ComboboxControl = ({ allowCustomValue, children, comboboxProps, defaultValue, disablePortal, emptyHelperText, emptyLabel, hasWarning, isInvalid, isLoading, items, onScrolledToBottom, onSearchChange, onValueChange, placeholder, size, state, value }) => {
|
|
53
55
|
const { disablePortal: disablePortalFromContext } = useContext(BitkitPortalContext);
|
|
54
56
|
const field = useFieldContext();
|
|
55
57
|
const { contains } = useFilter({ sensitivity: "base" });
|
|
56
58
|
const [filterText, setFilterText] = useState("");
|
|
57
|
-
const
|
|
59
|
+
const filtersItself = !onSearchChange;
|
|
60
|
+
const visibleItems = filterText && filtersItself ? items.filter((item) => contains(item.label, filterText)) : items;
|
|
58
61
|
const collection = createListCollection({
|
|
59
62
|
items: visibleItems,
|
|
60
63
|
groupBy: (item) => item.group || "",
|
|
61
|
-
isItemDisabled: (item) => !!item.disabled
|
|
64
|
+
isItemDisabled: (item) => !!isLoading || !!item.disabled
|
|
62
65
|
});
|
|
63
66
|
const itemValues = items.map((item) => item.value);
|
|
64
67
|
const itemsKey = JSON.stringify(itemValues);
|
|
@@ -67,7 +70,7 @@ var ComboboxControl = ({ allowCustomValue, children, comboboxProps, defaultValue
|
|
|
67
70
|
const prevValues = JSON.parse(prevItemsKey);
|
|
68
71
|
const isAppend = itemValues.length > prevValues.length && prevValues.every((v, i) => v === itemValues[i]);
|
|
69
72
|
setPrevItemsKey(itemsKey);
|
|
70
|
-
if (!isAppend) setFilterText("");
|
|
73
|
+
if (!isAppend && filtersItself) setFilterText("");
|
|
71
74
|
}
|
|
72
75
|
const [initialInputValue] = useState(() => {
|
|
73
76
|
const initialValue = value ?? defaultValue;
|
|
@@ -79,9 +82,21 @@ var ComboboxControl = ({ allowCustomValue, children, comboboxProps, defaultValue
|
|
|
79
82
|
const [prevValue, setPrevValue] = useState(value);
|
|
80
83
|
if (value !== prevValue) {
|
|
81
84
|
setPrevValue(value);
|
|
82
|
-
if (allowCustomValue)
|
|
85
|
+
if (allowCustomValue) {
|
|
86
|
+
setInputValue(value ? items.find((item) => item.value === value)?.label ?? value : "");
|
|
87
|
+
setFilterText("");
|
|
88
|
+
}
|
|
83
89
|
}
|
|
84
90
|
const lastEmittedValue = useRef(null);
|
|
91
|
+
const lastReportedSearch = useRef("");
|
|
92
|
+
const reportSearch = (searchText) => {
|
|
93
|
+
if (searchText === lastReportedSearch.current) return;
|
|
94
|
+
lastReportedSearch.current = searchText;
|
|
95
|
+
onSearchChange?.(searchText);
|
|
96
|
+
};
|
|
97
|
+
useEffect(() => {
|
|
98
|
+
reportSearch(filterText);
|
|
99
|
+
});
|
|
85
100
|
const skipFocusOpen = useRef(false);
|
|
86
101
|
const isInteractive = state !== "disabled" && state !== "readOnly";
|
|
87
102
|
const commitCustomValue = () => {
|
|
@@ -120,7 +135,9 @@ var ComboboxControl = ({ allowCustomValue, children, comboboxProps, defaultValue
|
|
|
120
135
|
lastEmittedValue.current = null;
|
|
121
136
|
onValueChange?.(void 0);
|
|
122
137
|
}
|
|
123
|
-
|
|
138
|
+
const nextFilterText = reason === "input-change" ? nextInputValue : "";
|
|
139
|
+
setFilterText(nextFilterText);
|
|
140
|
+
reportSearch(nextFilterText);
|
|
124
141
|
},
|
|
125
142
|
onOpenChange: ({ open }) => {
|
|
126
143
|
setIsOpen(open);
|
|
@@ -128,7 +145,11 @@ var ComboboxControl = ({ allowCustomValue, children, comboboxProps, defaultValue
|
|
|
128
145
|
},
|
|
129
146
|
onValueChange: (details) => {
|
|
130
147
|
const next = details.value[0];
|
|
131
|
-
if (allowCustomValue)
|
|
148
|
+
if (allowCustomValue) {
|
|
149
|
+
setInputValue(next ? items.find((item) => item.value === next)?.label ?? next : "");
|
|
150
|
+
setFilterText("");
|
|
151
|
+
reportSearch("");
|
|
152
|
+
}
|
|
132
153
|
if (next === void 0 && lastEmittedValue.current === null) return;
|
|
133
154
|
lastEmittedValue.current = next ?? null;
|
|
134
155
|
onValueChange?.(next);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BitkitCombobox.js","names":[],"sources":["../../../lib/components/BitkitCombobox/BitkitCombobox.tsx"],"sourcesContent":["import { createListCollection } from '@chakra-ui/react/collection';\nimport { Combobox, type ComboboxRootProps } from '@chakra-ui/react/combobox';\nimport { useFieldContext } from '@chakra-ui/react/field';\nimport { useFilter } from '@chakra-ui/react/locale';\nimport { Portal } from '@chakra-ui/react/portal';\nimport { forwardRef, useContext, useRef, useState } from 'react';\n\nimport IconErrorCircleFilled from '../../icons/IconErrorCircleFilled';\nimport IconWarningYellow from '../../icons/IconWarningYellow';\nimport AssetSelectChevron from '../../utilities/AssetSelectChevron';\nimport BitkitPortalContext from '../../utilities/BitkitPortalContext';\nimport { withSubComponents } from '../../utilities/withSubComponents';\nimport BitkitCloseButton from '../BitkitCloseButton/BitkitCloseButton';\nimport BitkitField, { type BitkitFieldProps } from '../BitkitField/BitkitField';\nimport BitkitSelectMenu, {\n type BitkitSelectMenuEmptyStateProps,\n type BitkitSelectMenuItemProps,\n type BitkitSelectMenuScrollProps,\n} from '../BitkitSelectMenu/BitkitSelectMenu';\nimport BitkitSelectMenuAction, { type BitkitSelectMenuActionChild } from '../BitkitSelectMenu/BitkitSelectMenuAction';\n\nexport type BitkitComboboxProps = Omit<BitkitFieldProps, 'children' | 'onChange' | 'state'> & {\n /**\n * Accept text that matches no option. `onValueChange` then fires with the typed text when the\n * user commits it (closing the menu or leaving the field), and with the option's `value` when\n * they pick one from the list.\n */\n allowCustomValue?: boolean;\n children?: BitkitSelectMenuActionChild;\n comboboxProps?: Omit<\n ComboboxRootProps,\n | 'allowCustomValue'\n | 'collection'\n | 'defaultInputValue'\n | 'defaultValue'\n | 'ids'\n | 'inputValue'\n | 'onInputValueChange'\n | 'onOpenChange'\n | 'onValueChange'\n | 'open'\n | 'openOnClick'\n | 'value'\n >;\n defaultValue?: string;\n disablePortal?: boolean;\n isLoading?: boolean;\n items: Array<BitkitSelectMenuItemProps>;\n onValueChange?: (newVal: string | undefined) => void;\n placeholder?: string;\n size?: 'md' | 'lg';\n state?: 'disabled' | 'error' | 'readOnly' | 'warning';\n value?: string;\n} & BitkitSelectMenuScrollProps &\n BitkitSelectMenuEmptyStateProps;\n\n/**\n * A text input that filters a list of options as you type. The menu opens on focus with every\n * option, narrows to the ones matching the input, and shows an empty state when nothing matches.\n * Unlike BitkitSelect's menu it has no separate search field — the input itself is the filter.\n */\nconst BitkitCombobox = forwardRef<HTMLDivElement, BitkitComboboxProps>((props: BitkitComboboxProps, ref) => {\n const {\n allowCustomValue,\n children,\n comboboxProps,\n defaultValue,\n disablePortal,\n emptyHelperText,\n emptyLabel,\n isLoading,\n items,\n onScrolledToBottom,\n onValueChange,\n placeholder = 'Enter value or select',\n size = 'lg',\n state,\n value,\n ...fieldProps\n } = props;\n\n return (\n <BitkitField ref={ref} state={state} {...fieldProps}>\n <ComboboxControl\n allowCustomValue={allowCustomValue}\n comboboxProps={comboboxProps}\n defaultValue={defaultValue}\n disablePortal={disablePortal}\n emptyHelperText={emptyHelperText ?? (allowCustomValue ? 'Enter custom value.' : undefined)}\n emptyLabel={emptyLabel}\n hasWarning={state === 'warning' || !!fieldProps.warningText}\n isInvalid={state === 'error' || !!fieldProps.errorText}\n isLoading={isLoading}\n items={items}\n onScrolledToBottom={onScrolledToBottom}\n onValueChange={onValueChange}\n placeholder={placeholder}\n size={size}\n state={state}\n value={value}\n >\n {children}\n </ComboboxControl>\n </BitkitField>\n );\n});\n\nBitkitCombobox.displayName = 'BitkitCombobox';\n\ntype ComboboxControlProps = Pick<\n BitkitComboboxProps,\n | 'allowCustomValue'\n | 'children'\n | 'comboboxProps'\n | 'defaultValue'\n | 'disablePortal'\n | 'emptyHelperText'\n | 'emptyLabel'\n | 'isLoading'\n | 'items'\n | 'onScrolledToBottom'\n | 'onValueChange'\n | 'state'\n | 'value'\n> &\n Required<Pick<BitkitComboboxProps, 'placeholder' | 'size'>> & {\n hasWarning: boolean;\n isInvalid: boolean;\n };\n\n/** Rendered inside BitkitField so it can read the field's ids and wire them to the input. */\nconst ComboboxControl = ({\n allowCustomValue,\n children,\n comboboxProps,\n defaultValue,\n disablePortal,\n emptyHelperText,\n emptyLabel,\n hasWarning,\n isInvalid,\n isLoading,\n items,\n onScrolledToBottom,\n onValueChange,\n placeholder,\n size,\n state,\n value,\n}: ComboboxControlProps) => {\n const { disablePortal: disablePortalFromContext } = useContext(BitkitPortalContext);\n const field = useFieldContext();\n\n const { contains } = useFilter({ sensitivity: 'base' });\n\n // Derive the collection on every render rather than using useListCollection, which snapshots\n // `items` into state as `initialItems` and never syncs it again, so changing `items` actually reaches the menu.\n // Deriving also keeps callers that pass an inline array literal safe, since nothing here is keyed on items identity.\n const [filterText, setFilterText] = useState('');\n const visibleItems = filterText ? items.filter((item) => contains(item.label, filterText)) : items;\n const collection = createListCollection<BitkitSelectMenuItemProps>({\n items: visibleItems,\n groupBy: (item) => item.group || '',\n isItemDisabled: (item) => !!item.disabled,\n });\n\n // A new set of items invalidates whatever the old ones were filtered by — otherwise leftover\n // filter text can hide every new item and leave the menu empty. Keyed on item values, not array\n // identity, so an inline `items={[…]}` literal doesn't wipe the filter on every keystroke; an\n // append (the next page of results) keeps it, so paging doesn't drop what the user typed.\n const itemValues = items.map((item) => item.value);\n const itemsKey = JSON.stringify(itemValues);\n const [prevItemsKey, setPrevItemsKey] = useState(itemsKey);\n if (prevItemsKey !== itemsKey) {\n const prevValues = JSON.parse(prevItemsKey) as Array<string>;\n const isAppend = itemValues.length > prevValues.length && prevValues.every((v, i) => v === itemValues[i]);\n setPrevItemsKey(itemsKey);\n if (!isAppend) {\n setFilterText('');\n }\n }\n\n // Zag only reports the input text once it changes, so mirror what it starts with — otherwise the\n // clear button stays hidden next to a pre-selected value.\n const [initialInputValue] = useState(() => {\n const initialValue = value ?? defaultValue;\n if (!initialValue) return '';\n return items.find((item) => item.value === initialValue)?.label ?? (allowCustomValue ? initialValue : '');\n });\n const [inputValue, setInputValue] = useState(initialInputValue);\n const [isOpen, setIsOpen] = useState(false);\n // A controlled `value` that no option carries can only live in the input, so follow it there.\n const [prevValue, setPrevValue] = useState(value);\n if (value !== prevValue) {\n setPrevValue(value);\n if (allowCustomValue) {\n setInputValue(value ? (items.find((item) => item.value === value)?.label ?? value) : '');\n }\n }\n const lastEmittedValue = useRef<string | null>(null);\n // Clicking the chevron focuses the input first, so without this the focus handler would open the\n // menu just before the trigger's own click closes it again.\n const skipFocusOpen = useRef(false);\n\n const isInteractive = state !== 'disabled' && state !== 'readOnly';\n\n const commitCustomValue = () => {\n const text = inputValue.trim();\n if (!allowCustomValue || !text) return;\n // Match the filter's case-insensitivity: typing \"react\" means the React option, not a custom\n // value that happens to read the same.\n const match = items.find((item) => item.label.toLowerCase() === text.toLowerCase());\n const committed = match ? match.value : text;\n if (lastEmittedValue.current === committed) return;\n lastEmittedValue.current = committed;\n onValueChange?.(committed);\n };\n\n const iconSize = size === 'md' ? '16' : '24';\n\n let statusIcon;\n if (isInvalid) {\n statusIcon = <IconErrorCircleFilled size={iconSize} color=\"icon/negative\" />;\n } else if (hasWarning) {\n statusIcon = <IconWarningYellow size={iconSize} />;\n }\n\n return (\n <Combobox.Root\n {...comboboxProps}\n allowCustomValue={allowCustomValue}\n collection={collection}\n defaultInputValue={initialInputValue}\n defaultValue={defaultValue ? [defaultValue] : undefined}\n disabled={state === 'disabled'}\n hasStatusIcon={!!statusIcon}\n // Chakra's Combobox doesn't take its input id from the surrounding Field, so the field\n // label's `htmlFor` would point at an element that doesn't exist. Hand it the field's ids\n // instead, and the label both labels and focuses the input.\n ids={field ? { input: field.ids.control, label: field.ids.label } : undefined}\n invalid={isInvalid}\n // Custom values live only in the input: Zag rewrites it from the collection on every value\n // change, which would blank text no option carries, so we own it and keep Zag off it.\n inputValue={allowCustomValue ? inputValue : undefined}\n onInputValueChange={({ inputValue: nextInputValue, reason }) => {\n setInputValue(nextInputValue);\n // The clear button empties `value` too, but a custom value never reached it, so Zag has\n // nothing to report — tell the consumer the field is empty now.\n if (reason === 'clear-trigger' && lastEmittedValue.current !== null) {\n lastEmittedValue.current = null;\n onValueChange?.(undefined);\n }\n // Only what the user types filters the list. Zag also writes the selected item's label\n // into the input, which would otherwise narrow the menu to that one item.\n setFilterText(reason === 'input-change' ? nextInputValue : '');\n }}\n onOpenChange={({ open }) => {\n setIsOpen(open);\n if (!open) commitCustomValue();\n }}\n onValueChange={(details) => {\n const next = details.value[0];\n // `selectionBehavior=\"preserve\"` leaves the input alone, so put the option's label there.\n if (allowCustomValue) setInputValue(next ? (items.find((item) => item.value === next)?.label ?? next) : '');\n // The clear path above already reported the empty field.\n if (next === undefined && lastEmittedValue.current === null) return;\n lastEmittedValue.current = next ?? null;\n onValueChange?.(next);\n }}\n open={isOpen}\n openOnClick\n readOnly={state === 'readOnly'}\n // Bypass Zag's isScrollable(contentEl) gate — our Content is overflow:hidden flex\n // column, so the real scroll container is itemList. See BitkitMultiselect for why.\n scrollToIndexFn={({ getElement }) => getElement()?.scrollIntoView({ block: 'nearest' })}\n selectionBehavior={allowCustomValue ? 'preserve' : undefined}\n size={size}\n // Stay controlled whenever `value` is provided — including `''` (\"nothing selected\"). A\n // truthiness check (`value ? … : undefined`) passes `undefined` for `''`, which flips\n // Combobox.Root to uncontrolled: it then ignores `value`, so resets don't take and\n // onValueChange stops firing when the same option is re-selected.\n value={value === undefined ? undefined : value ? [value] : []}\n >\n <Combobox.Control className=\"group\">\n <Combobox.Input\n onBlur={commitCustomValue}\n onKeyDown={(event) => {\n // Zag's Enter selects the highlighted item and does nothing when there is none, so\n // free text would just sit in an open menu. Commit it and close.\n if (!allowCustomValue || event.key !== 'Enter') return;\n if (event.currentTarget.getAttribute('aria-activedescendant')) return;\n commitCustomValue();\n setIsOpen(false);\n }}\n onFocus={() => {\n if (skipFocusOpen.current || !isInteractive) return;\n setIsOpen(true);\n }}\n placeholder={placeholder}\n />\n <Combobox.IndicatorGroup>\n {/* Zag hides the clear button until an option is selected; the design shows it for\n anything the field holds, including text that matches nothing yet. */}\n <Combobox.ClearTrigger asChild hidden={!isInteractive || (!inputValue && !value)}>\n <BitkitCloseButton size=\"sm\" />\n </Combobox.ClearTrigger>\n {statusIcon}\n <Combobox.Trigger\n asChild\n onPointerDown={() => {\n skipFocusOpen.current = true;\n requestAnimationFrame(() => {\n skipFocusOpen.current = false;\n });\n }}\n >\n <AssetSelectChevron />\n </Combobox.Trigger>\n </Combobox.IndicatorGroup>\n </Combobox.Control>\n <Portal disabled={disablePortal || disablePortalFromContext}>\n <Combobox.Positioner>\n <BitkitSelectMenu\n collection={collection}\n emptyHelperText={emptyHelperText}\n emptyLabel={emptyLabel}\n isLoading={isLoading}\n onScrolledToBottom={onScrolledToBottom}\n size={size}\n variant=\"combobox\"\n >\n {children}\n </BitkitSelectMenu>\n </Combobox.Positioner>\n </Portal>\n </Combobox.Root>\n );\n};\n\nexport default withSubComponents(BitkitCombobox, { Action: BitkitSelectMenuAction });\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"BitkitCombobox.js","names":[],"sources":["../../../lib/components/BitkitCombobox/BitkitCombobox.tsx"],"sourcesContent":["import { createListCollection } from '@chakra-ui/react/collection';\nimport { Combobox, type ComboboxRootProps } from '@chakra-ui/react/combobox';\nimport { useFieldContext } from '@chakra-ui/react/field';\nimport { useFilter } from '@chakra-ui/react/locale';\nimport { Portal } from '@chakra-ui/react/portal';\nimport { forwardRef, useContext, useEffect, useRef, useState } from 'react';\n\nimport IconErrorCircleFilled from '../../icons/IconErrorCircleFilled';\nimport IconWarningYellow from '../../icons/IconWarningYellow';\nimport AssetSelectChevron from '../../utilities/AssetSelectChevron';\nimport BitkitPortalContext from '../../utilities/BitkitPortalContext';\nimport { withSubComponents } from '../../utilities/withSubComponents';\nimport BitkitCloseButton from '../BitkitCloseButton/BitkitCloseButton';\nimport BitkitField, { type BitkitFieldProps } from '../BitkitField/BitkitField';\nimport BitkitSelectMenu, {\n type BitkitSelectMenuEmptyStateProps,\n type BitkitSelectMenuItemProps,\n type BitkitSelectMenuScrollProps,\n} from '../BitkitSelectMenu/BitkitSelectMenu';\nimport BitkitSelectMenuAction, { type BitkitSelectMenuActionChild } from '../BitkitSelectMenu/BitkitSelectMenuAction';\n\nexport type BitkitComboboxProps = Omit<BitkitFieldProps, 'children' | 'onChange' | 'state'> & {\n /**\n * Accept text that matches no option. `onValueChange` then fires with the typed text when the\n * user commits it (closing the menu or leaving the field), and with the option's `value` when\n * they pick one from the list.\n */\n allowCustomValue?: boolean;\n children?: BitkitSelectMenuActionChild;\n comboboxProps?: Omit<\n ComboboxRootProps,\n | 'allowCustomValue'\n | 'collection'\n | 'defaultInputValue'\n | 'defaultValue'\n | 'ids'\n | 'inputValue'\n | 'onInputValueChange'\n | 'onOpenChange'\n | 'onValueChange'\n | 'open'\n | 'openOnClick'\n | 'value'\n >;\n defaultValue?: string;\n disablePortal?: boolean;\n isLoading?: boolean;\n items: Array<BitkitSelectMenuItemProps>;\n /**\n * Fires with the text the user types, and with `''` once the input is cleared or filled in from a\n * selection. When provided, the combobox stops filtering `items` itself: pass only the options\n * that match, for example the results of a server-side search. Keep the selected option in\n * `items`, since its label is looked up there. With `allowCustomValue`, text committed before its\n * results arrive is matched only against the current `items`.\n */\n onSearchChange?: (searchText: string) => void;\n onValueChange?: (newVal: string | undefined) => void;\n placeholder?: string;\n size?: 'md' | 'lg';\n state?: 'disabled' | 'error' | 'readOnly' | 'warning';\n value?: string;\n} & BitkitSelectMenuScrollProps &\n BitkitSelectMenuEmptyStateProps;\n\n/**\n * A text input that filters a list of options as you type. The menu opens on focus with every\n * option, narrows to the ones matching the input, and shows an empty state when nothing matches.\n * Unlike BitkitSelect's menu it has no separate search field — the input itself is the filter.\n * Pass `onSearchChange` to do the filtering yourself, for example against a server-side search.\n */\nconst BitkitCombobox = forwardRef<HTMLDivElement, BitkitComboboxProps>((props: BitkitComboboxProps, ref) => {\n const {\n allowCustomValue,\n children,\n comboboxProps,\n defaultValue,\n disablePortal,\n emptyHelperText,\n emptyLabel,\n isLoading,\n items,\n onScrolledToBottom,\n onSearchChange,\n onValueChange,\n placeholder = 'Enter value or select',\n size = 'lg',\n state,\n value,\n ...fieldProps\n } = props;\n\n return (\n <BitkitField ref={ref} state={state} {...fieldProps}>\n <ComboboxControl\n allowCustomValue={allowCustomValue}\n comboboxProps={comboboxProps}\n defaultValue={defaultValue}\n disablePortal={disablePortal}\n emptyHelperText={emptyHelperText ?? (allowCustomValue ? 'Enter custom value.' : undefined)}\n emptyLabel={emptyLabel}\n hasWarning={state === 'warning' || !!fieldProps.warningText}\n isInvalid={state === 'error' || !!fieldProps.errorText}\n isLoading={isLoading}\n items={items}\n onScrolledToBottom={onScrolledToBottom}\n onSearchChange={onSearchChange}\n onValueChange={onValueChange}\n placeholder={placeholder}\n size={size}\n state={state}\n value={value}\n >\n {children}\n </ComboboxControl>\n </BitkitField>\n );\n});\n\nBitkitCombobox.displayName = 'BitkitCombobox';\n\ntype ComboboxControlProps = Pick<\n BitkitComboboxProps,\n | 'allowCustomValue'\n | 'children'\n | 'comboboxProps'\n | 'defaultValue'\n | 'disablePortal'\n | 'emptyHelperText'\n | 'emptyLabel'\n | 'isLoading'\n | 'items'\n | 'onScrolledToBottom'\n | 'onSearchChange'\n | 'onValueChange'\n | 'state'\n | 'value'\n> &\n Required<Pick<BitkitComboboxProps, 'placeholder' | 'size'>> & {\n hasWarning: boolean;\n isInvalid: boolean;\n };\n\n/** Rendered inside BitkitField so it can read the field's ids and wire them to the input. */\nconst ComboboxControl = ({\n allowCustomValue,\n children,\n comboboxProps,\n defaultValue,\n disablePortal,\n emptyHelperText,\n emptyLabel,\n hasWarning,\n isInvalid,\n isLoading,\n items,\n onScrolledToBottom,\n onSearchChange,\n onValueChange,\n placeholder,\n size,\n state,\n value,\n}: ComboboxControlProps) => {\n const { disablePortal: disablePortalFromContext } = useContext(BitkitPortalContext);\n const field = useFieldContext();\n\n const { contains } = useFilter({ sensitivity: 'base' });\n\n // Derive the collection on every render rather than using useListCollection, which snapshots\n // `items` into state as `initialItems` and never syncs it again, so changing `items` actually reaches the menu.\n // Deriving also keeps callers that pass an inline array literal safe, since nothing here is keyed on items identity.\n const [filterText, setFilterText] = useState('');\n const filtersItself = !onSearchChange;\n const visibleItems = filterText && filtersItself ? items.filter((item) => contains(item.label, filterText)) : items;\n // The menu hides the options behind its spinner while loading, so disable them too: otherwise arrow\n // keys and Enter can still pick one the user never saw.\n const collection = createListCollection<BitkitSelectMenuItemProps>({\n items: visibleItems,\n groupBy: (item) => item.group || '',\n isItemDisabled: (item) => !!isLoading || !!item.disabled,\n });\n\n // A new set of items invalidates whatever the old ones were filtered by — otherwise leftover\n // filter text can hide every new item and leave the menu empty. Keyed on item values, not array\n // identity, so an inline `items={[…]}` literal doesn't wipe the filter on every keystroke; an\n // append (the next page of results) keeps it, so paging doesn't drop what the user typed.\n const itemValues = items.map((item) => item.value);\n const itemsKey = JSON.stringify(itemValues);\n const [prevItemsKey, setPrevItemsKey] = useState(itemsKey);\n if (prevItemsKey !== itemsKey) {\n const prevValues = JSON.parse(prevItemsKey) as Array<string>;\n const isAppend = itemValues.length > prevValues.length && prevValues.every((v, i) => v === itemValues[i]);\n setPrevItemsKey(itemsKey);\n if (!isAppend && filtersItself) {\n setFilterText('');\n }\n }\n\n // Zag only reports the input text once it changes, so mirror what it starts with — otherwise the\n // clear button stays hidden next to a pre-selected value.\n const [initialInputValue] = useState(() => {\n const initialValue = value ?? defaultValue;\n if (!initialValue) return '';\n return items.find((item) => item.value === initialValue)?.label ?? (allowCustomValue ? initialValue : '');\n });\n const [inputValue, setInputValue] = useState(initialInputValue);\n const [isOpen, setIsOpen] = useState(false);\n // A controlled `value` that no option carries can only live in the input, so follow it there.\n const [prevValue, setPrevValue] = useState(value);\n if (value !== prevValue) {\n setPrevValue(value);\n if (allowCustomValue) {\n setInputValue(value ? (items.find((item) => item.value === value)?.label ?? value) : '');\n setFilterText('');\n }\n }\n const lastEmittedValue = useRef<string | null>(null);\n const lastReportedSearch = useRef('');\n const reportSearch = (searchText: string) => {\n if (searchText === lastReportedSearch.current) return;\n lastReportedSearch.current = searchText;\n onSearchChange?.(searchText);\n };\n // The input handlers report right away, so the consumer's results follow the input in the same\n // render. This catches what changes the search during render instead, like a controlled reset.\n useEffect(() => {\n reportSearch(filterText);\n });\n // Clicking the chevron focuses the input first, so without this the focus handler would open the\n // menu just before the trigger's own click closes it again.\n const skipFocusOpen = useRef(false);\n\n const isInteractive = state !== 'disabled' && state !== 'readOnly';\n\n const commitCustomValue = () => {\n const text = inputValue.trim();\n if (!allowCustomValue || !text) return;\n // Match the filter's case-insensitivity: typing \"react\" means the React option, not a custom\n // value that happens to read the same.\n const match = items.find((item) => item.label.toLowerCase() === text.toLowerCase());\n const committed = match ? match.value : text;\n if (lastEmittedValue.current === committed) return;\n lastEmittedValue.current = committed;\n onValueChange?.(committed);\n };\n\n const iconSize = size === 'md' ? '16' : '24';\n\n let statusIcon;\n if (isInvalid) {\n statusIcon = <IconErrorCircleFilled size={iconSize} color=\"icon/negative\" />;\n } else if (hasWarning) {\n statusIcon = <IconWarningYellow size={iconSize} />;\n }\n\n return (\n <Combobox.Root\n {...comboboxProps}\n allowCustomValue={allowCustomValue}\n collection={collection}\n defaultInputValue={initialInputValue}\n defaultValue={defaultValue ? [defaultValue] : undefined}\n disabled={state === 'disabled'}\n hasStatusIcon={!!statusIcon}\n // Chakra's Combobox doesn't take its input id from the surrounding Field, so the field\n // label's `htmlFor` would point at an element that doesn't exist. Hand it the field's ids\n // instead, and the label both labels and focuses the input.\n ids={field ? { input: field.ids.control, label: field.ids.label } : undefined}\n invalid={isInvalid}\n // Custom values live only in the input: Zag rewrites it from the collection on every value\n // change, which would blank text no option carries, so we own it and keep Zag off it.\n inputValue={allowCustomValue ? inputValue : undefined}\n onInputValueChange={({ inputValue: nextInputValue, reason }) => {\n setInputValue(nextInputValue);\n // The clear button empties `value` too, but a custom value never reached it, so Zag has\n // nothing to report — tell the consumer the field is empty now.\n if (reason === 'clear-trigger' && lastEmittedValue.current !== null) {\n lastEmittedValue.current = null;\n onValueChange?.(undefined);\n }\n // Only what the user types filters the list. Zag also writes the selected item's label\n // into the input, which would otherwise narrow the menu to that one item.\n const nextFilterText = reason === 'input-change' ? nextInputValue : '';\n setFilterText(nextFilterText);\n reportSearch(nextFilterText);\n }}\n onOpenChange={({ open }) => {\n setIsOpen(open);\n if (!open) commitCustomValue();\n }}\n onValueChange={(details) => {\n const next = details.value[0];\n // `selectionBehavior=\"preserve\"` leaves the input alone, so put the option's label there and\n // end the search, as Zag's own input rewrite does without it.\n if (allowCustomValue) {\n setInputValue(next ? (items.find((item) => item.value === next)?.label ?? next) : '');\n setFilterText('');\n reportSearch('');\n }\n // The clear path above already reported the empty field.\n if (next === undefined && lastEmittedValue.current === null) return;\n lastEmittedValue.current = next ?? null;\n onValueChange?.(next);\n }}\n open={isOpen}\n openOnClick\n readOnly={state === 'readOnly'}\n // Bypass Zag's isScrollable(contentEl) gate — our Content is overflow:hidden flex\n // column, so the real scroll container is itemList. See BitkitMultiselect for why.\n scrollToIndexFn={({ getElement }) => getElement()?.scrollIntoView({ block: 'nearest' })}\n selectionBehavior={allowCustomValue ? 'preserve' : undefined}\n size={size}\n // Stay controlled whenever `value` is provided — including `''` (\"nothing selected\"). A\n // truthiness check (`value ? … : undefined`) passes `undefined` for `''`, which flips\n // Combobox.Root to uncontrolled: it then ignores `value`, so resets don't take and\n // onValueChange stops firing when the same option is re-selected.\n value={value === undefined ? undefined : value ? [value] : []}\n >\n <Combobox.Control className=\"group\">\n <Combobox.Input\n onBlur={commitCustomValue}\n onKeyDown={(event) => {\n // Zag's Enter selects the highlighted item and does nothing when there is none, so\n // free text would just sit in an open menu. Commit it and close.\n if (!allowCustomValue || event.key !== 'Enter') return;\n if (event.currentTarget.getAttribute('aria-activedescendant')) return;\n commitCustomValue();\n setIsOpen(false);\n }}\n onFocus={() => {\n if (skipFocusOpen.current || !isInteractive) return;\n setIsOpen(true);\n }}\n placeholder={placeholder}\n />\n <Combobox.IndicatorGroup>\n {/* Zag hides the clear button until an option is selected; the design shows it for\n anything the field holds, including text that matches nothing yet. */}\n <Combobox.ClearTrigger asChild hidden={!isInteractive || (!inputValue && !value)}>\n <BitkitCloseButton size=\"sm\" />\n </Combobox.ClearTrigger>\n {statusIcon}\n <Combobox.Trigger\n asChild\n onPointerDown={() => {\n skipFocusOpen.current = true;\n requestAnimationFrame(() => {\n skipFocusOpen.current = false;\n });\n }}\n >\n <AssetSelectChevron />\n </Combobox.Trigger>\n </Combobox.IndicatorGroup>\n </Combobox.Control>\n <Portal disabled={disablePortal || disablePortalFromContext}>\n <Combobox.Positioner>\n <BitkitSelectMenu\n collection={collection}\n emptyHelperText={emptyHelperText}\n emptyLabel={emptyLabel}\n isLoading={isLoading}\n onScrolledToBottom={onScrolledToBottom}\n size={size}\n variant=\"combobox\"\n >\n {children}\n </BitkitSelectMenu>\n </Combobox.Positioner>\n </Portal>\n </Combobox.Root>\n );\n};\n\nexport default withSubComponents(BitkitCombobox, { Action: BitkitSelectMenuAction });\n"],"mappings":"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"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import IconArrowNortheast from "../../icons/IconArrowNortheast.js";
|
|
1
2
|
import IconChevronDown from "../../icons/IconChevronDown.js";
|
|
2
3
|
import IconChevronUp from "../../icons/IconChevronUp.js";
|
|
3
4
|
import { ICON_COMPONENTS_MAP } from "../common/notificationMaps.js";
|
|
@@ -71,10 +72,11 @@ var BitkitNoteCard = forwardRef((props, ref) => {
|
|
|
71
72
|
}), action && (action.href !== void 0 ? /* @__PURE__ */ jsx(BitkitButton, {
|
|
72
73
|
css: [styles.actionArea, hasList && styles.actionAreaList],
|
|
73
74
|
href: action.href,
|
|
75
|
+
isExternal: action.isExternal || action.target === "_blank",
|
|
74
76
|
onClick: action.onClick,
|
|
75
|
-
rel: action.target === "_blank" ? "noopener noreferrer" : void 0,
|
|
76
77
|
size: "md",
|
|
77
78
|
state: action.state === "loading" ? void 0 : action.state,
|
|
79
|
+
suffixIcon: action.isExternal ? IconArrowNortheast : void 0,
|
|
78
80
|
target: action.target,
|
|
79
81
|
variant: "tertiary",
|
|
80
82
|
children: action.label
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BitkitNoteCard.js","names":[],"sources":["../../../lib/components/BitkitNoteCard/BitkitNoteCard.tsx"],"sourcesContent":["import { Box, type BoxProps } from '@chakra-ui/react/box';\nimport { Collapsible } from '@chakra-ui/react/collapsible';\nimport { useSlotRecipe } from '@chakra-ui/react/styled-system';\nimport { Text } from '@chakra-ui/react/text';\nimport { type ElementType, forwardRef, type ReactNode, useState } from 'react';\n\nimport { IconChevronDown, IconChevronUp } from '../../icons';\nimport { type NotificationVariant } from '../../theme/common/AlertAndToast.common';\nimport BitkitButton from '../BitkitButton/BitkitButton';\nimport BitkitLinkButton from '../BitkitLinkButton/BitkitLinkButton';\nimport BitkitList from '../BitkitList/BitkitList';\nimport { ICON_COMPONENTS_MAP, type NotificationAction } from '../common/notificationMaps';\n\n// ----- Props -----\n\nexport type BitkitNoteCardProps = Omit<BoxProps, 'children' | 'title'> & {\n action?: NotificationAction;\n isCollapsible?: boolean;\n /**\n * The message body. Rendered in a `div`, so block-level content — a `BitkitMarkdown`, a list,\n * a table — is valid here, not just phrasing content.\n */\n message?: ReactNode;\n messageList?: ReactNode[];\n status?: NotificationVariant;\n title?: string;\n};\n\n// ----- Component -----\n\nconst BitkitNoteCard = forwardRef<HTMLDivElement, BitkitNoteCardProps>((props, ref) => {\n const { action, css, isCollapsible = false, message, messageList, status = 'info', title, ...rest } = props;\n\n const recipe = useSlotRecipe({ key: 'noteCard' });\n const styles = recipe({ status });\n\n const [isOpen, setIsOpen] = useState(false);\n\n const IconComponent: ElementType = ICON_COMPONENTS_MAP[status];\n const isProgress = status === 'progress';\n const listItems = (messageList ?? []).filter(\n (item) => item !== null && item !== undefined && typeof item !== 'boolean',\n );\n const hasMessage = message !== null && message !== undefined && message !== '' && typeof message !== 'boolean';\n const hasList = listItems.length > 0;\n const hasToggle = isCollapsible && hasList;\n\n const list = hasList ? (\n <BitkitList css={styles.messageList} size=\"md\">\n {listItems.map((item, index) => (\n <BitkitList.Item key={index}>{item}</BitkitList.Item>\n ))}\n </BitkitList>\n ) : null;\n\n return (\n <Box ref={ref} css={[styles.root, css]} {...rest}>\n <Box css={styles.iconBar}>\n <Box css={styles.iconWrapper}>{isProgress ? <IconComponent size=\"lg\" /> : <IconComponent size=\"24\" />}</Box>\n </Box>\n <Box css={[styles.content, !action && { paddingInlineEnd: '24' }]}>\n <Box css={[styles.messageBlock, !title && { paddingBlockStart: '2' }]}>\n {title && <Text css={styles.title}>{title}</Text>}\n {hasMessage && (\n <Text as=\"div\" css={styles.message}>\n {message}\n </Text>\n )}\n {hasToggle ? (\n <Collapsible.Root\n css={styles.collapsible}\n onOpenChange={(details) => setIsOpen(details.open)}\n open={isOpen}\n unstyled\n >\n <Collapsible.Content css={styles.collapsibleContent}>{list}</Collapsible.Content>\n <Collapsible.Trigger asChild>\n <BitkitLinkButton css={styles.collapsibleTrigger} suffixIcon={isOpen ? IconChevronUp : IconChevronDown}>\n {isOpen ? 'Hide details' : 'Show details'}\n </BitkitLinkButton>\n </Collapsible.Trigger>\n </Collapsible.Root>\n ) : (\n list\n )}\n </Box>\n {action &&\n (action.href !== undefined ? (\n <BitkitButton\n css={[styles.actionArea, hasList && styles.actionAreaList]}\n href={action.href}\n
|
|
1
|
+
{"version":3,"file":"BitkitNoteCard.js","names":[],"sources":["../../../lib/components/BitkitNoteCard/BitkitNoteCard.tsx"],"sourcesContent":["import { Box, type BoxProps } from '@chakra-ui/react/box';\nimport { Collapsible } from '@chakra-ui/react/collapsible';\nimport { useSlotRecipe } from '@chakra-ui/react/styled-system';\nimport { Text } from '@chakra-ui/react/text';\nimport { type ElementType, forwardRef, type ReactNode, useState } from 'react';\n\nimport { IconArrowNortheast, IconChevronDown, IconChevronUp } from '../../icons';\nimport { type NotificationVariant } from '../../theme/common/AlertAndToast.common';\nimport BitkitButton from '../BitkitButton/BitkitButton';\nimport BitkitLinkButton from '../BitkitLinkButton/BitkitLinkButton';\nimport BitkitList from '../BitkitList/BitkitList';\nimport { ICON_COMPONENTS_MAP, type NotificationAction } from '../common/notificationMaps';\n\n// ----- Props -----\n\nexport type BitkitNoteCardProps = Omit<BoxProps, 'children' | 'title'> & {\n action?: NotificationAction;\n isCollapsible?: boolean;\n /**\n * The message body. Rendered in a `div`, so block-level content — a `BitkitMarkdown`, a list,\n * a table — is valid here, not just phrasing content.\n */\n message?: ReactNode;\n messageList?: ReactNode[];\n status?: NotificationVariant;\n title?: string;\n};\n\n// ----- Component -----\n\nconst BitkitNoteCard = forwardRef<HTMLDivElement, BitkitNoteCardProps>((props, ref) => {\n const { action, css, isCollapsible = false, message, messageList, status = 'info', title, ...rest } = props;\n\n const recipe = useSlotRecipe({ key: 'noteCard' });\n const styles = recipe({ status });\n\n const [isOpen, setIsOpen] = useState(false);\n\n const IconComponent: ElementType = ICON_COMPONENTS_MAP[status];\n const isProgress = status === 'progress';\n const listItems = (messageList ?? []).filter(\n (item) => item !== null && item !== undefined && typeof item !== 'boolean',\n );\n const hasMessage = message !== null && message !== undefined && message !== '' && typeof message !== 'boolean';\n const hasList = listItems.length > 0;\n const hasToggle = isCollapsible && hasList;\n\n const list = hasList ? (\n <BitkitList css={styles.messageList} size=\"md\">\n {listItems.map((item, index) => (\n <BitkitList.Item key={index}>{item}</BitkitList.Item>\n ))}\n </BitkitList>\n ) : null;\n\n return (\n <Box ref={ref} css={[styles.root, css]} {...rest}>\n <Box css={styles.iconBar}>\n <Box css={styles.iconWrapper}>{isProgress ? <IconComponent size=\"lg\" /> : <IconComponent size=\"24\" />}</Box>\n </Box>\n <Box css={[styles.content, !action && { paddingInlineEnd: '24' }]}>\n <Box css={[styles.messageBlock, !title && { paddingBlockStart: '2' }]}>\n {title && <Text css={styles.title}>{title}</Text>}\n {hasMessage && (\n <Text as=\"div\" css={styles.message}>\n {message}\n </Text>\n )}\n {hasToggle ? (\n <Collapsible.Root\n css={styles.collapsible}\n onOpenChange={(details) => setIsOpen(details.open)}\n open={isOpen}\n unstyled\n >\n <Collapsible.Content css={styles.collapsibleContent}>{list}</Collapsible.Content>\n <Collapsible.Trigger asChild>\n <BitkitLinkButton css={styles.collapsibleTrigger} suffixIcon={isOpen ? IconChevronUp : IconChevronDown}>\n {isOpen ? 'Hide details' : 'Show details'}\n </BitkitLinkButton>\n </Collapsible.Trigger>\n </Collapsible.Root>\n ) : (\n list\n )}\n </Box>\n {action &&\n (action.href !== undefined ? (\n <BitkitButton\n css={[styles.actionArea, hasList && styles.actionAreaList]}\n href={action.href}\n isExternal={action.isExternal || action.target === '_blank'}\n onClick={action.onClick}\n size=\"md\"\n // See the known gap documented on `NotificationAction.state`: BitkitButton's anchor\n // variant has no loading state, so only `disabled` carries over here.\n state={action.state === 'loading' ? undefined : action.state}\n suffixIcon={action.isExternal ? IconArrowNortheast : undefined}\n target={action.target}\n variant=\"tertiary\"\n >\n {action.label}\n </BitkitButton>\n ) : (\n <BitkitButton\n css={[styles.actionArea, hasList && styles.actionAreaList]}\n onClick={action.onClick}\n size=\"md\"\n state={action.state}\n variant=\"tertiary\"\n marginBlock=\"4\"\n >\n {action.label}\n </BitkitButton>\n ))}\n </Box>\n </Box>\n );\n});\n\nBitkitNoteCard.displayName = 'BitkitNoteCard';\n\nexport default BitkitNoteCard;\n"],"mappings":";;;;;;;;;;;;;;AA8BA,IAAM,iBAAiB,YAAiD,OAAO,QAAQ;CACrF,MAAM,EAAE,QAAQ,KAAK,gBAAgB,OAAO,SAAS,aAAa,SAAS,QAAQ,OAAO,GAAG,SAAS;CAGtG,MAAM,SADS,cAAc,EAAE,KAAK,WAAW,CAChC,CAAA,CAAO,EAAE,OAAO,CAAC;CAEhC,MAAM,CAAC,QAAQ,aAAa,SAAS,KAAK;CAE1C,MAAM,gBAA6B,oBAAoB;CACvD,MAAM,aAAa,WAAW;CAC9B,MAAM,aAAa,eAAe,CAAC,EAAA,CAAG,QACnC,SAAS,SAAS,QAAQ,SAAS,KAAA,KAAa,OAAO,SAAS,SACnE;CACA,MAAM,aAAa,YAAY,QAAQ,YAAY,KAAA,KAAa,YAAY,MAAM,OAAO,YAAY;CACrG,MAAM,UAAU,UAAU,SAAS;CACnC,MAAM,YAAY,iBAAiB;CAEnC,MAAM,OAAO,UACX,oBAAC,oBAAD;EAAY,KAAK,OAAO;EAAa,MAAK;EACvC,UAAA,UAAU,KAAK,MAAM,UACpB,oBAAC,mBAAW,MAAZ,EAAA,UAA8B,KAAsB,GAA9B,KAA8B,CACrD;CACS,CAAA,IACV;CAEJ,OACE,qBAAC,KAAD;EAAU;EAAK,KAAK,CAAC,OAAO,MAAM,GAAG;EAAG,GAAI;EAA5C,UAAA,CACE,oBAAC,KAAD;GAAK,KAAK,OAAO;GACf,UAAA,oBAAC,KAAD;IAAK,KAAK,OAAO;IAAc,UAAA,aAAa,oBAAC,eAAD,EAAe,MAAK,KAAM,CAAA,IAAI,oBAAC,eAAD,EAAe,MAAK,KAAM,CAAA;GAAO,CAAA;EACxG,CAAA,GACL,qBAAC,KAAD;GAAK,KAAK,CAAC,OAAO,SAAS,CAAC,UAAU,EAAE,kBAAkB,KAAK,CAAC;GAAhE,UAAA,CACE,qBAAC,KAAD;IAAK,KAAK,CAAC,OAAO,cAAc,CAAC,SAAS,EAAE,mBAAmB,IAAI,CAAC;IAApE,UAAA;KACG,SAAS,oBAAC,MAAD;MAAM,KAAK,OAAO;MAAQ,UAAA;KAAY,CAAA;KAC/C,cACC,oBAAC,MAAD;MAAM,IAAG;MAAM,KAAK,OAAO;MACxB,UAAA;KACG,CAAA;KAEP,YACC,qBAAC,YAAY,MAAb;MACE,KAAK,OAAO;MACZ,eAAe,YAAY,UAAU,QAAQ,IAAI;MACjD,MAAM;MACN,UAAA;MAJF,UAAA,CAME,oBAAC,YAAY,SAAb;OAAqB,KAAK,OAAO;OAAqB,UAAA;MAA0B,CAAA,GAChF,oBAAC,YAAY,SAAb;OAAqB,SAAA;OACnB,UAAA,oBAAC,kBAAD;QAAkB,KAAK,OAAO;QAAoB,YAAY,SAAS,gBAAgB;QACpF,UAAA,SAAS,iBAAiB;OACX,CAAA;MACC,CAAA,CACL;KAElB,CAAA,IAAA;IAEC;GACJ,CAAA,GAAA,WACE,OAAO,SAAS,KAAA,IACf,oBAAC,cAAD;IACE,KAAK,CAAC,OAAO,YAAY,WAAW,OAAO,cAAc;IACzD,MAAM,OAAO;IACb,YAAY,OAAO,cAAc,OAAO,WAAW;IACnD,SAAS,OAAO;IAChB,MAAK;IAGL,OAAO,OAAO,UAAU,YAAY,KAAA,IAAY,OAAO;IACvD,YAAY,OAAO,aAAa,qBAAqB,KAAA;IACrD,QAAQ,OAAO;IACf,SAAQ;IAEP,UAAA,OAAO;GACI,CAAA,IAEd,oBAAC,cAAD;IACE,KAAK,CAAC,OAAO,YAAY,WAAW,OAAO,cAAc;IACzD,SAAS,OAAO;IAChB,MAAK;IACL,OAAO,OAAO;IACd,SAAQ;IACR,aAAY;IAEX,UAAA,OAAO;GACI,CAAA,EAEf;EACF,CAAA,CAAA;;AAET,CAAC;AAED,eAAe,cAAc"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import IconArrowNortheast from "../../icons/IconArrowNortheast.js";
|
|
1
2
|
import BitkitCloseButton from "../BitkitCloseButton/BitkitCloseButton.js";
|
|
2
3
|
import BitkitColorButton from "../BitkitColorButton/BitkitColorButton.js";
|
|
3
4
|
import BitkitLink from "../BitkitLink/BitkitLink.js";
|
|
@@ -30,9 +31,10 @@ var BitkitRibbon = forwardRef((props, ref) => {
|
|
|
30
31
|
}), action && (action.href !== void 0 ? /* @__PURE__ */ jsx(BitkitColorButton, {
|
|
31
32
|
colorVariant,
|
|
32
33
|
href: action.href,
|
|
33
|
-
isExternal: action.target === "_blank",
|
|
34
|
+
isExternal: action.isExternal || action.target === "_blank",
|
|
34
35
|
onClick: action.onClick,
|
|
35
36
|
state: action.state,
|
|
37
|
+
suffixIcon: action.isExternal ? IconArrowNortheast : void 0,
|
|
36
38
|
target: action.target,
|
|
37
39
|
children: action.label
|
|
38
40
|
}) : /* @__PURE__ */ jsx(BitkitColorButton, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BitkitRibbon.js","names":[],"sources":["../../../lib/components/BitkitRibbon/BitkitRibbon.tsx"],"sourcesContent":["import { Box, type BoxProps } from '@chakra-ui/react/box';\nimport { chakra, useSlotRecipe } from '@chakra-ui/react/styled-system';\nimport { forwardRef } from 'react';\n\nimport BitkitCloseButton from '../BitkitCloseButton/BitkitCloseButton';\nimport BitkitColorButton from '../BitkitColorButton/BitkitColorButton';\nimport BitkitLink from '../BitkitLink/BitkitLink';\nimport { type NotificationAction } from '../common/notificationMaps';\n\nexport type RibbonActionProps = NotificationAction;\n\nexport type RibbonLinkProps = {\n href?: string;\n label: string;\n target?: HTMLAnchorElement['target'];\n};\n\nexport interface BitkitRibbonProps extends Omit<BoxProps, 'colorPalette'> {\n action?: RibbonActionProps;\n colorVariant?: 'blue' | 'green' | 'purple' | 'red' | 'yellow';\n link?: RibbonLinkProps;\n onDismiss?: () => void;\n title?: string;\n}\n\nconst BitkitRibbon = forwardRef<HTMLDivElement, BitkitRibbonProps>((props, ref) => {\n const { action, children, colorVariant = 'blue', css, link, onDismiss, title, ...rest } = props;\n\n const recipe = useSlotRecipe({ key: 'ribbon' });\n const styles = recipe({ colorVariant });\n\n return (\n <Box ref={ref} css={[styles.root, css]} {...rest}>\n <chakra.div css={styles.content}>\n <chakra.div css={styles.textBlock}>\n {title && <chakra.strong>{title}</chakra.strong>}\n <chakra.span>{children}</chakra.span>\n {link && (\n <BitkitLink href={link.href} target={link.target} textDecoration=\"underline\">\n {link.label}\n </BitkitLink>\n )}\n </chakra.div>\n {action &&\n (action.href !== undefined ? (\n <BitkitColorButton\n colorVariant={colorVariant}\n href={action.href}\n isExternal={action.target === '_blank'}\n onClick={action.onClick}\n state={action.state}\n target={action.target}\n >\n {action.label}\n </BitkitColorButton>\n ) : (\n <BitkitColorButton colorVariant={colorVariant} onClick={action.onClick} state={action.state}>\n {action.label}\n </BitkitColorButton>\n ))}\n </chakra.div>\n {onDismiss && (\n <BitkitCloseButton colorVariant={colorVariant} css={styles.closeButton} size=\"sm\" onClick={onDismiss} />\n )}\n </Box>\n );\n});\n\nBitkitRibbon.displayName = 'BitkitRibbon';\n\nexport default BitkitRibbon;\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"BitkitRibbon.js","names":[],"sources":["../../../lib/components/BitkitRibbon/BitkitRibbon.tsx"],"sourcesContent":["import { Box, type BoxProps } from '@chakra-ui/react/box';\nimport { chakra, useSlotRecipe } from '@chakra-ui/react/styled-system';\nimport { forwardRef } from 'react';\n\nimport { IconArrowNortheast } from '../../icons';\nimport BitkitCloseButton from '../BitkitCloseButton/BitkitCloseButton';\nimport BitkitColorButton from '../BitkitColorButton/BitkitColorButton';\nimport BitkitLink from '../BitkitLink/BitkitLink';\nimport { type NotificationAction } from '../common/notificationMaps';\n\nexport type RibbonActionProps = NotificationAction;\n\nexport type RibbonLinkProps = {\n href?: string;\n label: string;\n target?: HTMLAnchorElement['target'];\n};\n\nexport interface BitkitRibbonProps extends Omit<BoxProps, 'colorPalette'> {\n action?: RibbonActionProps;\n colorVariant?: 'blue' | 'green' | 'purple' | 'red' | 'yellow';\n link?: RibbonLinkProps;\n onDismiss?: () => void;\n title?: string;\n}\n\nconst BitkitRibbon = forwardRef<HTMLDivElement, BitkitRibbonProps>((props, ref) => {\n const { action, children, colorVariant = 'blue', css, link, onDismiss, title, ...rest } = props;\n\n const recipe = useSlotRecipe({ key: 'ribbon' });\n const styles = recipe({ colorVariant });\n\n return (\n <Box ref={ref} css={[styles.root, css]} {...rest}>\n <chakra.div css={styles.content}>\n <chakra.div css={styles.textBlock}>\n {title && <chakra.strong>{title}</chakra.strong>}\n <chakra.span>{children}</chakra.span>\n {link && (\n <BitkitLink href={link.href} target={link.target} textDecoration=\"underline\">\n {link.label}\n </BitkitLink>\n )}\n </chakra.div>\n {action &&\n (action.href !== undefined ? (\n <BitkitColorButton\n colorVariant={colorVariant}\n href={action.href}\n isExternal={action.isExternal || action.target === '_blank'}\n onClick={action.onClick}\n state={action.state}\n suffixIcon={action.isExternal ? IconArrowNortheast : undefined}\n target={action.target}\n >\n {action.label}\n </BitkitColorButton>\n ) : (\n <BitkitColorButton colorVariant={colorVariant} onClick={action.onClick} state={action.state}>\n {action.label}\n </BitkitColorButton>\n ))}\n </chakra.div>\n {onDismiss && (\n <BitkitCloseButton colorVariant={colorVariant} css={styles.closeButton} size=\"sm\" onClick={onDismiss} />\n )}\n </Box>\n );\n});\n\nBitkitRibbon.displayName = 'BitkitRibbon';\n\nexport default BitkitRibbon;\n"],"mappings":";;;;;;;;;AA0BA,IAAM,eAAe,YAA+C,OAAO,QAAQ;CACjF,MAAM,EAAE,QAAQ,UAAU,eAAe,QAAQ,KAAK,MAAM,WAAW,OAAO,GAAG,SAAS;CAG1F,MAAM,SADS,cAAc,EAAE,KAAK,SAAS,CAC9B,CAAA,CAAO,EAAE,aAAa,CAAC;CAEtC,OACE,qBAAC,KAAD;EAAU;EAAK,KAAK,CAAC,OAAO,MAAM,GAAG;EAAG,GAAI;EAA5C,UAAA,CACE,qBAAC,OAAO,KAAR;GAAY,KAAK,OAAO;GAAxB,UAAA,CACE,qBAAC,OAAO,KAAR;IAAY,KAAK,OAAO;IAAxB,UAAA;KACG,SAAS,oBAAC,OAAO,QAAR,EAAA,UAAgB,MAAqB,CAAA;KAC/C,oBAAC,OAAO,MAAR,EAAc,SAAsB,CAAA;KACnC,QACC,oBAAC,YAAD;MAAY,MAAM,KAAK;MAAM,QAAQ,KAAK;MAAQ,gBAAe;MAC9D,UAAA,KAAK;KACI,CAAA;IAEJ;GACX,CAAA,GAAA,WACE,OAAO,SAAS,KAAA,IACf,oBAAC,mBAAD;IACgB;IACd,MAAM,OAAO;IACb,YAAY,OAAO,cAAc,OAAO,WAAW;IACnD,SAAS,OAAO;IAChB,OAAO,OAAO;IACd,YAAY,OAAO,aAAa,qBAAqB,KAAA;IACrD,QAAQ,OAAO;IAEd,UAAA,OAAO;GACS,CAAA,IAEnB,oBAAC,mBAAD;IAAiC;IAAc,SAAS,OAAO;IAAS,OAAO,OAAO;IACnF,UAAA,OAAO;GACS,CAAA,EAEb;EACX,CAAA,GAAA,aACC,oBAAC,mBAAD;GAAiC;GAAc,KAAK,OAAO;GAAa,MAAK;GAAK,SAAS;EAAY,CAAA,CAEtG;;AAET,CAAC;AAED,aAAa,cAAc"}
|
|
@@ -22,17 +22,16 @@ var SelectValue = ({ placeholder, state, size }) => {
|
|
|
22
22
|
const Icon = items[0]?.icon;
|
|
23
23
|
const avatar = items[0]?.avatar;
|
|
24
24
|
const label = items[0]?.label;
|
|
25
|
-
const showAvatar = avatar !== void 0;
|
|
26
25
|
const { avatarSize, iconSize } = selectSizeMap[size];
|
|
27
26
|
const styles = useSlotRecipe({ key: "select" })({ size });
|
|
28
27
|
return items[0] ? /* @__PURE__ */ jsxs(Select.ValueText, {
|
|
29
28
|
placeholder: placeholder ?? (state === "readOnly" ? "(not selected)" : "Select an option"),
|
|
30
|
-
children: [
|
|
29
|
+
children: [avatar ? /* @__PURE__ */ jsx(Box, {
|
|
31
30
|
"data-slot": "avatar",
|
|
32
31
|
css: styles.avatar,
|
|
33
32
|
children: /* @__PURE__ */ jsx(BitkitAvatar, {
|
|
34
33
|
variant: "image",
|
|
35
|
-
src: avatar
|
|
34
|
+
src: avatar,
|
|
36
35
|
name: label,
|
|
37
36
|
size: avatarSize
|
|
38
37
|
})
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BitkitSelect.js","names":[],"sources":["../../../lib/components/BitkitSelect/BitkitSelect.tsx"],"sourcesContent":["import { Box } from '@chakra-ui/react/box';\nimport { createListCollection } from '@chakra-ui/react/collection';\nimport { Portal } from '@chakra-ui/react/portal';\nimport { Select, type SelectRootProps, type SelectTriggerProps, useSelectContext } from '@chakra-ui/react/select';\nimport { useSlotRecipe } from '@chakra-ui/react/styled-system';\nimport { Text } from '@chakra-ui/react/text';\nimport { forwardRef, useContext } from 'react';\n\nimport { IconErrorCircleFilled, IconWarningYellow } from '../../icons';\nimport AssetSelectChevron from '../../utilities/AssetSelectChevron.tsx';\nimport BitkitPortalContext from '../../utilities/BitkitPortalContext';\nimport { withSubComponents } from '../../utilities/withSubComponents.ts';\nimport BitkitAvatar from '../BitkitAvatar/BitkitAvatar';\nimport BitkitField, { type BitkitFieldProps } from '../BitkitField/BitkitField.tsx';\nimport BitkitSelectMenu, {\n type BitkitSelectMenuEmptyStateProps,\n type BitkitSelectMenuItemProps,\n type BitkitSelectMenuScrollProps,\n type BitkitSelectMenuSearchProps,\n} from '../BitkitSelectMenu/BitkitSelectMenu.tsx';\nimport BitkitSelectMenuAction, {\n type BitkitSelectMenuActionChild,\n} from '../BitkitSelectMenu/BitkitSelectMenuAction.tsx';\nimport { selectSizeMap } from '../BitkitSelectMenu/SelectMenuShell.tsx';\n\nexport type BitkitSelectTriggerProps = SelectTriggerProps;\n\nexport type BitkitSelectProps = Omit<BitkitFieldProps, 'children' | 'onChange' | 'state'> & {\n children?: BitkitSelectMenuActionChild;\n defaultValue?: string;\n isLoading?: boolean;\n items: Array<BitkitSelectMenuItemProps>;\n onValueChange?: (newVal: string) => void;\n placeholder?: string;\n disablePortal?: boolean;\n selectProps?: Omit<SelectRootProps, 'collection' | 'defaultValue' | 'onValueChange' | 'size' | 'value'>;\n size?: 'md' | 'lg';\n state?: 'disabled' | 'error' | 'readOnly' | 'warning';\n triggerProps?: BitkitSelectTriggerProps;\n value?: string;\n} & BitkitSelectMenuSearchProps &\n BitkitSelectMenuScrollProps &\n BitkitSelectMenuEmptyStateProps;\n\ntype SelectValueProps = {\n placeholder?: string;\n size: NonNullable<BitkitSelectProps['size']>;\n state?: BitkitSelectProps['state'];\n};\n\nconst SelectValue = ({ placeholder, state, size }: SelectValueProps) => {\n const select = useSelectContext();\n const items = select.selectedItems as Array<BitkitSelectMenuItemProps>;\n\n const Icon = items[0]?.icon;\n const avatar = items[0]?.avatar;\n const label = items[0]?.label;\n
|
|
1
|
+
{"version":3,"file":"BitkitSelect.js","names":[],"sources":["../../../lib/components/BitkitSelect/BitkitSelect.tsx"],"sourcesContent":["import { Box } from '@chakra-ui/react/box';\nimport { createListCollection } from '@chakra-ui/react/collection';\nimport { Portal } from '@chakra-ui/react/portal';\nimport { Select, type SelectRootProps, type SelectTriggerProps, useSelectContext } from '@chakra-ui/react/select';\nimport { useSlotRecipe } from '@chakra-ui/react/styled-system';\nimport { Text } from '@chakra-ui/react/text';\nimport { forwardRef, useContext } from 'react';\n\nimport { IconErrorCircleFilled, IconWarningYellow } from '../../icons';\nimport AssetSelectChevron from '../../utilities/AssetSelectChevron.tsx';\nimport BitkitPortalContext from '../../utilities/BitkitPortalContext';\nimport { withSubComponents } from '../../utilities/withSubComponents.ts';\nimport BitkitAvatar from '../BitkitAvatar/BitkitAvatar';\nimport BitkitField, { type BitkitFieldProps } from '../BitkitField/BitkitField.tsx';\nimport BitkitSelectMenu, {\n type BitkitSelectMenuEmptyStateProps,\n type BitkitSelectMenuItemProps,\n type BitkitSelectMenuScrollProps,\n type BitkitSelectMenuSearchProps,\n} from '../BitkitSelectMenu/BitkitSelectMenu.tsx';\nimport BitkitSelectMenuAction, {\n type BitkitSelectMenuActionChild,\n} from '../BitkitSelectMenu/BitkitSelectMenuAction.tsx';\nimport { selectSizeMap } from '../BitkitSelectMenu/SelectMenuShell.tsx';\n\nexport type BitkitSelectTriggerProps = SelectTriggerProps;\n\nexport type BitkitSelectProps = Omit<BitkitFieldProps, 'children' | 'onChange' | 'state'> & {\n children?: BitkitSelectMenuActionChild;\n defaultValue?: string;\n isLoading?: boolean;\n items: Array<BitkitSelectMenuItemProps>;\n onValueChange?: (newVal: string) => void;\n placeholder?: string;\n disablePortal?: boolean;\n selectProps?: Omit<SelectRootProps, 'collection' | 'defaultValue' | 'onValueChange' | 'size' | 'value'>;\n size?: 'md' | 'lg';\n state?: 'disabled' | 'error' | 'readOnly' | 'warning';\n triggerProps?: BitkitSelectTriggerProps;\n value?: string;\n} & BitkitSelectMenuSearchProps &\n BitkitSelectMenuScrollProps &\n BitkitSelectMenuEmptyStateProps;\n\ntype SelectValueProps = {\n placeholder?: string;\n size: NonNullable<BitkitSelectProps['size']>;\n state?: BitkitSelectProps['state'];\n};\n\nconst SelectValue = ({ placeholder, state, size }: SelectValueProps) => {\n const select = useSelectContext();\n const items = select.selectedItems as Array<BitkitSelectMenuItemProps>;\n\n const Icon = items[0]?.icon;\n const avatar = items[0]?.avatar;\n const label = items[0]?.label;\n\n const { avatarSize, iconSize } = selectSizeMap[size];\n\n const recipe = useSlotRecipe({ key: 'select' });\n const styles = recipe({ size });\n\n return items[0] ? (\n <Select.ValueText placeholder={placeholder ?? (state === 'readOnly' ? '(not selected)' : 'Select an option')}>\n {avatar ? (\n // Same avatar handling as the menu item (see BitkitSelectMenu.tsx). The `data-slot`\n // lets the trigger recipe shrink its vertical padding so the avatar row keeps the\n // 48/40 trigger height (see the `&:has([data-slot=\"avatar\"])` rule in Select.recipe.ts).\n <Box data-slot=\"avatar\" css={styles.avatar}>\n <BitkitAvatar variant=\"image\" src={avatar} name={label} size={avatarSize} />\n </Box>\n ) : (\n Icon && <Icon css={styles.valueIcon} size={iconSize} flexShrink={0} />\n )}\n <Text as=\"span\" overflow=\"hidden\" textOverflow=\"ellipsis\" whiteSpace=\"nowrap\" minWidth=\"0\">\n {label}\n </Text>\n </Select.ValueText>\n ) : (\n <Select.ValueText\n placeholder={placeholder ?? (state === 'readOnly' ? '(not selected)' : 'Select an option')}\n display=\"block\"\n overflow=\"hidden\"\n textOverflow=\"ellipsis\"\n whiteSpace=\"nowrap\"\n />\n );\n};\n\nconst BitkitSelect = forwardRef<HTMLDivElement, BitkitSelectProps>((props: BitkitSelectProps, ref) => {\n const {\n children,\n defaultValue,\n emptyHelperText,\n emptyLabel,\n isLoading,\n items,\n onScrolledToBottom,\n onSearchChange,\n onValueChange,\n placeholder,\n disablePortal,\n searchValue,\n selectProps,\n size = 'lg',\n state,\n triggerProps,\n value,\n ...fieldProps\n } = props;\n\n const collection = createListCollection({\n items,\n groupBy: (item) => item.group || '',\n isItemDisabled: (item) => !!item.disabled,\n });\n\n const { disablePortal: disablePortalFromContext } = useContext(BitkitPortalContext);\n const isInvalid = state === 'error' || !!fieldProps.errorText;\n\n return (\n <BitkitField ref={ref} state={state} {...fieldProps}>\n <Select.Root\n collection={collection}\n size={size}\n {...selectProps}\n defaultValue={defaultValue ? [defaultValue] : undefined}\n disabled={state === 'disabled'}\n hasStatusIcon={state === 'error' || state === 'warning'}\n invalid={isInvalid}\n onValueChange={(newVal) => onValueChange?.(newVal.value[0])}\n readOnly={state === 'readOnly'}\n // Bypass Zag's isScrollable(contentEl) gate — our Content is overflow:hidden flex\n // column, so the real scroll container is itemList. See BitkitMultiselect for why.\n scrollToIndexFn={({ getElement }) => getElement()?.scrollIntoView({ block: 'nearest' })}\n // Stay controlled whenever `value` is provided — including `''` (\"nothing selected\"). A\n // truthiness check (`value ? … : undefined`) passes `undefined` for `''`, which flips\n // Select.Root to uncontrolled: it then ignores `value`, so resets don't take and\n // onValueChange stops firing when the same option is re-selected.\n value={value === undefined ? undefined : value ? [value] : []}\n >\n <Select.Control className=\"group\">\n <Select.HiddenSelect />\n <Select.Trigger {...triggerProps} aria-readonly={state === 'readOnly' || undefined}>\n <SelectValue placeholder={placeholder} size={size} state={state} />\n </Select.Trigger>\n <Select.IndicatorGroup>\n {state === 'error' && (\n <Select.Indicator>\n <IconErrorCircleFilled size={size === 'lg' ? '24' : '16'} color=\"icon/negative\" />\n </Select.Indicator>\n )}\n {state === 'warning' && (\n <Select.Indicator>\n <IconWarningYellow size={size === 'lg' ? '24' : '16'} />\n </Select.Indicator>\n )}\n <Select.Indicator asChild>\n <AssetSelectChevron />\n </Select.Indicator>\n </Select.IndicatorGroup>\n </Select.Control>\n <Portal disabled={disablePortal || disablePortalFromContext}>\n <Select.Positioner>\n <BitkitSelectMenu\n collection={collection}\n emptyHelperText={emptyHelperText}\n emptyLabel={emptyLabel}\n isLoading={isLoading}\n onScrolledToBottom={onScrolledToBottom}\n onSearchChange={onSearchChange}\n searchValue={searchValue}\n size={size}\n >\n {children}\n </BitkitSelectMenu>\n </Select.Positioner>\n </Portal>\n </Select.Root>\n </BitkitField>\n );\n});\n\nBitkitSelect.displayName = 'BitkitSelect';\n\nexport default withSubComponents(BitkitSelect, { Action: BitkitSelectMenuAction });\n"],"mappings":"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"}
|
|
@@ -19,10 +19,7 @@ export type BitkitSelectMenuItemProps = {
|
|
|
19
19
|
label: string;
|
|
20
20
|
group?: string;
|
|
21
21
|
icon?: BitkitIconComponent;
|
|
22
|
-
|
|
23
|
-
* with the initials of `label`; omitting the prop renders no avatar and leaves the row's
|
|
24
|
-
* leading slot to `icon`. */
|
|
25
|
-
avatar?: string | null;
|
|
22
|
+
avatar?: string;
|
|
26
23
|
helperText?: ReactNode;
|
|
27
24
|
/** A `BitkitBadge` after the label. Forced to `colorVariant="neutral"` on a disabled item. */
|
|
28
25
|
badge?: ReactElement<BitkitBadgeProps>;
|
|
@@ -37,7 +37,6 @@ var BitkitSelectMenu = forwardRef((props, ref) => {
|
|
|
37
37
|
BitkitSelectMenu.displayName = "BitkitSelectMenu";
|
|
38
38
|
var SelectMenuItem = ({ NS, avatarSize, item, iconSize, spinnerSize, styles }) => {
|
|
39
39
|
const Icon = item.icon;
|
|
40
|
-
const showAvatar = item.avatar !== void 0;
|
|
41
40
|
if (item.loading) return /* @__PURE__ */ jsxs(Box, {
|
|
42
41
|
css: styles.item,
|
|
43
42
|
children: [/* @__PURE__ */ jsx(Spinner, {
|
|
@@ -53,12 +52,12 @@ var SelectMenuItem = ({ NS, avatarSize, item, iconSize, spinnerSize, styles }) =
|
|
|
53
52
|
css: styles.item,
|
|
54
53
|
item,
|
|
55
54
|
children: [
|
|
56
|
-
|
|
55
|
+
item.avatar && /* @__PURE__ */ jsx(Box, {
|
|
57
56
|
"data-slot": "avatar",
|
|
58
57
|
css: styles.avatar,
|
|
59
58
|
children: /* @__PURE__ */ jsx(BitkitAvatar, {
|
|
60
59
|
variant: "image",
|
|
61
|
-
src: item.avatar
|
|
60
|
+
src: item.avatar,
|
|
62
61
|
name: item.label,
|
|
63
62
|
size: avatarSize
|
|
64
63
|
})
|
|
@@ -69,7 +68,7 @@ var SelectMenuItem = ({ NS, avatarSize, item, iconSize, spinnerSize, styles }) =
|
|
|
69
68
|
display: "flex",
|
|
70
69
|
alignItems: "center",
|
|
71
70
|
gap: "8",
|
|
72
|
-
children: [Icon && !
|
|
71
|
+
children: [Icon && !item.avatar && /* @__PURE__ */ jsx(Icon, {
|
|
73
72
|
css: styles.itemIcon,
|
|
74
73
|
size: iconSize
|
|
75
74
|
}), /* @__PURE__ */ jsx(Text, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BitkitSelectMenu.js","names":[],"sources":["../../../lib/components/BitkitSelectMenu/BitkitSelectMenu.tsx"],"sourcesContent":["import { Box } from '@chakra-ui/react/box';\nimport { type ListCollection } from '@chakra-ui/react/collection';\nimport { Combobox } from '@chakra-ui/react/combobox';\nimport { Select, type SelectContentProps } from '@chakra-ui/react/select';\nimport { Spinner } from '@chakra-ui/react/spinner';\nimport { type SystemStyleObject, useSlotRecipe } from '@chakra-ui/react/styled-system';\nimport { Text } from '@chakra-ui/react/text';\nimport { cloneElement, forwardRef, type ReactElement, type ReactNode } from 'react';\n\nimport { type BitkitIconComponent, IconCheck } from '../../icons';\nimport BitkitAvatar from '../BitkitAvatar/BitkitAvatar';\nimport { type BitkitBadgeProps } from '../BitkitBadge/BitkitBadge';\nimport { type BitkitSelectMenuActionChild } from './BitkitSelectMenuAction';\nimport { SelectMenuShell, selectSizeMap, type SelectSizes } from './SelectMenuShell';\n\nexport { type BitkitSelectMenuActionProps } from './BitkitSelectMenuAction';\n\nexport type BitkitSelectMenuSearchProps = {\n /** When provided, a search input is rendered above the menu items. `searchValue` is required alongside. */\n onSearchChange?: (searchText: string) => void;\n searchValue?: string;\n};\n\nexport type BitkitSelectMenuScrollProps = {\n /** Fires when the item list reaches its end. Position only: keep your own loading and end-of-list guards. */\n onScrolledToBottom?: () => void;\n};\n\nexport type BitkitSelectMenuItemProps = {\n value: string;\n label: string;\n group?: string;\n icon?: BitkitIconComponent;\n
|
|
1
|
+
{"version":3,"file":"BitkitSelectMenu.js","names":[],"sources":["../../../lib/components/BitkitSelectMenu/BitkitSelectMenu.tsx"],"sourcesContent":["import { Box } from '@chakra-ui/react/box';\nimport { type ListCollection } from '@chakra-ui/react/collection';\nimport { Combobox } from '@chakra-ui/react/combobox';\nimport { Select, type SelectContentProps } from '@chakra-ui/react/select';\nimport { Spinner } from '@chakra-ui/react/spinner';\nimport { type SystemStyleObject, useSlotRecipe } from '@chakra-ui/react/styled-system';\nimport { Text } from '@chakra-ui/react/text';\nimport { cloneElement, forwardRef, type ReactElement, type ReactNode } from 'react';\n\nimport { type BitkitIconComponent, IconCheck } from '../../icons';\nimport BitkitAvatar from '../BitkitAvatar/BitkitAvatar';\nimport { type BitkitBadgeProps } from '../BitkitBadge/BitkitBadge';\nimport { type BitkitSelectMenuActionChild } from './BitkitSelectMenuAction';\nimport { SelectMenuShell, selectSizeMap, type SelectSizes } from './SelectMenuShell';\n\nexport { type BitkitSelectMenuActionProps } from './BitkitSelectMenuAction';\n\nexport type BitkitSelectMenuSearchProps = {\n /** When provided, a search input is rendered above the menu items. `searchValue` is required alongside. */\n onSearchChange?: (searchText: string) => void;\n searchValue?: string;\n};\n\nexport type BitkitSelectMenuScrollProps = {\n /** Fires when the item list reaches its end. Position only: keep your own loading and end-of-list guards. */\n onScrolledToBottom?: () => void;\n};\n\nexport type BitkitSelectMenuItemProps = {\n value: string;\n label: string;\n group?: string;\n icon?: BitkitIconComponent;\n avatar?: string;\n helperText?: ReactNode;\n /** A `BitkitBadge` after the label. Forced to `colorVariant=\"neutral\"` on a disabled item. */\n badge?: ReactElement<BitkitBadgeProps>;\n disabled?: boolean;\n loading?: boolean;\n};\n\nexport type BitkitSelectMenuEmptyStateProps = {\n emptyLabel?: string;\n emptyHelperText?: string;\n};\n\nexport type BitkitSelectMenuProps = {\n children?: BitkitSelectMenuActionChild;\n collection: ListCollection<BitkitSelectMenuItemProps>;\n /** Forwarded to the underlying Select/Combobox.Content. Kept for internal callers\n * (e.g. BitkitCalendar's in-grid selects that need to tweak max-height / width). The\n * public components (BitkitSelect, BitkitCombobox) do not expose this escape hatch. */\n contentProps?: SelectContentProps;\n isLoading?: boolean;\n size?: 'md' | 'lg';\n variant?: 'select' | 'combobox';\n} & BitkitSelectMenuSearchProps &\n BitkitSelectMenuScrollProps &\n BitkitSelectMenuEmptyStateProps;\n\nconst BitkitSelectMenu = forwardRef<HTMLDivElement, BitkitSelectMenuProps>((props, ref) => {\n const { children, collection, variant = 'select', size, ...shellProps } = props;\n const NS = variant === 'combobox' ? Combobox : Select;\n const recipe = useSlotRecipe({ key: 'select' });\n const styles = recipe({ size });\n const { avatarSize, iconSize, spinnerSize } = selectSizeMap[size ?? 'lg'];\n\n return (\n <SelectMenuShell\n NS={NS}\n collection={collection}\n contentRef={ref}\n iconSize={iconSize}\n renderItem={(item) => (\n <SelectMenuItem\n key={item.value}\n NS={NS}\n avatarSize={avatarSize}\n item={item}\n iconSize={iconSize}\n spinnerSize={spinnerSize}\n styles={styles}\n />\n )}\n spinnerSize={spinnerSize}\n styles={styles}\n {...shellProps}\n >\n {children}\n </SelectMenuShell>\n );\n});\n\nBitkitSelectMenu.displayName = 'BitkitSelectMenu';\n\ntype SelectMenuItemRenderProps = {\n NS: typeof Select | typeof Combobox;\n avatarSize: SelectSizes['avatarSize'];\n item: BitkitSelectMenuItemProps;\n iconSize: SelectSizes['iconSize'];\n spinnerSize: SelectSizes['spinnerSize'];\n styles: Record<string, SystemStyleObject>;\n};\n\nconst SelectMenuItem = ({ NS, avatarSize, item, iconSize, spinnerSize, styles }: SelectMenuItemRenderProps) => {\n const Icon = item.icon;\n\n if (item.loading) {\n // Rendered as a plain Box, not NS.Item — Zag's state machine won't track it as\n // an option, so keyboard nav skips it and it can't be selected.\n return (\n <Box css={styles.item}>\n <Spinner variant=\"purple\" size={spinnerSize} css={styles.itemLoading} />\n <Text css={styles.itemLoadingLabel}>Loading...</Text>\n </Box>\n );\n }\n\n return (\n <NS.Item css={styles.item} item={item}>\n {item.avatar && (\n <Box data-slot=\"avatar\" css={styles.avatar}>\n <BitkitAvatar variant=\"image\" src={item.avatar} name={item.label} size={avatarSize} />\n </Box>\n )}\n <Box css={styles.itemContent}>\n <Box display=\"flex\" alignItems=\"center\" gap=\"8\">\n {Icon && !item.avatar && <Icon css={styles.itemIcon} size={iconSize} />}\n <Text css={styles.itemLabel}>{item.label}</Text>\n </Box>\n {item.helperText && <Text css={styles.itemHelperText}>{item.helperText}</Text>}\n </Box>\n {item.badge && (\n <Box css={styles.itemBadge}>\n {item.disabled ? cloneElement(item.badge, { colorVariant: 'neutral' }) : item.badge}\n </Box>\n )}\n <NS.ItemIndicator asChild>\n <IconCheck size={iconSize} css={styles.itemIndicator} />\n </NS.ItemIndicator>\n </NS.Item>\n );\n};\n\nexport default BitkitSelectMenu;\n"],"mappings":";;;;;;;;;;;;AA4DA,IAAM,mBAAmB,YAAmD,OAAO,QAAQ;CACzF,MAAM,EAAE,UAAU,YAAY,UAAU,UAAU,MAAM,GAAG,eAAe;CAC1E,MAAM,KAAK,YAAY,aAAa,WAAW;CAE/C,MAAM,SADS,cAAc,EAAE,KAAK,SAAS,CAC9B,CAAA,CAAO,EAAE,KAAK,CAAC;CAC9B,MAAM,EAAE,YAAY,UAAU,gBAAgB,cAAc,QAAQ;CAEpE,OACE,oBAAC,iBAAD;EACM;EACQ;EACZ,YAAY;EACF;EACV,aAAa,SACX,oBAAC,gBAAD;GAEM;GACQ;GACN;GACI;GACG;GACL;EACT,GAPM,KAAK,KAOX;EAEU;EACL;EACR,GAAI;EAEH;CACc,CAAA;AAErB,CAAC;AAED,iBAAiB,cAAc;AAW/B,IAAM,kBAAkB,EAAE,IAAI,YAAY,MAAM,UAAU,aAAa,aAAwC;CAC7G,MAAM,OAAO,KAAK;CAElB,IAAI,KAAK,SAGP,OACE,qBAAC,KAAD;EAAK,KAAK,OAAO;EAAjB,UAAA,CACE,oBAAC,SAAD;GAAS,SAAQ;GAAS,MAAM;GAAa,KAAK,OAAO;EAAc,CAAA,GACvE,oBAAC,MAAD;GAAM,KAAK,OAAO;GAAkB,UAAA;EAAgB,CAAA,CACjD;;CAIT,OACE,qBAAC,GAAG,MAAJ;EAAS,KAAK,OAAO;EAAY;EAAjC,UAAA;GACG,KAAK,UACJ,oBAAC,KAAD;IAAK,aAAU;IAAS,KAAK,OAAO;IAClC,UAAA,oBAAC,cAAD;KAAc,SAAQ;KAAQ,KAAK,KAAK;KAAQ,MAAM,KAAK;KAAO,MAAM;IAAa,CAAA;GAClF,CAAA;GAEP,qBAAC,KAAD;IAAK,KAAK,OAAO;IAAjB,UAAA,CACE,qBAAC,KAAD;KAAK,SAAQ;KAAO,YAAW;KAAS,KAAI;KAA5C,UAAA,CACG,QAAQ,CAAC,KAAK,UAAU,oBAAC,MAAD;MAAM,KAAK,OAAO;MAAU,MAAM;KAAW,CAAA,GACtE,oBAAC,MAAD;MAAM,KAAK,OAAO;MAAY,UAAA,KAAK;KAAY,CAAA,CAC5C;IACJ,CAAA,GAAA,KAAK,cAAc,oBAAC,MAAD;KAAM,KAAK,OAAO;KAAiB,UAAA,KAAK;IAAiB,CAAA,CAC1E;;GACJ,KAAK,SACJ,oBAAC,KAAD;IAAK,KAAK,OAAO;IACd,UAAA,KAAK,WAAW,aAAa,KAAK,OAAO,EAAE,cAAc,UAAU,CAAC,IAAI,KAAK;GAC3E,CAAA;GAEP,oBAAC,GAAG,eAAJ;IAAkB,SAAA;IAChB,UAAA,oBAAC,WAAD;KAAW,MAAM;KAAU,KAAK,OAAO;IAAgB,CAAA;GACvC,CAAA;EACX;;AAEb"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import IconArrowNortheast from "../../icons/IconArrowNortheast.js";
|
|
1
2
|
import BitkitCloseButton from "../BitkitCloseButton/BitkitCloseButton.js";
|
|
2
3
|
import BitkitColorButton from "../BitkitColorButton/BitkitColorButton.js";
|
|
3
4
|
import { BUTTON_COLORS_MAP, ICON_COMPONENTS_MAP } from "../common/notificationMaps.js";
|
|
@@ -35,9 +36,10 @@ var BitkitToaster = () => {
|
|
|
35
36
|
colorVariant: BUTTON_COLORS_MAP[variant],
|
|
36
37
|
css: styles.actionTrigger,
|
|
37
38
|
href: toast.meta.action.href,
|
|
38
|
-
isExternal: toast.meta.action.target === "_blank",
|
|
39
|
+
isExternal: toast.meta.action.isExternal || toast.meta.action.target === "_blank",
|
|
39
40
|
onClick: toast.meta.action.onClick,
|
|
40
41
|
state: toast.meta.action.state,
|
|
42
|
+
suffixIcon: toast.meta.action.isExternal ? IconArrowNortheast : void 0,
|
|
41
43
|
target: toast.meta.action.target,
|
|
42
44
|
children: toast.meta.action.label
|
|
43
45
|
}) : /* @__PURE__ */ jsx(BitkitColorButton, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BitkitToaster.js","names":[],"sources":["../../../lib/components/BitkitToast/BitkitToaster.tsx"],"sourcesContent":["import { Box } from '@chakra-ui/react/box';\nimport { useSlotRecipe, useToken } from '@chakra-ui/react/styled-system';\nimport { Text } from '@chakra-ui/react/text';\nimport { Toast, Toaster } from '@chakra-ui/react/toast';\n\nimport BitkitCloseButton from '../BitkitCloseButton/BitkitCloseButton';\nimport BitkitColorButton from '../BitkitColorButton/BitkitColorButton';\nimport { BUTTON_COLORS_MAP, ICON_COMPONENTS_MAP } from '../common/notificationMaps';\nimport { type BitkitToastVariant, toaster } from './BitkitToast';\n\nconst BitkitToaster = () => {\n const toastRecipe = useSlotRecipe({ key: 'toast' });\n const [zIndex] = useToken('zIndex', 'toast');\n\n return (\n <Toaster toaster={toaster} style={{ zIndex }}>\n {(toast) => {\n const variant = toast.type as BitkitToastVariant;\n const styles = toastRecipe({ closable: toast.closable, variant });\n const IconComponent = ICON_COMPONENTS_MAP[variant];\n return (\n <Toast.Root variant={variant} {...toast.meta?.dataAttributes}>\n <IconComponent css={styles.icon} />\n <Box css={styles.content}>\n {!!toast.title && <Toast.Title>{toast.title}</Toast.Title>}\n <Toast.Description>{toast.description}</Toast.Description>\n {!!toast.meta?.action &&\n (toast.meta.action.href !== undefined ? (\n <BitkitColorButton\n colorVariant={BUTTON_COLORS_MAP[variant]}\n css={styles.actionTrigger}\n href={toast.meta.action.href}\n isExternal={toast.meta.action.target === '_blank'}\n onClick={toast.meta.action.onClick}\n state={toast.meta.action.state}\n target={toast.meta.action.target}\n >\n {toast.meta.action.label}\n </BitkitColorButton>\n ) : (\n <BitkitColorButton\n colorVariant={BUTTON_COLORS_MAP[variant]}\n css={styles.actionTrigger}\n onClick={toast.meta.action.onClick}\n state={toast.meta.action.state}\n >\n {toast.meta.action.label}\n </BitkitColorButton>\n ))}\n {!!toast.meta?.timestamp && <Text css={styles.timestamp}>{toast.meta.timestamp}</Text>}\n </Box>\n {toast.closable && (\n <Toast.CloseTrigger asChild>\n <BitkitCloseButton\n aria-label=\"Dismiss notification\"\n colorVariant={BUTTON_COLORS_MAP[variant]}\n size=\"sm\"\n tooltipText=\"Dismiss\"\n />\n </Toast.CloseTrigger>\n )}\n </Toast.Root>\n );\n }}\n </Toaster>\n );\n};\n\nexport default BitkitToaster;\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"BitkitToaster.js","names":[],"sources":["../../../lib/components/BitkitToast/BitkitToaster.tsx"],"sourcesContent":["import { Box } from '@chakra-ui/react/box';\nimport { useSlotRecipe, useToken } from '@chakra-ui/react/styled-system';\nimport { Text } from '@chakra-ui/react/text';\nimport { Toast, Toaster } from '@chakra-ui/react/toast';\n\nimport { IconArrowNortheast } from '../../icons';\nimport BitkitCloseButton from '../BitkitCloseButton/BitkitCloseButton';\nimport BitkitColorButton from '../BitkitColorButton/BitkitColorButton';\nimport { BUTTON_COLORS_MAP, ICON_COMPONENTS_MAP } from '../common/notificationMaps';\nimport { type BitkitToastVariant, toaster } from './BitkitToast';\n\nconst BitkitToaster = () => {\n const toastRecipe = useSlotRecipe({ key: 'toast' });\n const [zIndex] = useToken('zIndex', 'toast');\n\n return (\n <Toaster toaster={toaster} style={{ zIndex }}>\n {(toast) => {\n const variant = toast.type as BitkitToastVariant;\n const styles = toastRecipe({ closable: toast.closable, variant });\n const IconComponent = ICON_COMPONENTS_MAP[variant];\n return (\n <Toast.Root variant={variant} {...toast.meta?.dataAttributes}>\n <IconComponent css={styles.icon} />\n <Box css={styles.content}>\n {!!toast.title && <Toast.Title>{toast.title}</Toast.Title>}\n <Toast.Description>{toast.description}</Toast.Description>\n {!!toast.meta?.action &&\n (toast.meta.action.href !== undefined ? (\n <BitkitColorButton\n colorVariant={BUTTON_COLORS_MAP[variant]}\n css={styles.actionTrigger}\n href={toast.meta.action.href}\n isExternal={toast.meta.action.isExternal || toast.meta.action.target === '_blank'}\n onClick={toast.meta.action.onClick}\n state={toast.meta.action.state}\n suffixIcon={toast.meta.action.isExternal ? IconArrowNortheast : undefined}\n target={toast.meta.action.target}\n >\n {toast.meta.action.label}\n </BitkitColorButton>\n ) : (\n <BitkitColorButton\n colorVariant={BUTTON_COLORS_MAP[variant]}\n css={styles.actionTrigger}\n onClick={toast.meta.action.onClick}\n state={toast.meta.action.state}\n >\n {toast.meta.action.label}\n </BitkitColorButton>\n ))}\n {!!toast.meta?.timestamp && <Text css={styles.timestamp}>{toast.meta.timestamp}</Text>}\n </Box>\n {toast.closable && (\n <Toast.CloseTrigger asChild>\n <BitkitCloseButton\n aria-label=\"Dismiss notification\"\n colorVariant={BUTTON_COLORS_MAP[variant]}\n size=\"sm\"\n tooltipText=\"Dismiss\"\n />\n </Toast.CloseTrigger>\n )}\n </Toast.Root>\n );\n }}\n </Toaster>\n );\n};\n\nexport default BitkitToaster;\n"],"mappings":";;;;;;;;;;;AAWA,IAAM,sBAAsB;CAC1B,MAAM,cAAc,cAAc,EAAE,KAAK,QAAQ,CAAC;CAClD,MAAM,CAAC,UAAU,SAAS,UAAU,OAAO;CAE3C,OACE,oBAAC,SAAD;EAAkB;EAAS,OAAO,EAAE,OAAO;EACvC,WAAA,UAAU;GACV,MAAM,UAAU,MAAM;GACtB,MAAM,SAAS,YAAY;IAAE,UAAU,MAAM;IAAU;GAAQ,CAAC;GAChE,MAAM,gBAAgB,oBAAoB;GAC1C,OACE,qBAAC,MAAM,MAAP;IAAqB;IAAS,GAAI,MAAM,MAAM;IAA9C,UAAA;KACE,oBAAC,eAAD,EAAe,KAAK,OAAO,KAAO,CAAA;KAClC,qBAAC,KAAD;MAAK,KAAK,OAAO;MAAjB,UAAA;OACG,CAAC,CAAC,MAAM,SAAS,oBAAC,MAAM,OAAP,EAAA,UAAc,MAAM,MAAmB,CAAA;OACzD,oBAAC,MAAM,aAAP,EAAA,UAAoB,MAAM,YAA+B,CAAA;OACxD,CAAC,CAAC,MAAM,MAAM,WACZ,MAAM,KAAK,OAAO,SAAS,KAAA,IAC1B,oBAAC,mBAAD;QACE,cAAc,kBAAkB;QAChC,KAAK,OAAO;QACZ,MAAM,MAAM,KAAK,OAAO;QACxB,YAAY,MAAM,KAAK,OAAO,cAAc,MAAM,KAAK,OAAO,WAAW;QACzE,SAAS,MAAM,KAAK,OAAO;QAC3B,OAAO,MAAM,KAAK,OAAO;QACzB,YAAY,MAAM,KAAK,OAAO,aAAa,qBAAqB,KAAA;QAChE,QAAQ,MAAM,KAAK,OAAO;QAEzB,UAAA,MAAM,KAAK,OAAO;OACF,CAAA,IAEnB,oBAAC,mBAAD;QACE,cAAc,kBAAkB;QAChC,KAAK,OAAO;QACZ,SAAS,MAAM,KAAK,OAAO;QAC3B,OAAO,MAAM,KAAK,OAAO;QAExB,UAAA,MAAM,KAAK,OAAO;OACF,CAAA;OAEtB,CAAC,CAAC,MAAM,MAAM,aAAa,oBAAC,MAAD;QAAM,KAAK,OAAO;QAAY,UAAA,MAAM,KAAK;OAAgB,CAAA;MAClF;;KACJ,MAAM,YACL,oBAAC,MAAM,cAAP;MAAoB,SAAA;MAClB,UAAA,oBAAC,mBAAD;OACE,cAAW;OACX,cAAc,kBAAkB;OAChC,MAAK;OACL,aAAY;MACb,CAAA;KACiB,CAAA;IAEZ;;EAEhB;CACO,CAAA;AAEb"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import IconArrowNortheast from "../../icons/IconArrowNortheast.js";
|
|
1
2
|
import BitkitCloseButton from "../BitkitCloseButton/BitkitCloseButton.js";
|
|
2
3
|
import { rem } from "../../theme/themeUtils.js";
|
|
3
4
|
import BitkitColorButton from "../BitkitColorButton/BitkitColorButton.js";
|
|
@@ -8,9 +9,10 @@ import { Alert } from "@chakra-ui/react/alert";
|
|
|
8
9
|
var ActionButton = ({ action, colorVariant }) => action.href !== void 0 ? /* @__PURE__ */ jsx(BitkitColorButton, {
|
|
9
10
|
colorVariant,
|
|
10
11
|
href: action.href,
|
|
11
|
-
isExternal: action.target === "_blank",
|
|
12
|
+
isExternal: action.isExternal || action.target === "_blank",
|
|
12
13
|
onClick: action.onClick,
|
|
13
14
|
state: action.state,
|
|
15
|
+
suffixIcon: action.isExternal ? IconArrowNortheast : void 0,
|
|
14
16
|
target: action.target,
|
|
15
17
|
marginBlock: rem(-4),
|
|
16
18
|
marginInlineEnd: "12",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NotificationContent.js","names":[],"sources":["../../../lib/components/common/NotificationContent.tsx"],"sourcesContent":["/* Shared inner structure for notification-like components (Alert, PromoBanner):\n the content + action row and the trailing close button, plus the two buttons themselves.\n Alert.Root is the flex container, so this fragment's row and close button land as flex\n siblings next to the leading visual (indicator / illustration).\n The constants here are load-bearing and must not drift across the two components:\n `rem(11)` rowGap + the button `rem(-4)` bleed keep a single-line notification on 48px,\n and `flex: 1 1 rem(240)` on the content lets the action wrap below the text on narrow widths.\n\n `stackable` (opt-in, currently only PromoBanner) makes the elements this fragment owns\n responsive: below `tablet` the content/action row stacks vertically and the close button\n floats to the top-right corner. It positions the button absolutely, so the consuming\n component must give Alert.Root `position=\"relative\"`. Because the button floats over the\n top-right corner (32px at `inset 8` ⇒ a 40px band), the content row reserves a matching\n inline-end gutter below `tablet` so title/message text never runs under it — independent of\n how tall the leading visual is. The leading visual and the Root's own flex direction / gap /\n padding stay with the consumer — they differ per component. */\n\nimport { Alert } from '@chakra-ui/react/alert';\nimport { Box } from '@chakra-ui/react/box';\nimport { type ReactNode } from 'react';\n\nimport { rem } from '../../theme/themeUtils';\nimport BitkitCloseButton, { type BitkitCloseButtonProps } from '../BitkitCloseButton/BitkitCloseButton';\nimport BitkitColorButton, { type BitkitColorButtonProps } from '../BitkitColorButton/BitkitColorButton';\nimport { type BUTTON_COLORS_MAP, type NotificationAction } from './notificationMaps';\n\ntype NotificationColorVariant = (typeof BUTTON_COLORS_MAP)[keyof typeof BUTTON_COLORS_MAP];\n\nconst ActionButton = ({\n action,\n colorVariant,\n}: {\n action: NotificationAction;\n colorVariant: BitkitColorButtonProps['colorVariant'];\n}) =>\n action.href !== undefined ? (\n <BitkitColorButton\n colorVariant={colorVariant}\n href={action.href}\n isExternal={action.target === '_blank'}\n onClick={action.onClick}\n state={action.state}\n target={action.target}\n marginBlock={rem(-4)}\n marginInlineEnd=\"12\"\n whiteSpace=\"nowrap\"\n >\n {action.label}\n </BitkitColorButton>\n ) : (\n <BitkitColorButton\n colorVariant={colorVariant}\n onClick={action.onClick}\n state={action.state}\n marginBlock={rem(-4)}\n marginInlineEnd=\"12\"\n whiteSpace=\"nowrap\"\n >\n {action.label}\n </BitkitColorButton>\n );\n\nconst CloseButton = ({\n colorVariant,\n onClose,\n stackable,\n}: {\n colorVariant: BitkitCloseButtonProps['colorVariant'];\n onClose?: () => void;\n stackable?: boolean;\n}) => (\n <BitkitCloseButton\n alignSelf=\"flex-start\"\n size=\"sm\"\n onClick={onClose}\n colorVariant={colorVariant}\n // Below tablet (stackable only) it floats to the top-right corner (needs Root\n // position=\"relative\"); otherwise inline with the -4 bleed that keeps a single line on 48px.\n position={stackable ? { base: 'absolute', tablet: 'static' } : undefined}\n insetBlockStart={stackable ? '8' : undefined}\n insetInlineEnd={stackable ? '8' : undefined}\n marginBlock={{ base: stackable ? '0' : rem(-4), tablet: rem(-4) }}\n />\n);\n\nexport interface NotificationContentProps {\n action?: NotificationAction;\n colorVariant: NotificationColorVariant;\n dismissible?: boolean;\n messageText: ReactNode;\n onClose?: () => void;\n /** Opt in to responsive stacking below `tablet`. Requires Root `position=\"relative\"`. */\n stackable?: boolean;\n titleText?: ReactNode;\n}\n\nexport const NotificationContent = ({\n action,\n colorVariant,\n dismissible,\n messageText,\n onClose,\n stackable,\n titleText,\n}: NotificationContentProps) => (\n <>\n {/* content + action share a wrapping row so the action drops below the text on narrow widths;\n when stacking, the row switches to a column below tablet */}\n <Box\n display=\"flex\"\n flexDirection={stackable ? { base: 'column', tablet: 'row' } : undefined}\n flex={stackable ? { base: '0 1 0%', tablet: '1' } : '1'}\n minWidth=\"0\"\n flexWrap=\"wrap\"\n alignItems={stackable ? { base: 'flex-start', tablet: 'center' } : 'center'}\n columnGap=\"16\"\n rowGap={stackable ? { base: '20', tablet: rem(11) } : rem(11)}\n paddingInlineEnd={stackable && dismissible ? { base: '40', tablet: '0' } : undefined}\n >\n <Alert.Content minWidth=\"0\" flex={stackable ? { base: 'none', tablet: `1 1 ${rem(240)}` } : `1 1 ${rem(240)}`}>\n {titleText && <Alert.Title>{titleText}</Alert.Title>}\n <Alert.Description>{messageText}</Alert.Description>\n </Alert.Content>\n {!!action && <ActionButton action={action} colorVariant={colorVariant} />}\n </Box>\n {!!dismissible && <CloseButton colorVariant={colorVariant} onClose={onClose} stackable={stackable} />}\n </>\n);\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"NotificationContent.js","names":[],"sources":["../../../lib/components/common/NotificationContent.tsx"],"sourcesContent":["/* Shared inner structure for notification-like components (Alert, PromoBanner):\n the content + action row and the trailing close button, plus the two buttons themselves.\n Alert.Root is the flex container, so this fragment's row and close button land as flex\n siblings next to the leading visual (indicator / illustration).\n The constants here are load-bearing and must not drift across the two components:\n `rem(11)` rowGap + the button `rem(-4)` bleed keep a single-line notification on 48px,\n and `flex: 1 1 rem(240)` on the content lets the action wrap below the text on narrow widths.\n\n `stackable` (opt-in, currently only PromoBanner) makes the elements this fragment owns\n responsive: below `tablet` the content/action row stacks vertically and the close button\n floats to the top-right corner. It positions the button absolutely, so the consuming\n component must give Alert.Root `position=\"relative\"`. Because the button floats over the\n top-right corner (32px at `inset 8` ⇒ a 40px band), the content row reserves a matching\n inline-end gutter below `tablet` so title/message text never runs under it — independent of\n how tall the leading visual is. The leading visual and the Root's own flex direction / gap /\n padding stay with the consumer — they differ per component. */\n\nimport { Alert } from '@chakra-ui/react/alert';\nimport { Box } from '@chakra-ui/react/box';\nimport { type ReactNode } from 'react';\n\nimport { IconArrowNortheast } from '../../icons';\nimport { rem } from '../../theme/themeUtils';\nimport BitkitCloseButton, { type BitkitCloseButtonProps } from '../BitkitCloseButton/BitkitCloseButton';\nimport BitkitColorButton, { type BitkitColorButtonProps } from '../BitkitColorButton/BitkitColorButton';\nimport { type BUTTON_COLORS_MAP, type NotificationAction } from './notificationMaps';\n\ntype NotificationColorVariant = (typeof BUTTON_COLORS_MAP)[keyof typeof BUTTON_COLORS_MAP];\n\nconst ActionButton = ({\n action,\n colorVariant,\n}: {\n action: NotificationAction;\n colorVariant: BitkitColorButtonProps['colorVariant'];\n}) =>\n action.href !== undefined ? (\n <BitkitColorButton\n colorVariant={colorVariant}\n href={action.href}\n isExternal={action.isExternal || action.target === '_blank'}\n onClick={action.onClick}\n state={action.state}\n suffixIcon={action.isExternal ? IconArrowNortheast : undefined}\n target={action.target}\n marginBlock={rem(-4)}\n marginInlineEnd=\"12\"\n whiteSpace=\"nowrap\"\n >\n {action.label}\n </BitkitColorButton>\n ) : (\n <BitkitColorButton\n colorVariant={colorVariant}\n onClick={action.onClick}\n state={action.state}\n marginBlock={rem(-4)}\n marginInlineEnd=\"12\"\n whiteSpace=\"nowrap\"\n >\n {action.label}\n </BitkitColorButton>\n );\n\nconst CloseButton = ({\n colorVariant,\n onClose,\n stackable,\n}: {\n colorVariant: BitkitCloseButtonProps['colorVariant'];\n onClose?: () => void;\n stackable?: boolean;\n}) => (\n <BitkitCloseButton\n alignSelf=\"flex-start\"\n size=\"sm\"\n onClick={onClose}\n colorVariant={colorVariant}\n // Below tablet (stackable only) it floats to the top-right corner (needs Root\n // position=\"relative\"); otherwise inline with the -4 bleed that keeps a single line on 48px.\n position={stackable ? { base: 'absolute', tablet: 'static' } : undefined}\n insetBlockStart={stackable ? '8' : undefined}\n insetInlineEnd={stackable ? '8' : undefined}\n marginBlock={{ base: stackable ? '0' : rem(-4), tablet: rem(-4) }}\n />\n);\n\nexport interface NotificationContentProps {\n action?: NotificationAction;\n colorVariant: NotificationColorVariant;\n dismissible?: boolean;\n messageText: ReactNode;\n onClose?: () => void;\n /** Opt in to responsive stacking below `tablet`. Requires Root `position=\"relative\"`. */\n stackable?: boolean;\n titleText?: ReactNode;\n}\n\nexport const NotificationContent = ({\n action,\n colorVariant,\n dismissible,\n messageText,\n onClose,\n stackable,\n titleText,\n}: NotificationContentProps) => (\n <>\n {/* content + action share a wrapping row so the action drops below the text on narrow widths;\n when stacking, the row switches to a column below tablet */}\n <Box\n display=\"flex\"\n flexDirection={stackable ? { base: 'column', tablet: 'row' } : undefined}\n flex={stackable ? { base: '0 1 0%', tablet: '1' } : '1'}\n minWidth=\"0\"\n flexWrap=\"wrap\"\n alignItems={stackable ? { base: 'flex-start', tablet: 'center' } : 'center'}\n columnGap=\"16\"\n rowGap={stackable ? { base: '20', tablet: rem(11) } : rem(11)}\n paddingInlineEnd={stackable && dismissible ? { base: '40', tablet: '0' } : undefined}\n >\n <Alert.Content minWidth=\"0\" flex={stackable ? { base: 'none', tablet: `1 1 ${rem(240)}` } : `1 1 ${rem(240)}`}>\n {titleText && <Alert.Title>{titleText}</Alert.Title>}\n <Alert.Description>{messageText}</Alert.Description>\n </Alert.Content>\n {!!action && <ActionButton action={action} colorVariant={colorVariant} />}\n </Box>\n {!!dismissible && <CloseButton colorVariant={colorVariant} onClose={onClose} stackable={stackable} />}\n </>\n);\n"],"mappings":";;;;;;;;AA6BA,IAAM,gBAAgB,EACpB,QACA,mBAKA,OAAO,SAAS,KAAA,IACd,oBAAC,mBAAD;CACgB;CACd,MAAM,OAAO;CACb,YAAY,OAAO,cAAc,OAAO,WAAW;CACnD,SAAS,OAAO;CAChB,OAAO,OAAO;CACd,YAAY,OAAO,aAAa,qBAAqB,KAAA;CACrD,QAAQ,OAAO;CACf,aAAa,IAAI,EAAE;CACnB,iBAAgB;CAChB,YAAW;CAEV,UAAA,OAAO;AACS,CAAA,IAEnB,oBAAC,mBAAD;CACgB;CACd,SAAS,OAAO;CAChB,OAAO,OAAO;CACd,aAAa,IAAI,EAAE;CACnB,iBAAgB;CAChB,YAAW;CAEV,UAAA,OAAO;AACS,CAAA;AAGvB,IAAM,eAAe,EACnB,cACA,SACA,gBAMA,oBAAC,mBAAD;CACE,WAAU;CACV,MAAK;CACL,SAAS;CACK;CAGd,UAAU,YAAY;EAAE,MAAM;EAAY,QAAQ;CAAS,IAAI,KAAA;CAC/D,iBAAiB,YAAY,MAAM,KAAA;CACnC,gBAAgB,YAAY,MAAM,KAAA;CAClC,aAAa;EAAE,MAAM,YAAY,MAAM,IAAI,EAAE;EAAG,QAAQ,IAAI,EAAE;CAAE;AACjE,CAAA;AAcH,IAAa,uBAAuB,EAClC,QACA,cACA,aACA,aACA,SACA,WACA,gBAEA,qBAAA,UAAA,EAAA,UAAA,CAGE,qBAAC,KAAD;CACE,SAAQ;CACR,eAAe,YAAY;EAAE,MAAM;EAAU,QAAQ;CAAM,IAAI,KAAA;CAC/D,MAAM,YAAY;EAAE,MAAM;EAAU,QAAQ;CAAI,IAAI;CACpD,UAAS;CACT,UAAS;CACT,YAAY,YAAY;EAAE,MAAM;EAAc,QAAQ;CAAS,IAAI;CACnE,WAAU;CACV,QAAQ,YAAY;EAAE,MAAM;EAAM,QAAQ,IAAI,EAAE;CAAE,IAAI,IAAI,EAAE;CAC5D,kBAAkB,aAAa,cAAc;EAAE,MAAM;EAAM,QAAQ;CAAI,IAAI,KAAA;CAT7E,UAAA,CAWE,qBAAC,MAAM,SAAP;EAAe,UAAS;EAAI,MAAM,YAAY;GAAE,MAAM;GAAQ,QAAQ,OAAO,IAAI,GAAG;EAAI,IAAI,OAAO,IAAI,GAAG;EAA1G,UAAA,CACG,aAAa,oBAAC,MAAM,OAAP,EAAA,UAAc,UAAuB,CAAA,GACnD,oBAAC,MAAM,aAAP,EAAA,UAAoB,YAA+B,CAAA,CACtC;CACd,CAAA,GAAA,CAAC,CAAC,UAAU,oBAAC,cAAD;EAAsB;EAAsB;CAAe,CAAA,CACrE;AACJ,CAAA,GAAA,CAAC,CAAC,eAAe,oBAAC,aAAD;CAA2B;CAAuB;CAAoB;AAAY,CAAA,CACpG,EAAA,CAAA"}
|
|
@@ -2,6 +2,8 @@ import { ElementType } from 'react';
|
|
|
2
2
|
import { NotificationVariant } from '../../theme/common/AlertAndToast.common';
|
|
3
3
|
export type NotificationAction = {
|
|
4
4
|
href?: string;
|
|
5
|
+
/** Only with `href`: opens the link in a new tab and adds the external-link arrow. */
|
|
6
|
+
isExternal?: boolean;
|
|
5
7
|
label: string;
|
|
6
8
|
onClick?: () => void;
|
|
7
9
|
/**
|
|
@@ -16,6 +18,7 @@ export type NotificationAction = {
|
|
|
16
18
|
* same trick — its own change.
|
|
17
19
|
*/
|
|
18
20
|
state?: 'disabled' | 'loading';
|
|
21
|
+
/** Anchor target. Ignored when `isExternal` is set. */
|
|
19
22
|
target?: HTMLAnchorElement['target'];
|
|
20
23
|
};
|
|
21
24
|
export declare const BUTTON_COLORS_MAP: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"notificationMaps.js","names":[],"sources":["../../../lib/components/common/notificationMaps.tsx"],"sourcesContent":["/* Shared component-level maps for notification-like components (Alert, Toast). */\n\nimport { Spinner } from '@chakra-ui/react/spinner';\nimport { type ElementType } from 'react';\n\nimport IconCheck from '../../icons/IconCheck';\nimport IconErrorCircleFilled from '../../icons/IconErrorCircleFilled';\nimport IconInfoCircle from '../../icons/IconInfoCircle';\nimport IconSparkleFilled from '../../icons/IconSparkleFilled';\nimport IconWarning from '../../icons/IconWarning';\nimport { type NotificationVariant } from '../../theme/common/AlertAndToast.common';\n\nexport type NotificationAction = {\n href?: string;\n label: string;\n onClick?: () => void;\n /**\n * Disabled or loading state of the action button.\n *\n * Known gap: not every consumer can honour the full range. Alert, Toast, PromoBanner and Ribbon\n * actions render via `BitkitColorButton` and support both. `BitkitNoteCard`'s **link** actions (`href`\n * set) render via `BitkitButton`, whose anchor variant excludes `loading` at the type level — so\n * `'loading'` degrades to no state there. That's an implementation limit of `BitkitButton`'s\n * anchor branch, not a rule about links: loading anchors work fine on `BitkitColorButton` (`href`\n * kept, pointer events off, activation guarded). Closing it means teaching `BitkitButton` the\n * same trick — its own change.\n */\n state?: 'disabled' | 'loading';\n target?: HTMLAnchorElement['target'];\n};\n\nexport const BUTTON_COLORS_MAP = {\n ai: 'indigo',\n critical: 'red',\n info: 'blue',\n progress: 'purple',\n success: 'green',\n warning: 'yellow',\n} as const satisfies Record<NotificationVariant, string>;\n\nexport const ICON_COMPONENTS_MAP: Record<NotificationVariant, ElementType> = {\n ai: IconSparkleFilled,\n critical: IconErrorCircleFilled,\n info: IconInfoCircle,\n progress: Spinner,\n success: IconCheck,\n warning: IconWarning,\n};\n"],"mappings":";;;;;;;
|
|
1
|
+
{"version":3,"file":"notificationMaps.js","names":[],"sources":["../../../lib/components/common/notificationMaps.tsx"],"sourcesContent":["/* Shared component-level maps for notification-like components (Alert, Toast). */\n\nimport { Spinner } from '@chakra-ui/react/spinner';\nimport { type ElementType } from 'react';\n\nimport IconCheck from '../../icons/IconCheck';\nimport IconErrorCircleFilled from '../../icons/IconErrorCircleFilled';\nimport IconInfoCircle from '../../icons/IconInfoCircle';\nimport IconSparkleFilled from '../../icons/IconSparkleFilled';\nimport IconWarning from '../../icons/IconWarning';\nimport { type NotificationVariant } from '../../theme/common/AlertAndToast.common';\n\nexport type NotificationAction = {\n href?: string;\n /** Only with `href`: opens the link in a new tab and adds the external-link arrow. */\n isExternal?: boolean;\n label: string;\n onClick?: () => void;\n /**\n * Disabled or loading state of the action button.\n *\n * Known gap: not every consumer can honour the full range. Alert, Toast, PromoBanner and Ribbon\n * actions render via `BitkitColorButton` and support both. `BitkitNoteCard`'s **link** actions (`href`\n * set) render via `BitkitButton`, whose anchor variant excludes `loading` at the type level — so\n * `'loading'` degrades to no state there. That's an implementation limit of `BitkitButton`'s\n * anchor branch, not a rule about links: loading anchors work fine on `BitkitColorButton` (`href`\n * kept, pointer events off, activation guarded). Closing it means teaching `BitkitButton` the\n * same trick — its own change.\n */\n state?: 'disabled' | 'loading';\n /** Anchor target. Ignored when `isExternal` is set. */\n target?: HTMLAnchorElement['target'];\n};\n\nexport const BUTTON_COLORS_MAP = {\n ai: 'indigo',\n critical: 'red',\n info: 'blue',\n progress: 'purple',\n success: 'green',\n warning: 'yellow',\n} as const satisfies Record<NotificationVariant, string>;\n\nexport const ICON_COMPONENTS_MAP: Record<NotificationVariant, ElementType> = {\n ai: IconSparkleFilled,\n critical: IconErrorCircleFilled,\n info: IconInfoCircle,\n progress: Spinner,\n success: IconCheck,\n warning: IconWarning,\n};\n"],"mappings":";;;;;;;AAkCA,IAAa,oBAAoB;CAC/B,IAAI;CACJ,UAAU;CACV,MAAM;CACN,UAAU;CACV,SAAS;CACT,SAAS;AACX;AAEA,IAAa,sBAAgE;CAC3E,IAAI;CACJ,UAAU;CACV,MAAM;CACN,UAAU;CACV,SAAS;CACT,SAAS;AACX"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
declare const pageSidebarSlotRecipe: import('@chakra-ui/react').SlotRecipeDefinition<"body" | "footer" | "title" | "root" | "item" | "
|
|
1
|
+
declare const pageSidebarSlotRecipe: import('@chakra-ui/react').SlotRecipeDefinition<"body" | "footer" | "title" | "root" | "item" | "suffixIcon" | "titleText" | "titleBlock" | "itemLabel" | "itemIcon" | "contentBlock" | "divider" | "progressBlocker" | "progressConnector" | "progressContentBlock" | "progressIcon" | "progressLabel" | "progressRow" | "progressSecondaryText" | "progressTextBlock" | "project" | "projectLabel" | "projectValue" | "projectValueWrapper" | "selectionMarker", {
|
|
2
2
|
hasBack: {
|
|
3
3
|
true: {
|
|
4
4
|
title: {
|
|
@@ -1681,7 +1681,7 @@ declare const slotRecipes: {
|
|
|
1681
1681
|
};
|
|
1682
1682
|
};
|
|
1683
1683
|
}>;
|
|
1684
|
-
pageSidebar: import('@chakra-ui/react').SlotRecipeDefinition<"body" | "footer" | "title" | "root" | "item" | "
|
|
1684
|
+
pageSidebar: import('@chakra-ui/react').SlotRecipeDefinition<"body" | "footer" | "title" | "root" | "item" | "suffixIcon" | "titleText" | "titleBlock" | "itemLabel" | "itemIcon" | "contentBlock" | "divider" | "progressBlocker" | "progressConnector" | "progressContentBlock" | "progressIcon" | "progressLabel" | "progressRow" | "progressSecondaryText" | "progressTextBlock" | "project" | "projectLabel" | "projectValue" | "projectValueWrapper" | "selectionMarker", {
|
|
1685
1685
|
hasBack: {
|
|
1686
1686
|
true: {
|
|
1687
1687
|
title: {
|