@bitrise/bitkit-v2 0.3.481-beta.3065 → 0.3.481-beta.3067

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.
@@ -35,7 +35,7 @@ export interface BitkitFilterSelectProps extends Omit<BoxProps, 'children' | 'de
35
35
  */
36
36
  pattern?: boolean;
37
37
  /**
38
- * Plural of `label`: the empty chip (`All projects`), a multiple selection (`2 Workflows`), and a
38
+ * Plural of `label`: the empty chip (`All projects`), a multiple selection (`2 workflows`), and a
39
39
  * multi-select's panel title. Required, since a guessed plural reads wrong too often.
40
40
  */
41
41
  pluralLabel: string;
@@ -111,7 +111,7 @@ var BitkitFilterSelect = forwardRef((props, ref) => {
111
111
  } else if (hasChip) {
112
112
  let chipProps = { value: emptyText };
113
113
  if (value.length > 1) chipProps = {
114
- label: pluralLabel,
114
+ label: toRunningText(pluralLabel),
115
115
  labelAfter: true,
116
116
  value: value.length
117
117
  };
@@ -1 +1 @@
1
- {"version":3,"file":"BitkitFilterSelect.js","names":["_Fragment"],"sources":["../../../lib/components/BitkitFilter/BitkitFilterSelect.tsx"],"sourcesContent":["// eslint-disable-next-line no-restricted-imports\nimport { mergeRefs } from '@chakra-ui/react';\nimport { type BoxProps } from '@chakra-ui/react/box';\nimport { forwardRef, useEffect, useMemo, useRef, useState } from 'react';\n\nimport { useFilterAddItem, useFilterContext, useFilterRegistration } from './BitkitFilter.context';\nimport { isPattern, SEARCH_DEBOUNCE_MS, toRunningText } from './BitkitFilter.utils';\nimport FilterChip, { type FilterChipProps } from './components/FilterChip';\nimport FilterField from './components/FilterField';\nimport FilterPanel from './components/FilterPanel';\nimport FilterSelectForm, { type BitkitFilterSelectItem } from './components/FilterSelectForm';\n\nexport type { BitkitFilterSelectItem };\n\nconst NO_VALUE: string[] = [];\n\nexport interface BitkitFilterSelectProps extends Omit<BoxProps, 'children' | 'defaultValue' | 'onChange'> {\n /**\n * Why the filter doesn't apply yet, e.g. \"Select a project first\". Its entry in the \"Add filter\"\n * menu is muted with an info icon, and says this in a tooltip when highlighted (spelled out under\n * the label on mobile). Only applies inside `BitkitFilter.Add`. A filter with no options is\n * disabled anyway.\n */\n disabledReason?: string;\n /** Chip text while nothing is selected. Defaults to `All {pluralLabel}`. */\n emptyLabel?: string;\n items: BitkitFilterSelectItem[];\n /** Name of the filter: the popover title, and the prefix of a single selection on the chip (`Project: iOS`). */\n label: string;\n /** Shows a loading row in place of the options, e.g. while `onSearchChange` results are fetched. */\n loading?: boolean;\n /** Text of the loading row. Defaults to \"Loading...\". */\n loadingLabel?: string;\n multiple?: boolean;\n /**\n * Hands search over to you: `items` are shown as they are, without filtering them locally. Use it\n * for server-side search. Called once typing pauses (300ms), right away with `''` on clear and\n * close; ordering the responses is up to you. The search input is always shown with it; otherwise\n * only above 5 items.\n */\n onSearchChange?: (searchText: string) => void;\n /** Called on \"Apply\", \"Clear\" and the chip's remove button — not on every click in the popover. */\n onValueChange: (value: string[]) => void;\n /**\n * Offers a wildcard pattern (`release-*`) as an alternative to picking options. A pattern is\n * applied as the single value, e.g. `['release-*']`; matching it is up to you. The preview of\n * matching options only checks the labels of the `items` at hand, not a server search.\n */\n pattern?: boolean;\n /**\n * Plural of `label`: the empty chip (`All projects`), a multiple selection (`2 Workflows`), and a\n * multi-select's panel title. Required, since a guessed plural reads wrong too often.\n */\n pluralLabel: string;\n /** Lists the options in the order of `items`, instead of the applied ones first. */\n preserveOrder?: boolean;\n /**\n * A value is always applied, so the popover offers no \"Clear\". Pass the default as `resetValue`\n * too, for \"Clear filters\" to restore. Not for a filter inside `BitkitFilter.Add`.\n */\n required?: boolean;\n /** The value \"Clear filters\" restores. Defaults to no selection. */\n resetValue?: string[];\n /** Defaults to \"Search for options\". */\n searchPlaceholder?: string;\n value: string[];\n}\n\n/**\n * A filter picking one or more options from a list, applied with the popover's \"Apply\". Directly\n * in `BitkitFilter` it is a dropdown chip that is always shown; inside `BitkitFilter.Add` it is a\n * removable chip that only appears while it holds a value.\n */\n// The ref lands on the chip's (or on mobile the field's) button; other props on its wrapper.\nconst BitkitFilterSelect = forwardRef<HTMLButtonElement, BitkitFilterSelectProps>((props, ref) => {\n const {\n disabledReason,\n emptyLabel,\n items,\n label,\n loading,\n loadingLabel,\n multiple,\n onSearchChange,\n onValueChange,\n pattern,\n pluralLabel,\n preserveOrder,\n required,\n resetValue = NO_VALUE,\n searchPlaceholder,\n value,\n ...rest\n } = props;\n const { isMobile } = useFilterContext();\n const [open, setOpen] = useState(false);\n const addItem = useFilterAddItem({\n disabledReason,\n hasOptions: !!items.length || !!onSearchChange,\n isEmpty: !value.length,\n label,\n });\n\n useFilterRegistration(value, resetValue, onValueChange);\n\n // Remember every option seen, so a selection keeps its label, group and icon once a search has replaced `items`.\n const knownItems = useRef(new Map<string, BitkitFilterSelectItem>());\n items.forEach((item) => knownItems.current.set(item.value, item));\n\n // A filter just added through \"Add filter\" hands focus to its new chip as its panel closes.\n const triggerRef = useRef<HTMLButtonElement>(null);\n const setRef = useMemo(() => mergeRefs<HTMLButtonElement>(ref, triggerRef), [ref]);\n const isFocusPending = useRef(false);\n // A filter emptied from its own chip loses the chip, so its panel hands focus to \"Add filter\" on close.\n const isEmptiedFromChip = useRef(false);\n\n // Debounced here rather than in the form, which outlives the close by its exit animation: closing\n // drops a pending search, so it can't land after the reset to `''`. Clearing goes through right away.\n const searchTimeout = useRef<ReturnType<typeof setTimeout>>(undefined);\n useEffect(() => () => clearTimeout(searchTimeout.current), []);\n const search = (text: string) => {\n clearTimeout(searchTimeout.current);\n if (text) searchTimeout.current = setTimeout(() => onSearchChange?.(text), SEARCH_DEBOUNCE_MS);\n else onSearchChange?.('');\n };\n\n const onOpenChange = (isOpen: boolean) => {\n setOpen(isOpen);\n if (!isOpen && onSearchChange) search('');\n };\n\n const form = (isAdding: boolean) => (\n <FilterSelectForm\n isEditing={!!value.length}\n isOrderFixed={preserveOrder}\n isRequired={required && !addItem}\n items={items}\n label={label}\n loading={loading}\n loadingLabel={loadingLabel}\n multiple={multiple}\n onApply={(next) => {\n if (isAdding) isFocusPending.current = true;\n onValueChange(next);\n if (isAdding) addItem?.stopAdding();\n else if (addItem && !next.length) isEmptiedFromChip.current = true;\n onOpenChange(false);\n }}\n onCancel={() => {\n if (isAdding) addItem?.stopAdding();\n onOpenChange(false);\n }}\n onSearchChange={onSearchChange ? search : undefined}\n pattern={pattern}\n pluralLabel={pluralLabel}\n searchPlaceholder={searchPlaceholder}\n selectedItems={value\n .filter((selected) => !isPattern(selected))\n .map((selected) => knownItems.current.get(selected) ?? { label: selected, value: selected })}\n value={value}\n />\n );\n\n // Inside `Add`, an empty filter has no chip: it is listed in the menu, and its panel opens from\n // there, anchored to the \"Add filter\" button. It stays mounted once a value is applied, so it can\n // close (and hand over focus) the usual way.\n const addPanel = addItem ? (\n <FilterPanel\n finalFocusEl={() => {\n const chip = isFocusPending.current ? triggerRef.current : null;\n isFocusPending.current = false;\n return chip ?? addItem.getTrigger();\n }}\n getAnchorElement={addItem.getTrigger}\n onOpenChange={(isOpen) => {\n if (isOpen) return;\n addItem.stopAdding();\n onOpenChange(false);\n }}\n open={addItem.isAdding}\n >\n {form(true)}\n </FilterPanel>\n ) : null;\n\n const emptyText = emptyLabel ?? `All ${toRunningText(pluralLabel)}`;\n const singleText = value.length === 1 ? (knownItems.current.get(value[0])?.label ?? value[0]) : '';\n const onRemove = addItem\n ? () => {\n onValueChange([]);\n addItem.focusTrigger();\n }\n : undefined;\n\n // Inside `Add`, an empty filter has no chip of its own.\n const hasChip = !addItem || !!value.length;\n let trigger = null;\n if (hasChip && isMobile) {\n let fieldValue = emptyText;\n if (value.length > 1) fieldValue = `${value.length} selected`;\n else if (value.length) fieldValue = singleText;\n trigger = (\n <FilterField\n {...rest}\n isOpen={open}\n label={multiple ? pluralLabel : label}\n onClick={() => onOpenChange(true)}\n onRemove={onRemove}\n ref={setRef}\n value={fieldValue}\n />\n );\n } else if (hasChip) {\n let chipProps: FilterChipProps = { value: emptyText };\n if (value.length > 1) chipProps = { label: pluralLabel, labelAfter: true, value: value.length };\n else if (value.length) chipProps = { label: `${label}:`, value: singleText };\n trigger = (\n <FilterChip {...chipProps} onRemove={onRemove} ref={setRef} removeLabel={`Remove ${label} filter`} {...rest} />\n );\n }\n\n // Emptied from its own chip, the chip goes, so focus moves to \"Add filter\", like on remove. Handed over\n // as the panel closes: on mobile, earlier would lose it to the closing view's focus trap.\n const chipPanelFinalFocusEl = addItem\n ? () => {\n if (!isEmptiedFromChip.current) return null;\n isEmptiedFromChip.current = false;\n const addTrigger = addItem.getTrigger();\n if (addTrigger instanceof HTMLButtonElement && !addTrigger.disabled) return addTrigger;\n addItem.focusTrigger();\n return null;\n }\n : undefined;\n\n // Both panels stay mounted whether or not a value is applied (the chip's one without its chip), so\n // neither is remounted, and each can still close and hand over focus once the value changes.\n return (\n <>\n <FilterPanel finalFocusEl={chipPanelFinalFocusEl} onOpenChange={onOpenChange} open={open} trigger={trigger}>\n {form(false)}\n </FilterPanel>\n {addPanel}\n </>\n );\n});\n\nBitkitFilterSelect.displayName = 'BitkitFilterSelect';\n\nexport default BitkitFilterSelect;\n"],"mappings":"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"}
1
+ {"version":3,"file":"BitkitFilterSelect.js","names":["_Fragment"],"sources":["../../../lib/components/BitkitFilter/BitkitFilterSelect.tsx"],"sourcesContent":["// eslint-disable-next-line no-restricted-imports\nimport { mergeRefs } from '@chakra-ui/react';\nimport { type BoxProps } from '@chakra-ui/react/box';\nimport { forwardRef, useEffect, useMemo, useRef, useState } from 'react';\n\nimport { useFilterAddItem, useFilterContext, useFilterRegistration } from './BitkitFilter.context';\nimport { isPattern, SEARCH_DEBOUNCE_MS, toRunningText } from './BitkitFilter.utils';\nimport FilterChip, { type FilterChipProps } from './components/FilterChip';\nimport FilterField from './components/FilterField';\nimport FilterPanel from './components/FilterPanel';\nimport FilterSelectForm, { type BitkitFilterSelectItem } from './components/FilterSelectForm';\n\nexport type { BitkitFilterSelectItem };\n\nconst NO_VALUE: string[] = [];\n\nexport interface BitkitFilterSelectProps extends Omit<BoxProps, 'children' | 'defaultValue' | 'onChange'> {\n /**\n * Why the filter doesn't apply yet, e.g. \"Select a project first\". Its entry in the \"Add filter\"\n * menu is muted with an info icon, and says this in a tooltip when highlighted (spelled out under\n * the label on mobile). Only applies inside `BitkitFilter.Add`. A filter with no options is\n * disabled anyway.\n */\n disabledReason?: string;\n /** Chip text while nothing is selected. Defaults to `All {pluralLabel}`. */\n emptyLabel?: string;\n items: BitkitFilterSelectItem[];\n /** Name of the filter: the popover title, and the prefix of a single selection on the chip (`Project: iOS`). */\n label: string;\n /** Shows a loading row in place of the options, e.g. while `onSearchChange` results are fetched. */\n loading?: boolean;\n /** Text of the loading row. Defaults to \"Loading...\". */\n loadingLabel?: string;\n multiple?: boolean;\n /**\n * Hands search over to you: `items` are shown as they are, without filtering them locally. Use it\n * for server-side search. Called once typing pauses (300ms), right away with `''` on clear and\n * close; ordering the responses is up to you. The search input is always shown with it; otherwise\n * only above 5 items.\n */\n onSearchChange?: (searchText: string) => void;\n /** Called on \"Apply\", \"Clear\" and the chip's remove button — not on every click in the popover. */\n onValueChange: (value: string[]) => void;\n /**\n * Offers a wildcard pattern (`release-*`) as an alternative to picking options. A pattern is\n * applied as the single value, e.g. `['release-*']`; matching it is up to you. The preview of\n * matching options only checks the labels of the `items` at hand, not a server search.\n */\n pattern?: boolean;\n /**\n * Plural of `label`: the empty chip (`All projects`), a multiple selection (`2 workflows`), and a\n * multi-select's panel title. Required, since a guessed plural reads wrong too often.\n */\n pluralLabel: string;\n /** Lists the options in the order of `items`, instead of the applied ones first. */\n preserveOrder?: boolean;\n /**\n * A value is always applied, so the popover offers no \"Clear\". Pass the default as `resetValue`\n * too, for \"Clear filters\" to restore. Not for a filter inside `BitkitFilter.Add`.\n */\n required?: boolean;\n /** The value \"Clear filters\" restores. Defaults to no selection. */\n resetValue?: string[];\n /** Defaults to \"Search for options\". */\n searchPlaceholder?: string;\n value: string[];\n}\n\n/**\n * A filter picking one or more options from a list, applied with the popover's \"Apply\". Directly\n * in `BitkitFilter` it is a dropdown chip that is always shown; inside `BitkitFilter.Add` it is a\n * removable chip that only appears while it holds a value.\n */\n// The ref lands on the chip's (or on mobile the field's) button; other props on its wrapper.\nconst BitkitFilterSelect = forwardRef<HTMLButtonElement, BitkitFilterSelectProps>((props, ref) => {\n const {\n disabledReason,\n emptyLabel,\n items,\n label,\n loading,\n loadingLabel,\n multiple,\n onSearchChange,\n onValueChange,\n pattern,\n pluralLabel,\n preserveOrder,\n required,\n resetValue = NO_VALUE,\n searchPlaceholder,\n value,\n ...rest\n } = props;\n const { isMobile } = useFilterContext();\n const [open, setOpen] = useState(false);\n const addItem = useFilterAddItem({\n disabledReason,\n hasOptions: !!items.length || !!onSearchChange,\n isEmpty: !value.length,\n label,\n });\n\n useFilterRegistration(value, resetValue, onValueChange);\n\n // Remember every option seen, so a selection keeps its label, group and icon once a search has replaced `items`.\n const knownItems = useRef(new Map<string, BitkitFilterSelectItem>());\n items.forEach((item) => knownItems.current.set(item.value, item));\n\n // A filter just added through \"Add filter\" hands focus to its new chip as its panel closes.\n const triggerRef = useRef<HTMLButtonElement>(null);\n const setRef = useMemo(() => mergeRefs<HTMLButtonElement>(ref, triggerRef), [ref]);\n const isFocusPending = useRef(false);\n // A filter emptied from its own chip loses the chip, so its panel hands focus to \"Add filter\" on close.\n const isEmptiedFromChip = useRef(false);\n\n // Debounced here rather than in the form, which outlives the close by its exit animation: closing\n // drops a pending search, so it can't land after the reset to `''`. Clearing goes through right away.\n const searchTimeout = useRef<ReturnType<typeof setTimeout>>(undefined);\n useEffect(() => () => clearTimeout(searchTimeout.current), []);\n const search = (text: string) => {\n clearTimeout(searchTimeout.current);\n if (text) searchTimeout.current = setTimeout(() => onSearchChange?.(text), SEARCH_DEBOUNCE_MS);\n else onSearchChange?.('');\n };\n\n const onOpenChange = (isOpen: boolean) => {\n setOpen(isOpen);\n if (!isOpen && onSearchChange) search('');\n };\n\n const form = (isAdding: boolean) => (\n <FilterSelectForm\n isEditing={!!value.length}\n isOrderFixed={preserveOrder}\n isRequired={required && !addItem}\n items={items}\n label={label}\n loading={loading}\n loadingLabel={loadingLabel}\n multiple={multiple}\n onApply={(next) => {\n if (isAdding) isFocusPending.current = true;\n onValueChange(next);\n if (isAdding) addItem?.stopAdding();\n else if (addItem && !next.length) isEmptiedFromChip.current = true;\n onOpenChange(false);\n }}\n onCancel={() => {\n if (isAdding) addItem?.stopAdding();\n onOpenChange(false);\n }}\n onSearchChange={onSearchChange ? search : undefined}\n pattern={pattern}\n pluralLabel={pluralLabel}\n searchPlaceholder={searchPlaceholder}\n selectedItems={value\n .filter((selected) => !isPattern(selected))\n .map((selected) => knownItems.current.get(selected) ?? { label: selected, value: selected })}\n value={value}\n />\n );\n\n // Inside `Add`, an empty filter has no chip: it is listed in the menu, and its panel opens from\n // there, anchored to the \"Add filter\" button. It stays mounted once a value is applied, so it can\n // close (and hand over focus) the usual way.\n const addPanel = addItem ? (\n <FilterPanel\n finalFocusEl={() => {\n const chip = isFocusPending.current ? triggerRef.current : null;\n isFocusPending.current = false;\n return chip ?? addItem.getTrigger();\n }}\n getAnchorElement={addItem.getTrigger}\n onOpenChange={(isOpen) => {\n if (isOpen) return;\n addItem.stopAdding();\n onOpenChange(false);\n }}\n open={addItem.isAdding}\n >\n {form(true)}\n </FilterPanel>\n ) : null;\n\n const emptyText = emptyLabel ?? `All ${toRunningText(pluralLabel)}`;\n const singleText = value.length === 1 ? (knownItems.current.get(value[0])?.label ?? value[0]) : '';\n const onRemove = addItem\n ? () => {\n onValueChange([]);\n addItem.focusTrigger();\n }\n : undefined;\n\n // Inside `Add`, an empty filter has no chip of its own.\n const hasChip = !addItem || !!value.length;\n let trigger = null;\n if (hasChip && isMobile) {\n let fieldValue = emptyText;\n if (value.length > 1) fieldValue = `${value.length} selected`;\n else if (value.length) fieldValue = singleText;\n trigger = (\n <FilterField\n {...rest}\n isOpen={open}\n label={multiple ? pluralLabel : label}\n onClick={() => onOpenChange(true)}\n onRemove={onRemove}\n ref={setRef}\n value={fieldValue}\n />\n );\n } else if (hasChip) {\n let chipProps: FilterChipProps = { value: emptyText };\n if (value.length > 1) chipProps = { label: toRunningText(pluralLabel), labelAfter: true, value: value.length };\n else if (value.length) chipProps = { label: `${label}:`, value: singleText };\n trigger = (\n <FilterChip {...chipProps} onRemove={onRemove} ref={setRef} removeLabel={`Remove ${label} filter`} {...rest} />\n );\n }\n\n // Emptied from its own chip, the chip goes, so focus moves to \"Add filter\", like on remove. Handed over\n // as the panel closes: on mobile, earlier would lose it to the closing view's focus trap.\n const chipPanelFinalFocusEl = addItem\n ? () => {\n if (!isEmptiedFromChip.current) return null;\n isEmptiedFromChip.current = false;\n const addTrigger = addItem.getTrigger();\n if (addTrigger instanceof HTMLButtonElement && !addTrigger.disabled) return addTrigger;\n addItem.focusTrigger();\n return null;\n }\n : undefined;\n\n // Both panels stay mounted whether or not a value is applied (the chip's one without its chip), so\n // neither is remounted, and each can still close and hand over focus once the value changes.\n return (\n <>\n <FilterPanel finalFocusEl={chipPanelFinalFocusEl} onOpenChange={onOpenChange} open={open} trigger={trigger}>\n {form(false)}\n </FilterPanel>\n {addPanel}\n </>\n );\n});\n\nBitkitFilterSelect.displayName = 'BitkitFilterSelect';\n\nexport default BitkitFilterSelect;\n"],"mappings":"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"}
@@ -5,7 +5,7 @@ export interface FilterChipProps extends Omit<BoxProps, 'children'> {
5
5
  /** Muted text before `value` — the filter's label (`Workflow:`), or the noun after a count. */
6
6
  label?: string;
7
7
  icon?: BitkitIconComponent;
8
- /** Puts `label` after `value`, for a count: `2 Workflows`. */
8
+ /** Puts `label` after `value`, for a count: `2 workflows`. */
9
9
  labelAfter?: boolean;
10
10
  /** Turns the chevron into a separate remove button. */
11
11
  onRemove?: () => void;
@@ -1 +1 @@
1
- {"version":3,"file":"FilterChip.js","names":[],"sources":["../../../../lib/components/BitkitFilter/components/FilterChip.tsx"],"sourcesContent":["import { Box, type BoxProps } from '@chakra-ui/react/box';\nimport { Popover } from '@chakra-ui/react/popover';\n// `chakra.button`: the chip's two halves are bare buttons styled by the filter recipe; BitkitButton's\n// recipe would fight the chip's own border, height and hover.\nimport { chakra } from '@chakra-ui/react/styled-system';\nimport { Text } from '@chakra-ui/react/text';\nimport { forwardRef, type ReactElement, type ReactNode } from 'react';\n\nimport { type BitkitIconComponent, IconChevronDown, IconCross } from '../../../icons';\nimport { useFilterContext } from '../BitkitFilter.context';\n\nexport interface FilterChipProps extends Omit<BoxProps, 'children'> {\n /** Muted text before `value` — the filter's label (`Workflow:`), or the noun after a count. */\n label?: string;\n icon?: BitkitIconComponent;\n /** Puts `label` after `value`, for a count: `2 Workflows`. */\n labelAfter?: boolean;\n /** Turns the chevron into a separate remove button. */\n onRemove?: () => void;\n /** Accessible name of the remove button. */\n removeLabel?: string;\n /**\n * Wraps the chip's button in whatever opens its popover. Defaults to `Popover.Trigger`, so the\n * chip must render inside a `Popover.Root` unless this is given.\n */\n renderTrigger?: (button: ReactElement) => ReactNode;\n value: ReactNode;\n}\n\nconst asPopoverTrigger = (button: ReactElement) => <Popover.Trigger asChild>{button}</Popover.Trigger>;\n\n/** The chip a filter shows in the bar: a button opening the filter's popover, and an optional remove button. */\nconst FilterChip = forwardRef<HTMLButtonElement, FilterChipProps>((props, ref) => {\n const {\n css,\n icon: Icon,\n label,\n labelAfter,\n onRemove,\n removeLabel,\n renderTrigger = asPopoverTrigger,\n value,\n ...rest\n } = props;\n const { isDisabled, styles } = useFilterContext();\n\n const labelNode = label ? (\n <Text as=\"span\" css={styles.chipLabel}>\n {label}\n </Text>\n ) : null;\n\n return (\n <Box css={[styles.chip, css]} {...rest}>\n {renderTrigger(\n // `aria-disabled` while the page loads, not `disabled`: a focus handed to the chip has to stay on it.\n // The panel ignores opening meanwhile.\n <chakra.button\n aria-disabled={isDisabled || undefined}\n data-disabled={isDisabled ? '' : undefined}\n css={styles.chipTrigger}\n ref={ref}\n type=\"button\"\n >\n {!!Icon && <Icon css={styles.chipIcon} data-chip-icon size=\"16\" />}\n {!labelAfter && labelNode}\n <Text as=\"span\" css={styles.chipValue}>\n {value}\n </Text>\n {labelAfter && labelNode}\n {!onRemove && <IconChevronDown css={styles.chipChevron} size=\"16\" />}\n </chakra.button>,\n )}\n {!!onRemove && (\n <chakra.button\n aria-label={removeLabel}\n aria-disabled={isDisabled || undefined}\n data-disabled={isDisabled ? '' : undefined}\n css={styles.chipRemove}\n onClick={isDisabled ? undefined : onRemove}\n type=\"button\"\n >\n <IconCross size=\"16\" />\n </chakra.button>\n )}\n </Box>\n );\n});\n\nFilterChip.displayName = 'FilterChip';\n\nexport default FilterChip;\n"],"mappings":";;;;;;;;;;AA6BA,IAAM,oBAAoB,WAAyB,oBAAC,QAAQ,SAAT;CAAiB,SAAA;CAAS,UAAA;AAAwB,CAAA;;AAGrG,IAAM,aAAa,YAAgD,OAAO,QAAQ;CAChF,MAAM,EACJ,KACA,MAAM,MACN,OACA,YACA,UACA,aACA,gBAAgB,kBAChB,OACA,GAAG,SACD;CACJ,MAAM,EAAE,YAAY,WAAW,iBAAiB;CAEhD,MAAM,YAAY,QAChB,oBAAC,MAAD;EAAM,IAAG;EAAO,KAAK,OAAO;EACzB,UAAA;CACG,CAAA,IACJ;CAEJ,OACE,qBAAC,KAAD;EAAK,KAAK,CAAC,OAAO,MAAM,GAAG;EAAG,GAAI;EAAlC,UAAA,CACG,cAGC,qBAAC,OAAO,QAAR;GACE,iBAAe,cAAc,KAAA;GAC7B,iBAAe,aAAa,KAAK,KAAA;GACjC,KAAK,OAAO;GACP;GACL,MAAK;GALP,UAAA;IAOG,CAAC,CAAC,QAAQ,oBAAC,MAAD;KAAM,KAAK,OAAO;KAAU,kBAAA;KAAe,MAAK;IAAM,CAAA;IAChE,CAAC,cAAc;IAChB,oBAAC,MAAD;KAAM,IAAG;KAAO,KAAK,OAAO;KACzB,UAAA;IACG,CAAA;IACL,cAAc;IACd,CAAC,YAAY,oBAAC,iBAAD;KAAiB,KAAK,OAAO;KAAa,MAAK;IAAM,CAAA;GACtD;EACjB,CAAA,CAAA,GACC,CAAC,CAAC,YACD,oBAAC,OAAO,QAAR;GACE,cAAY;GACZ,iBAAe,cAAc,KAAA;GAC7B,iBAAe,aAAa,KAAK,KAAA;GACjC,KAAK,OAAO;GACZ,SAAS,aAAa,KAAA,IAAY;GAClC,MAAK;GAEL,UAAA,oBAAC,WAAD,EAAW,MAAK,KAAM,CAAA;EACT,CAAA,CAEd;;AAET,CAAC;AAED,WAAW,cAAc"}
1
+ {"version":3,"file":"FilterChip.js","names":[],"sources":["../../../../lib/components/BitkitFilter/components/FilterChip.tsx"],"sourcesContent":["import { Box, type BoxProps } from '@chakra-ui/react/box';\nimport { Popover } from '@chakra-ui/react/popover';\n// `chakra.button`: the chip's two halves are bare buttons styled by the filter recipe; BitkitButton's\n// recipe would fight the chip's own border, height and hover.\nimport { chakra } from '@chakra-ui/react/styled-system';\nimport { Text } from '@chakra-ui/react/text';\nimport { forwardRef, type ReactElement, type ReactNode } from 'react';\n\nimport { type BitkitIconComponent, IconChevronDown, IconCross } from '../../../icons';\nimport { useFilterContext } from '../BitkitFilter.context';\n\nexport interface FilterChipProps extends Omit<BoxProps, 'children'> {\n /** Muted text before `value` — the filter's label (`Workflow:`), or the noun after a count. */\n label?: string;\n icon?: BitkitIconComponent;\n /** Puts `label` after `value`, for a count: `2 workflows`. */\n labelAfter?: boolean;\n /** Turns the chevron into a separate remove button. */\n onRemove?: () => void;\n /** Accessible name of the remove button. */\n removeLabel?: string;\n /**\n * Wraps the chip's button in whatever opens its popover. Defaults to `Popover.Trigger`, so the\n * chip must render inside a `Popover.Root` unless this is given.\n */\n renderTrigger?: (button: ReactElement) => ReactNode;\n value: ReactNode;\n}\n\nconst asPopoverTrigger = (button: ReactElement) => <Popover.Trigger asChild>{button}</Popover.Trigger>;\n\n/** The chip a filter shows in the bar: a button opening the filter's popover, and an optional remove button. */\nconst FilterChip = forwardRef<HTMLButtonElement, FilterChipProps>((props, ref) => {\n const {\n css,\n icon: Icon,\n label,\n labelAfter,\n onRemove,\n removeLabel,\n renderTrigger = asPopoverTrigger,\n value,\n ...rest\n } = props;\n const { isDisabled, styles } = useFilterContext();\n\n const labelNode = label ? (\n <Text as=\"span\" css={styles.chipLabel}>\n {label}\n </Text>\n ) : null;\n\n return (\n <Box css={[styles.chip, css]} {...rest}>\n {renderTrigger(\n // `aria-disabled` while the page loads, not `disabled`: a focus handed to the chip has to stay on it.\n // The panel ignores opening meanwhile.\n <chakra.button\n aria-disabled={isDisabled || undefined}\n data-disabled={isDisabled ? '' : undefined}\n css={styles.chipTrigger}\n ref={ref}\n type=\"button\"\n >\n {!!Icon && <Icon css={styles.chipIcon} data-chip-icon size=\"16\" />}\n {!labelAfter && labelNode}\n <Text as=\"span\" css={styles.chipValue}>\n {value}\n </Text>\n {labelAfter && labelNode}\n {!onRemove && <IconChevronDown css={styles.chipChevron} size=\"16\" />}\n </chakra.button>,\n )}\n {!!onRemove && (\n <chakra.button\n aria-label={removeLabel}\n aria-disabled={isDisabled || undefined}\n data-disabled={isDisabled ? '' : undefined}\n css={styles.chipRemove}\n onClick={isDisabled ? undefined : onRemove}\n type=\"button\"\n >\n <IconCross size=\"16\" />\n </chakra.button>\n )}\n </Box>\n );\n});\n\nFilterChip.displayName = 'FilterChip';\n\nexport default FilterChip;\n"],"mappings":";;;;;;;;;;AA6BA,IAAM,oBAAoB,WAAyB,oBAAC,QAAQ,SAAT;CAAiB,SAAA;CAAS,UAAA;AAAwB,CAAA;;AAGrG,IAAM,aAAa,YAAgD,OAAO,QAAQ;CAChF,MAAM,EACJ,KACA,MAAM,MACN,OACA,YACA,UACA,aACA,gBAAgB,kBAChB,OACA,GAAG,SACD;CACJ,MAAM,EAAE,YAAY,WAAW,iBAAiB;CAEhD,MAAM,YAAY,QAChB,oBAAC,MAAD;EAAM,IAAG;EAAO,KAAK,OAAO;EACzB,UAAA;CACG,CAAA,IACJ;CAEJ,OACE,qBAAC,KAAD;EAAK,KAAK,CAAC,OAAO,MAAM,GAAG;EAAG,GAAI;EAAlC,UAAA,CACG,cAGC,qBAAC,OAAO,QAAR;GACE,iBAAe,cAAc,KAAA;GAC7B,iBAAe,aAAa,KAAK,KAAA;GACjC,KAAK,OAAO;GACP;GACL,MAAK;GALP,UAAA;IAOG,CAAC,CAAC,QAAQ,oBAAC,MAAD;KAAM,KAAK,OAAO;KAAU,kBAAA;KAAe,MAAK;IAAM,CAAA;IAChE,CAAC,cAAc;IAChB,oBAAC,MAAD;KAAM,IAAG;KAAO,KAAK,OAAO;KACzB,UAAA;IACG,CAAA;IACL,cAAc;IACd,CAAC,YAAY,oBAAC,iBAAD;KAAiB,KAAK,OAAO;KAAa,MAAK;IAAM,CAAA;GACtD;EACjB,CAAA,CAAA,GACC,CAAC,CAAC,YACD,oBAAC,OAAO,QAAR;GACE,cAAY;GACZ,iBAAe,cAAc,KAAA;GAC7B,iBAAe,aAAa,KAAK,KAAA;GACjC,KAAK,OAAO;GACZ,SAAS,aAAa,KAAA,IAAY;GAClC,MAAK;GAEL,UAAA,oBAAC,WAAD,EAAW,MAAK,KAAM,CAAA;EACT,CAAA,CAEd;;AAET,CAAC;AAED,WAAW,cAAc"}
@@ -256,21 +256,25 @@ var FilterListbox = (props) => {
256
256
  selectionMode: multiple ? "multiple" : "single",
257
257
  size: isMobile ? "lg" : "md",
258
258
  value,
259
- children: [/* @__PURE__ */ jsx(Listbox.Label, {
260
- srOnly: true,
261
- children: label
262
- }), /* @__PURE__ */ jsxs(Listbox.Content, { children: [groups.map(([group, groupItems]) => group ? /* @__PURE__ */ jsxs(Listbox.ItemGroup, { children: [/* @__PURE__ */ jsx(Listbox.ItemGroupLabel, {
263
- asChild: true,
264
- children: /* @__PURE__ */ jsx(BitkitGroupHeading, {
265
- label: group,
266
- paddingBlockStart: "12",
267
- paddingInline: "24"
259
+ children: [
260
+ /* @__PURE__ */ jsx(Listbox.Label, {
261
+ srOnly: true,
262
+ children: label
263
+ }),
264
+ /* @__PURE__ */ jsx(Listbox.Content, { children: groups.map(([group, groupItems]) => group ? /* @__PURE__ */ jsxs(Listbox.ItemGroup, { children: [/* @__PURE__ */ jsx(Listbox.ItemGroupLabel, {
265
+ asChild: true,
266
+ children: /* @__PURE__ */ jsx(BitkitGroupHeading, {
267
+ label: group,
268
+ paddingBlockStart: "12",
269
+ paddingInline: "24"
270
+ })
271
+ }), groupItems.map(renderItem)] }, group) : groupItems.map(renderItem)) }),
272
+ showLimitHelper && /* @__PURE__ */ jsx(Box, {
273
+ paddingBlockStart: "8",
274
+ paddingInline: "24",
275
+ children: /* @__PURE__ */ jsx(LimitHelper, {})
268
276
  })
269
- }), groupItems.map(renderItem)] }, group) : groupItems.map(renderItem)), showLimitHelper && /* @__PURE__ */ jsx(Box, {
270
- paddingBlockStart: "8",
271
- paddingInline: "24",
272
- children: /* @__PURE__ */ jsx(LimitHelper, {})
273
- })] })]
277
+ ]
274
278
  });
275
279
  };
276
280
  var FilterPatternBody = ({ items, onValueChange, pluralLabel, value }) => {
@@ -300,10 +304,18 @@ var FilterPatternBody = ({ items, onValueChange, pluralLabel, value }) => {
300
304
  ")"
301
305
  ]
302
306
  });
303
- else results = /* @__PURE__ */ jsx(BitkitList_default, {
307
+ else results = /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(BitkitList_default, {
304
308
  size: "md",
305
- children: matches.map((item) => /* @__PURE__ */ jsx(BitkitList_default.Item, { children: item.label }, item.value))
306
- });
309
+ children: matches.slice(0, 100).map((item) => /* @__PURE__ */ jsx(BitkitList_default.Item, { children: item.label }, item.value))
310
+ }), matches.length > 100 && /* @__PURE__ */ jsxs(Text, {
311
+ css: styles.listHelperText,
312
+ marginBlockStart: "16",
313
+ children: [
314
+ "Showing the first ",
315
+ 100,
316
+ " matches."
317
+ ]
318
+ })] });
307
319
  return /* @__PURE__ */ jsxs(Box, {
308
320
  css: styles.pattern,
309
321
  "data-filter-pattern": "",
@@ -1 +1 @@
1
- {"version":3,"file":"FilterSelectForm.js","names":["_Fragment","BitkitList"],"sources":["../../../../lib/components/BitkitFilter/components/FilterSelectForm.tsx"],"sourcesContent":["import { Box } from '@chakra-ui/react/box';\nimport { createListCollection } from '@chakra-ui/react/collection';\nimport { Listbox } from '@chakra-ui/react/listbox';\nimport { HStack } from '@chakra-ui/react/stack';\nimport { useSlotRecipe } from '@chakra-ui/react/styled-system';\nimport { Text } from '@chakra-ui/react/text';\nimport { Fragment, type ReactNode, useEffect, useId, useMemo, useRef, useState } from 'react';\n\nimport { type BitkitIconComponent, IconCheck } from '../../../icons';\nimport { type BitkitColorToken } from '../../../theme/themeUtils';\nimport BitkitCheckbox from '../../BitkitCheckbox/BitkitCheckbox';\nimport BitkitCheckboxGroup from '../../BitkitCheckboxGroup/BitkitCheckboxGroup';\nimport BitkitGroupHeading from '../../BitkitGroupHeading/BitkitGroupHeading';\nimport BitkitInlineLoading from '../../BitkitInlineLoading/BitkitInlineLoading';\nimport BitkitList from '../../BitkitList/BitkitList';\nimport BitkitSearchInput from '../../BitkitSearchInput/BitkitSearchInput';\nimport BitkitTextInput from '../../BitkitTextInput/BitkitTextInput';\nimport { useFilterContext } from '../BitkitFilter.context';\nimport { compilePattern, isEqualFilterValue, isPattern, toRunningText } from '../BitkitFilter.utils';\nimport { FilterPanelFooter, FilterPanelHeader } from './FilterPanel';\nimport FilterSearchInput from './FilterSearchInput';\n\nexport interface BitkitFilterSelectItem {\n /**\n * Lists the option under this heading, with the others of the same group. Only the open list shows\n * it: the chip shows the label alone, so the label has to make sense without its group.\n */\n group?: string;\n icon?: BitkitIconComponent;\n /** Colors the icon, e.g. a status: `icon/positive` for \"Passed\". */\n iconColor?: BitkitColorToken<'icon'>;\n label: string;\n /** Unique across all groups. */\n value: string;\n}\n\n/**\n * Gathers the options by `group`, so the list (and its keyboard order) runs group by group. The groups\n * keep their order in `items`, with the ungrouped options (an empty-named group) first; within a\n * group the options keep their order in `options`, e.g. the applied ones first.\n */\nconst groupItems = (options: BitkitFilterSelectItem[], items: BitkitFilterSelectItem[]) => {\n const groups = new Map<string, BitkitFilterSelectItem[]>([['', []]]);\n [...items, ...options].forEach((item) => {\n if (!groups.has(item.group ?? '')) groups.set(item.group ?? '', []);\n });\n options.forEach((option) => groups.get(option.group ?? '')?.push(option));\n return [...groups].filter(([, groupOptions]) => groupOptions.length);\n};\n\n/** The list renders at most this many options; search narrows down the rest. */\nexport const MAX_VISIBLE_ITEMS = 100;\n\n/** Below this many options the list is short enough to scan, so there is no search input. */\nconst SEARCH_THRESHOLD = 5;\n\nexport interface FilterSelectFormProps {\n /** The filter holds a value: the footer offers \"Clear\" instead of the wildcard-mode switch. */\n isEditing: boolean;\n /** Keeps `items` in their own order instead of listing the applied ones first — for a switch's fixed options. */\n isOrderFixed?: boolean;\n /** A value is always applied: no \"Clear\" in the footer. */\n isRequired?: boolean;\n items: BitkitFilterSelectItem[];\n label: string;\n loading?: boolean;\n loadingLabel?: string;\n multiple?: boolean;\n onApply: (value: string[]) => void;\n onCancel: () => void;\n onSearchChange?: (searchText: string) => void;\n pattern?: boolean;\n pluralLabel: string;\n searchPlaceholder?: string;\n /**\n * The applied selection with its labels. Without a search, any of it missing from `items` — e.g.\n * left behind by a server search — is listed first, so it can still be unchecked.\n */\n selectedItems?: BitkitFilterSelectItem[];\n value: string[];\n}\n\n/** The body of a select filter's panel: search, the option list or wildcard pattern, and the footer. */\nconst FilterSelectForm = (props: FilterSelectFormProps) => {\n const {\n isEditing,\n isOrderFixed,\n isRequired,\n items,\n label,\n loading,\n loadingLabel = 'Loading...',\n multiple,\n onApply,\n onCancel,\n onSearchChange,\n pattern,\n pluralLabel,\n searchPlaceholder = 'Search for options',\n selectedItems = [],\n value,\n } = props;\n const { isMobile, styles } = useFilterContext();\n\n const [draft, setDraft] = useState(value);\n const [mode, setMode] = useState<'list' | 'pattern'>(pattern && value[0] && isPattern(value[0]) ? 'pattern' : 'list');\n const [searchText, setSearchText] = useState('');\n // Set once the options have loaded, so a later reload (a search, or clearing it) keeps the search and footer.\n const [hasLoaded, setHasLoaded] = useState(!loading);\n if (!loading && !hasLoaded) setHasLoaded(true);\n\n const onSearchTextChange = (text: string) => {\n setSearchText(text);\n onSearchChange?.(text);\n };\n\n // With `onSearchChange` the consumer filters `items` itself, e.g. on the server. Without a search,\n // the applied options come first. `value` doesn't change while the panel is open, so the order is\n // set when it opens and rows don't jump as options are (un)checked.\n const matchingItems = useMemo(() => {\n if (!searchText) {\n if (isOrderFixed) return items;\n const applied = value.flatMap((selected) => {\n const item = items.find((option) => option.value === selected);\n return item ?? selectedItems.find((option) => option.value === selected) ?? [];\n });\n return [...applied, ...items.filter((item) => !value.includes(item.value))];\n }\n if (onSearchChange) return items;\n const query = searchText.toLowerCase();\n return items.filter((item) => item.label.toLowerCase().includes(query));\n }, [isOrderFixed, items, onSearchChange, searchText, selectedItems, value]);\n\n const visibleGroups = useMemo(\n () => groupItems(matchingItems.slice(0, MAX_VISIBLE_ITEMS), items),\n [items, matchingItems],\n );\n const visibleItems = visibleGroups.flatMap(([, groupItems]) => groupItems);\n // The first load shows only the title and a loading row; later loads keep the input and footer in place.\n const isInitialLoading = !!loading && !hasLoaded;\n const showSearch = !isInitialLoading && (!!onSearchChange || items.length > SEARCH_THRESHOLD);\n const searchLabel = `Search ${toRunningText(pluralLabel)}`;\n const showLimitHelper = matchingItems.length > MAX_VISIBLE_ITEMS;\n\n const patternValue = draft[0] ?? '';\n const isApplyDisabled =\n isEqualFilterValue(draft, value) ||\n (mode === 'pattern' && !(patternValue && isPattern(patternValue))) ||\n (!!isRequired && !draft.length);\n\n // Switching between the list and the pattern moves focus into the new view, so typing goes there.\n const bodyRef = useRef<HTMLDivElement>(null);\n const hasSwitchedMode = useRef(false);\n useEffect(() => {\n if (!hasSwitchedMode.current) return;\n bodyRef.current\n ?.querySelector<HTMLElement>('input:not([type=hidden]), [role=listbox], input[type=checkbox]')\n ?.focus();\n }, [mode]);\n\n let start;\n if (isEditing && !isRequired) {\n start = { label: multiple && isMobile ? 'Clear all' : 'Clear', onClick: () => onApply([]) };\n } else if (pattern && !isEditing) {\n start = {\n label: mode === 'list' ? 'Use wildcard pattern' : 'Select from list',\n onClick: () => {\n hasSwitchedMode.current = true;\n setMode(mode === 'list' ? 'pattern' : 'list');\n setDraft([]);\n },\n };\n }\n\n let list;\n if (loading) {\n list = <BitkitInlineLoading css={styles.loading} label={loadingLabel} size={isMobile ? 'lg' : 'md'} />;\n } else if (!visibleItems.length) {\n list = (\n <Text css={styles.loading} color=\"text/secondary\" textStyle={isMobile ? 'body/lg/regular' : 'body/md/regular'}>\n {searchText ? 'No result. Refine your search term.' : 'No options available.'}\n </Text>\n );\n } else if (multiple && !isMobile) {\n list = (\n <Box css={styles.checkboxList}>\n <BitkitCheckboxGroup aria-label={label} onValueChange={setDraft} size=\"md\" value={draft}>\n {visibleGroups.map(([group, groupItems]) => {\n const checkboxes = groupItems.map((item) => (\n <BitkitCheckbox\n key={item.value}\n labelText={\n item.icon ? (\n <HStack gap=\"4\">\n <item.icon color={item.iconColor} size=\"16\" />\n {item.label}\n </HStack>\n ) : (\n item.label\n )\n }\n value={item.value}\n />\n ));\n return group ? (\n <CheckboxOptionGroup key={group} label={group}>\n {checkboxes}\n </CheckboxOptionGroup>\n ) : (\n <Fragment key={group}>{checkboxes}</Fragment>\n );\n })}\n </BitkitCheckboxGroup>\n {showLimitHelper && <LimitHelper marginBlockStart=\"16\" />}\n </Box>\n );\n } else {\n list = (\n <FilterListbox\n groups={visibleGroups}\n items={visibleItems}\n label={label}\n multiple={multiple}\n onValueChange={setDraft}\n showLimitHelper={showLimitHelper}\n value={draft}\n />\n );\n }\n\n let search = null;\n if (showSearch && isMobile) {\n search = (\n <FilterSearchInput\n label={searchLabel}\n onValueChange={onSearchTextChange}\n placeholder={searchPlaceholder}\n value={searchText}\n />\n );\n } else if (showSearch) {\n search = (\n <Box css={styles.popoverSearch} data-filter-search=\"\">\n <BitkitSearchInput\n label={searchLabel}\n onChange={onSearchTextChange}\n placeholder={searchPlaceholder}\n size=\"md\"\n value={searchText}\n />\n </Box>\n );\n }\n\n const body = (\n <Box css={styles.popoverBody} ref={bodyRef}>\n {mode === 'pattern' ? (\n <FilterPatternBody items={items} onValueChange={setDraft} pluralLabel={pluralLabel} value={patternValue} />\n ) : (\n <>\n {search}\n {list}\n </>\n )}\n </Box>\n );\n\n return (\n <>\n <FilterPanelHeader\n count={multiple && mode === 'list' ? draft.length : undefined}\n title={multiple ? pluralLabel : label}\n />\n {isMobile ? (\n <Box flex=\"1\" overflowY=\"auto\">\n {body}\n </Box>\n ) : (\n body\n )}\n {!isInitialLoading && (\n <FilterPanelFooter\n applyLabel={multiple && mode === 'list' && draft.length ? `Apply (${draft.length})` : undefined}\n isApplyDisabled={isApplyDisabled}\n onApply={() => onApply(mode === 'pattern' ? [patternValue] : draft)}\n onCancel={onCancel}\n start={start}\n />\n )}\n </>\n );\n};\n\n/** A named group of the desktop checkboxes, so a screen reader tells which group an option is in. */\nconst CheckboxOptionGroup = ({ children, label }: { children: ReactNode; label: string }) => {\n const headingId = useId();\n\n return (\n // Stacked like the checkbox group's own children, which its flex column no longer reaches in here.\n <Box aria-labelledby={headingId} display=\"flex\" flexDirection=\"column\" gap=\"12\" role=\"group\">\n {/* The list pads 20; the heading lines up 24 from the popover's edge, like the listbox's. */}\n <BitkitGroupHeading id={headingId} label={label} paddingBlockStart=\"12\" paddingInline=\"4\" />\n {children}\n </Box>\n );\n};\n\nconst LimitHelper = ({ marginBlockStart }: { marginBlockStart?: '16' }) => {\n const { styles } = useFilterContext();\n\n return (\n <Text css={styles.listHelperText} marginBlockStart={marginBlockStart}>\n Showing the first {MAX_VISIBLE_ITEMS} options. Search to find more.\n </Text>\n );\n};\n\ninterface FilterListboxProps {\n groups: [string, BitkitFilterSelectItem[]][];\n /** `groups` flattened, in the same order: the collection's (and so the keyboard's) order. */\n items: BitkitFilterSelectItem[];\n label: string;\n multiple?: boolean;\n onValueChange: (value: string[]) => void;\n showLimitHelper: boolean;\n value: string[];\n}\n\n/**\n * The option list for a single select, and on mobile for a multiple one too — there the rows are\n * full-width and highlighted when selected, with a checkbox drawn in front.\n */\nconst FilterListbox = (props: FilterListboxProps) => {\n const { groups, items, label, multiple, onValueChange, showLimitHelper, value } = props;\n const { isMobile, styles } = useFilterContext();\n const collection = useMemo(() => createListCollection({ items }), [items]);\n const checkboxStyles = useSlotRecipe({ key: 'checkbox' })({ size: 'lg' });\n\n const checkIndicator = (\n <Listbox.ItemIndicator>\n <IconCheck size=\"16\" />\n </Listbox.ItemIndicator>\n );\n\n const renderItem = (item: BitkitFilterSelectItem) => {\n const state = value.includes(item.value) ? 'checked' : 'unchecked';\n const check = multiple ? (\n // Drawn, not a real checkbox: the option row is the control, and a nested input would be a second one.\n <Box aria-hidden css={[checkboxStyles.control, styles.mobileListCheckbox]} data-state={state}>\n <IconCheck />\n </Box>\n ) : (\n <Box aria-hidden css={styles.mobileListCheck} data-state={state}>\n <IconCheck size=\"16\" />\n </Box>\n );\n return (\n <Listbox.Item item={item} key={item.value}>\n {isMobile && check}\n {!!item.icon && <item.icon color={item.iconColor} size={isMobile ? '24' : '16'} />}\n <Listbox.ItemText>{item.label}</Listbox.ItemText>\n {!isMobile && checkIndicator}\n </Listbox.Item>\n );\n };\n\n return (\n <Listbox.Root\n collection={collection}\n onValueChange={(details) => onValueChange(details.value)}\n selectionMode={multiple ? 'multiple' : 'single'}\n size={isMobile ? 'lg' : 'md'}\n value={value}\n >\n {/* Zag points the content's aria-labelledby at the label, so it has to exist. */}\n <Listbox.Label srOnly>{label}</Listbox.Label>\n <Listbox.Content>\n {/* Ungrouped options sit straight in the list: a group around them would have no name. */}\n {groups.map(([group, groupItems]) =>\n group ? (\n <Listbox.ItemGroup key={group}>\n <Listbox.ItemGroupLabel asChild>\n <BitkitGroupHeading label={group} paddingBlockStart=\"12\" paddingInline=\"24\" />\n </Listbox.ItemGroupLabel>\n {groupItems.map(renderItem)}\n </Listbox.ItemGroup>\n ) : (\n groupItems.map(renderItem)\n ),\n )}\n {showLimitHelper && (\n <Box paddingBlockStart=\"8\" paddingInline=\"24\">\n <LimitHelper />\n </Box>\n )}\n </Listbox.Content>\n </Listbox.Root>\n );\n};\n\ninterface FilterPatternBodyProps {\n items: BitkitFilterSelectItem[];\n onValueChange: (value: string[]) => void;\n pluralLabel: string;\n value: string;\n}\n\nconst FilterPatternBody = ({ items, onValueChange, pluralLabel, value }: FilterPatternBodyProps) => {\n const { isMobile, styles } = useFilterContext();\n const noun = toRunningText(pluralLabel);\n const matches = useMemo(() => {\n if (!isPattern(value)) return null;\n const regExp = compilePattern(value);\n return items.filter((item) => !isPattern(item.label) && regExp.test(item.label));\n }, [items, value]);\n\n let results;\n if (!matches) {\n results = (\n <Text color=\"text/secondary\" textStyle=\"body/md/regular\">\n Enter a pattern with the wildcard &lsquo;*&rsquo; to view {noun} with matching names.\n </Text>\n );\n } else if (!matches.length) {\n results = (\n <Text color=\"text/tertiary\" textStyle=\"body/md/regular\">\n (no matching {noun})\n </Text>\n );\n } else {\n results = (\n <BitkitList size=\"md\">\n {matches.map((item) => (\n <BitkitList.Item key={item.value}>{item.label}</BitkitList.Item>\n ))}\n </BitkitList>\n );\n }\n\n return (\n <Box css={styles.pattern} data-filter-pattern=\"\">\n <BitkitTextInput\n helperText=\"Allowed wildcard character: *\"\n label=\"Wildcard pattern\"\n onValueChange={(text) => onValueChange(text ? [text] : [])}\n placeholder=\"(e.g. *main*, release-*)\"\n size={isMobile ? 'lg' : 'md'}\n value={value}\n />\n <Box css={styles.patternResults}>\n <Text css={styles.patternResultsLabel}>Matching results{matches?.length ? ` (${matches.length})` : ''}</Text>\n {results}\n </Box>\n </Box>\n );\n};\n\nexport default FilterSelectForm;\n"],"mappings":"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"}
1
+ {"version":3,"file":"FilterSelectForm.js","names":["_Fragment","BitkitList"],"sources":["../../../../lib/components/BitkitFilter/components/FilterSelectForm.tsx"],"sourcesContent":["import { Box } from '@chakra-ui/react/box';\nimport { createListCollection } from '@chakra-ui/react/collection';\nimport { Listbox } from '@chakra-ui/react/listbox';\nimport { HStack } from '@chakra-ui/react/stack';\nimport { useSlotRecipe } from '@chakra-ui/react/styled-system';\nimport { Text } from '@chakra-ui/react/text';\nimport { Fragment, type ReactNode, useEffect, useId, useMemo, useRef, useState } from 'react';\n\nimport { type BitkitIconComponent, IconCheck } from '../../../icons';\nimport { type BitkitColorToken } from '../../../theme/themeUtils';\nimport BitkitCheckbox from '../../BitkitCheckbox/BitkitCheckbox';\nimport BitkitCheckboxGroup from '../../BitkitCheckboxGroup/BitkitCheckboxGroup';\nimport BitkitGroupHeading from '../../BitkitGroupHeading/BitkitGroupHeading';\nimport BitkitInlineLoading from '../../BitkitInlineLoading/BitkitInlineLoading';\nimport BitkitList from '../../BitkitList/BitkitList';\nimport BitkitSearchInput from '../../BitkitSearchInput/BitkitSearchInput';\nimport BitkitTextInput from '../../BitkitTextInput/BitkitTextInput';\nimport { useFilterContext } from '../BitkitFilter.context';\nimport { compilePattern, isEqualFilterValue, isPattern, toRunningText } from '../BitkitFilter.utils';\nimport { FilterPanelFooter, FilterPanelHeader } from './FilterPanel';\nimport FilterSearchInput from './FilterSearchInput';\n\nexport interface BitkitFilterSelectItem {\n /**\n * Lists the option under this heading, with the others of the same group. Only the open list shows\n * it: the chip shows the label alone, so the label has to make sense without its group.\n */\n group?: string;\n icon?: BitkitIconComponent;\n /** Colors the icon, e.g. a status: `icon/positive` for \"Passed\". */\n iconColor?: BitkitColorToken<'icon'>;\n label: string;\n /** Unique across all groups. */\n value: string;\n}\n\n/**\n * Gathers the options by `group`, so the list (and its keyboard order) runs group by group. The groups\n * keep their order in `items`, with the ungrouped options (an empty-named group) first; within a\n * group the options keep their order in `options`, e.g. the applied ones first.\n */\nconst groupItems = (options: BitkitFilterSelectItem[], items: BitkitFilterSelectItem[]) => {\n const groups = new Map<string, BitkitFilterSelectItem[]>([['', []]]);\n [...items, ...options].forEach((item) => {\n if (!groups.has(item.group ?? '')) groups.set(item.group ?? '', []);\n });\n options.forEach((option) => groups.get(option.group ?? '')?.push(option));\n return [...groups].filter(([, groupOptions]) => groupOptions.length);\n};\n\n/** The list renders at most this many options; search narrows down the rest. */\nexport const MAX_VISIBLE_ITEMS = 100;\n\n/** Below this many options the list is short enough to scan, so there is no search input. */\nconst SEARCH_THRESHOLD = 5;\n\nexport interface FilterSelectFormProps {\n /** The filter holds a value: the footer offers \"Clear\" instead of the wildcard-mode switch. */\n isEditing: boolean;\n /** Keeps `items` in their own order instead of listing the applied ones first — for a switch's fixed options. */\n isOrderFixed?: boolean;\n /** A value is always applied: no \"Clear\" in the footer. */\n isRequired?: boolean;\n items: BitkitFilterSelectItem[];\n label: string;\n loading?: boolean;\n loadingLabel?: string;\n multiple?: boolean;\n onApply: (value: string[]) => void;\n onCancel: () => void;\n onSearchChange?: (searchText: string) => void;\n pattern?: boolean;\n pluralLabel: string;\n searchPlaceholder?: string;\n /**\n * The applied selection with its labels. Without a search, any of it missing from `items` — e.g.\n * left behind by a server search — is listed first, so it can still be unchecked.\n */\n selectedItems?: BitkitFilterSelectItem[];\n value: string[];\n}\n\n/** The body of a select filter's panel: search, the option list or wildcard pattern, and the footer. */\nconst FilterSelectForm = (props: FilterSelectFormProps) => {\n const {\n isEditing,\n isOrderFixed,\n isRequired,\n items,\n label,\n loading,\n loadingLabel = 'Loading...',\n multiple,\n onApply,\n onCancel,\n onSearchChange,\n pattern,\n pluralLabel,\n searchPlaceholder = 'Search for options',\n selectedItems = [],\n value,\n } = props;\n const { isMobile, styles } = useFilterContext();\n\n const [draft, setDraft] = useState(value);\n const [mode, setMode] = useState<'list' | 'pattern'>(pattern && value[0] && isPattern(value[0]) ? 'pattern' : 'list');\n const [searchText, setSearchText] = useState('');\n // Set once the options have loaded, so a later reload (a search, or clearing it) keeps the search and footer.\n const [hasLoaded, setHasLoaded] = useState(!loading);\n if (!loading && !hasLoaded) setHasLoaded(true);\n\n const onSearchTextChange = (text: string) => {\n setSearchText(text);\n onSearchChange?.(text);\n };\n\n // With `onSearchChange` the consumer filters `items` itself, e.g. on the server. Without a search,\n // the applied options come first. `value` doesn't change while the panel is open, so the order is\n // set when it opens and rows don't jump as options are (un)checked.\n const matchingItems = useMemo(() => {\n if (!searchText) {\n if (isOrderFixed) return items;\n const applied = value.flatMap((selected) => {\n const item = items.find((option) => option.value === selected);\n return item ?? selectedItems.find((option) => option.value === selected) ?? [];\n });\n return [...applied, ...items.filter((item) => !value.includes(item.value))];\n }\n if (onSearchChange) return items;\n const query = searchText.toLowerCase();\n return items.filter((item) => item.label.toLowerCase().includes(query));\n }, [isOrderFixed, items, onSearchChange, searchText, selectedItems, value]);\n\n const visibleGroups = useMemo(\n () => groupItems(matchingItems.slice(0, MAX_VISIBLE_ITEMS), items),\n [items, matchingItems],\n );\n const visibleItems = visibleGroups.flatMap(([, groupItems]) => groupItems);\n // The first load shows only the title and a loading row; later loads keep the input and footer in place.\n const isInitialLoading = !!loading && !hasLoaded;\n const showSearch = !isInitialLoading && (!!onSearchChange || items.length > SEARCH_THRESHOLD);\n const searchLabel = `Search ${toRunningText(pluralLabel)}`;\n const showLimitHelper = matchingItems.length > MAX_VISIBLE_ITEMS;\n\n const patternValue = draft[0] ?? '';\n const isApplyDisabled =\n isEqualFilterValue(draft, value) ||\n (mode === 'pattern' && !(patternValue && isPattern(patternValue))) ||\n (!!isRequired && !draft.length);\n\n // Switching between the list and the pattern moves focus into the new view, so typing goes there.\n const bodyRef = useRef<HTMLDivElement>(null);\n const hasSwitchedMode = useRef(false);\n useEffect(() => {\n if (!hasSwitchedMode.current) return;\n bodyRef.current\n ?.querySelector<HTMLElement>('input:not([type=hidden]), [role=listbox], input[type=checkbox]')\n ?.focus();\n }, [mode]);\n\n let start;\n if (isEditing && !isRequired) {\n start = { label: multiple && isMobile ? 'Clear all' : 'Clear', onClick: () => onApply([]) };\n } else if (pattern && !isEditing) {\n start = {\n label: mode === 'list' ? 'Use wildcard pattern' : 'Select from list',\n onClick: () => {\n hasSwitchedMode.current = true;\n setMode(mode === 'list' ? 'pattern' : 'list');\n setDraft([]);\n },\n };\n }\n\n let list;\n if (loading) {\n list = <BitkitInlineLoading css={styles.loading} label={loadingLabel} size={isMobile ? 'lg' : 'md'} />;\n } else if (!visibleItems.length) {\n list = (\n <Text css={styles.loading} color=\"text/secondary\" textStyle={isMobile ? 'body/lg/regular' : 'body/md/regular'}>\n {searchText ? 'No result. Refine your search term.' : 'No options available.'}\n </Text>\n );\n } else if (multiple && !isMobile) {\n list = (\n <Box css={styles.checkboxList}>\n <BitkitCheckboxGroup aria-label={label} onValueChange={setDraft} size=\"md\" value={draft}>\n {visibleGroups.map(([group, groupItems]) => {\n const checkboxes = groupItems.map((item) => (\n <BitkitCheckbox\n key={item.value}\n labelText={\n item.icon ? (\n <HStack gap=\"4\">\n <item.icon color={item.iconColor} size=\"16\" />\n {item.label}\n </HStack>\n ) : (\n item.label\n )\n }\n value={item.value}\n />\n ));\n return group ? (\n <CheckboxOptionGroup key={group} label={group}>\n {checkboxes}\n </CheckboxOptionGroup>\n ) : (\n <Fragment key={group}>{checkboxes}</Fragment>\n );\n })}\n </BitkitCheckboxGroup>\n {showLimitHelper && <LimitHelper marginBlockStart=\"16\" />}\n </Box>\n );\n } else {\n list = (\n <FilterListbox\n groups={visibleGroups}\n items={visibleItems}\n label={label}\n multiple={multiple}\n onValueChange={setDraft}\n showLimitHelper={showLimitHelper}\n value={draft}\n />\n );\n }\n\n let search = null;\n if (showSearch && isMobile) {\n search = (\n <FilterSearchInput\n label={searchLabel}\n onValueChange={onSearchTextChange}\n placeholder={searchPlaceholder}\n value={searchText}\n />\n );\n } else if (showSearch) {\n search = (\n <Box css={styles.popoverSearch} data-filter-search=\"\">\n <BitkitSearchInput\n label={searchLabel}\n onChange={onSearchTextChange}\n placeholder={searchPlaceholder}\n size=\"md\"\n value={searchText}\n />\n </Box>\n );\n }\n\n const body = (\n <Box css={styles.popoverBody} ref={bodyRef}>\n {mode === 'pattern' ? (\n <FilterPatternBody items={items} onValueChange={setDraft} pluralLabel={pluralLabel} value={patternValue} />\n ) : (\n <>\n {search}\n {list}\n </>\n )}\n </Box>\n );\n\n return (\n <>\n <FilterPanelHeader\n count={multiple && mode === 'list' ? draft.length : undefined}\n title={multiple ? pluralLabel : label}\n />\n {isMobile ? (\n <Box flex=\"1\" overflowY=\"auto\">\n {body}\n </Box>\n ) : (\n body\n )}\n {!isInitialLoading && (\n <FilterPanelFooter\n applyLabel={multiple && mode === 'list' && draft.length ? `Apply (${draft.length})` : undefined}\n isApplyDisabled={isApplyDisabled}\n onApply={() => onApply(mode === 'pattern' ? [patternValue] : draft)}\n onCancel={onCancel}\n start={start}\n />\n )}\n </>\n );\n};\n\n/** A named group of the desktop checkboxes, so a screen reader tells which group an option is in. */\nconst CheckboxOptionGroup = ({ children, label }: { children: ReactNode; label: string }) => {\n const headingId = useId();\n\n return (\n // Stacked like the checkbox group's own children, which its flex column no longer reaches in here.\n <Box aria-labelledby={headingId} display=\"flex\" flexDirection=\"column\" gap=\"12\" role=\"group\">\n {/* The list pads 20; the heading lines up 24 from the popover's edge, like the listbox's. */}\n <BitkitGroupHeading id={headingId} label={label} paddingBlockStart=\"12\" paddingInline=\"4\" />\n {children}\n </Box>\n );\n};\n\nconst LimitHelper = ({ marginBlockStart }: { marginBlockStart?: '16' }) => {\n const { styles } = useFilterContext();\n\n return (\n <Text css={styles.listHelperText} marginBlockStart={marginBlockStart}>\n Showing the first {MAX_VISIBLE_ITEMS} options. Search to find more.\n </Text>\n );\n};\n\ninterface FilterListboxProps {\n groups: [string, BitkitFilterSelectItem[]][];\n /** `groups` flattened, in the same order: the collection's (and so the keyboard's) order. */\n items: BitkitFilterSelectItem[];\n label: string;\n multiple?: boolean;\n onValueChange: (value: string[]) => void;\n showLimitHelper: boolean;\n value: string[];\n}\n\n/**\n * The option list for a single select, and on mobile for a multiple one too — there the rows are\n * full-width and highlighted when selected, with a checkbox drawn in front.\n */\nconst FilterListbox = (props: FilterListboxProps) => {\n const { groups, items, label, multiple, onValueChange, showLimitHelper, value } = props;\n const { isMobile, styles } = useFilterContext();\n const collection = useMemo(() => createListCollection({ items }), [items]);\n const checkboxStyles = useSlotRecipe({ key: 'checkbox' })({ size: 'lg' });\n\n const checkIndicator = (\n <Listbox.ItemIndicator>\n <IconCheck size=\"16\" />\n </Listbox.ItemIndicator>\n );\n\n const renderItem = (item: BitkitFilterSelectItem) => {\n const state = value.includes(item.value) ? 'checked' : 'unchecked';\n const check = multiple ? (\n // Drawn, not a real checkbox: the option row is the control, and a nested input would be a second one.\n <Box aria-hidden css={[checkboxStyles.control, styles.mobileListCheckbox]} data-state={state}>\n <IconCheck />\n </Box>\n ) : (\n <Box aria-hidden css={styles.mobileListCheck} data-state={state}>\n <IconCheck size=\"16\" />\n </Box>\n );\n return (\n <Listbox.Item item={item} key={item.value}>\n {isMobile && check}\n {!!item.icon && <item.icon color={item.iconColor} size={isMobile ? '24' : '16'} />}\n <Listbox.ItemText>{item.label}</Listbox.ItemText>\n {!isMobile && checkIndicator}\n </Listbox.Item>\n );\n };\n\n return (\n <Listbox.Root\n collection={collection}\n onValueChange={(details) => onValueChange(details.value)}\n selectionMode={multiple ? 'multiple' : 'single'}\n size={isMobile ? 'lg' : 'md'}\n value={value}\n >\n {/* Zag points the content's aria-labelledby at the label, so it has to exist. */}\n <Listbox.Label srOnly>{label}</Listbox.Label>\n <Listbox.Content>\n {/* Ungrouped options sit straight in the list: a group around them would have no name. */}\n {groups.map(([group, groupItems]) =>\n group ? (\n <Listbox.ItemGroup key={group}>\n <Listbox.ItemGroupLabel asChild>\n <BitkitGroupHeading label={group} paddingBlockStart=\"12\" paddingInline=\"24\" />\n </Listbox.ItemGroupLabel>\n {groupItems.map(renderItem)}\n </Listbox.ItemGroup>\n ) : (\n groupItems.map(renderItem)\n ),\n )}\n </Listbox.Content>\n {/* Outside the content, whose `listbox` role allows only options and groups. */}\n {showLimitHelper && (\n <Box paddingBlockStart=\"8\" paddingInline=\"24\">\n <LimitHelper />\n </Box>\n )}\n </Listbox.Root>\n );\n};\n\ninterface FilterPatternBodyProps {\n items: BitkitFilterSelectItem[];\n onValueChange: (value: string[]) => void;\n pluralLabel: string;\n value: string;\n}\n\nconst FilterPatternBody = ({ items, onValueChange, pluralLabel, value }: FilterPatternBodyProps) => {\n const { isMobile, styles } = useFilterContext();\n const noun = toRunningText(pluralLabel);\n const matches = useMemo(() => {\n if (!isPattern(value)) return null;\n const regExp = compilePattern(value);\n return items.filter((item) => !isPattern(item.label) && regExp.test(item.label));\n }, [items, value]);\n\n let results;\n if (!matches) {\n results = (\n <Text color=\"text/secondary\" textStyle=\"body/md/regular\">\n Enter a pattern with the wildcard &lsquo;*&rsquo; to view {noun} with matching names.\n </Text>\n );\n } else if (!matches.length) {\n results = (\n <Text color=\"text/tertiary\" textStyle=\"body/md/regular\">\n (no matching {noun})\n </Text>\n );\n } else {\n results = (\n <>\n <BitkitList size=\"md\">\n {matches.slice(0, MAX_VISIBLE_ITEMS).map((item) => (\n <BitkitList.Item key={item.value}>{item.label}</BitkitList.Item>\n ))}\n </BitkitList>\n {matches.length > MAX_VISIBLE_ITEMS && (\n <Text css={styles.listHelperText} marginBlockStart=\"16\">\n Showing the first {MAX_VISIBLE_ITEMS} matches.\n </Text>\n )}\n </>\n );\n }\n\n return (\n <Box css={styles.pattern} data-filter-pattern=\"\">\n <BitkitTextInput\n helperText=\"Allowed wildcard character: *\"\n label=\"Wildcard pattern\"\n onValueChange={(text) => onValueChange(text ? [text] : [])}\n placeholder=\"(e.g. *main*, release-*)\"\n size={isMobile ? 'lg' : 'md'}\n value={value}\n />\n <Box css={styles.patternResults}>\n <Text css={styles.patternResultsLabel}>Matching results{matches?.length ? ` (${matches.length})` : ''}</Text>\n {results}\n </Box>\n </Box>\n );\n};\n\nexport default FilterSelectForm;\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@bitrise/bitkit-v2",
3
3
  "private": false,
4
- "version": "0.3.481-beta.3065",
4
+ "version": "0.3.481-beta.3067",
5
5
  "description": "Bitrise Design System Components built with Chakra UI V3",
6
6
  "keywords": [
7
7
  "react",