@bitrise/bitkit-v2 0.3.481-beta.3101 → 0.3.481-beta.3105

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. package/dist/components/BitkitCalendar/BitkitCalendar.d.ts +0 -12
  2. package/dist/components/BitkitCalendar/BitkitCalendar.js +1 -7
  3. package/dist/components/BitkitCalendar/BitkitCalendar.js.map +1 -1
  4. package/dist/components/BitkitCalendar/components/CalendarContent.d.ts +1 -5
  5. package/dist/components/BitkitCalendar/components/CalendarContent.js +43 -60
  6. package/dist/components/BitkitCalendar/components/CalendarContent.js.map +1 -1
  7. package/dist/components/BitkitCalendar/components/CalendarMonth.d.ts +2 -4
  8. package/dist/components/BitkitCalendar/components/CalendarMonth.js +5 -15
  9. package/dist/components/BitkitCalendar/components/CalendarMonth.js.map +1 -1
  10. package/dist/components/BitkitCalendar/components/CalendarMonthSelect.d.ts +1 -2
  11. package/dist/components/BitkitCalendar/components/CalendarMonthSelect.js +2 -3
  12. package/dist/components/BitkitCalendar/components/CalendarMonthSelect.js.map +1 -1
  13. package/dist/components/BitkitCalendar/components/CalendarNextTrigger.d.ts +1 -3
  14. package/dist/components/BitkitCalendar/components/CalendarNextTrigger.js +3 -3
  15. package/dist/components/BitkitCalendar/components/CalendarNextTrigger.js.map +1 -1
  16. package/dist/components/BitkitCalendar/components/CalendarPrevTrigger.d.ts +1 -3
  17. package/dist/components/BitkitCalendar/components/CalendarPrevTrigger.js +3 -3
  18. package/dist/components/BitkitCalendar/components/CalendarPrevTrigger.js.map +1 -1
  19. package/dist/components/BitkitCalendar/components/CalendarSelect.d.ts +0 -1
  20. package/dist/components/BitkitCalendar/components/CalendarSelect.js +3 -5
  21. package/dist/components/BitkitCalendar/components/CalendarSelect.js.map +1 -1
  22. package/dist/components/BitkitCalendar/components/CalendarTrigger.js +0 -1
  23. package/dist/components/BitkitCalendar/components/CalendarTrigger.js.map +1 -1
  24. package/dist/components/BitkitCalendar/components/CalendarYearSelect.d.ts +1 -2
  25. package/dist/components/BitkitCalendar/components/CalendarYearSelect.js +2 -3
  26. package/dist/components/BitkitCalendar/components/CalendarYearSelect.js.map +1 -1
  27. package/dist/components/BitkitDatePicker/BitkitDatePicker.d.ts +0 -2
  28. package/dist/components/BitkitDatePicker/BitkitDatePicker.js +1 -2
  29. package/dist/components/BitkitDatePicker/BitkitDatePicker.js.map +1 -1
  30. package/dist/components/BitkitLabelTooltip/BitkitLabelTooltip.d.ts +3 -4
  31. package/dist/components/BitkitLabelTooltip/BitkitLabelTooltip.js +2 -3
  32. package/dist/components/BitkitLabelTooltip/BitkitLabelTooltip.js.map +1 -1
  33. package/dist/components/BitkitMarkdown/BitkitMarkdown.js +1 -1
  34. package/dist/components/BitkitNoteCard/BitkitNoteCard.js +1 -1
  35. package/dist/components/BitkitPageSidebar/BitkitPageSidebar.js +1 -1
  36. package/dist/components/BitkitTextInput/BitkitTextInput.js +13 -16
  37. package/dist/components/BitkitTextInput/BitkitTextInput.js.map +1 -1
  38. package/dist/components/BitkitUIHeader/BitkitUIHeader.d.ts +41 -0
  39. package/dist/components/BitkitUIHeader/BitkitUIHeader.js +114 -0
  40. package/dist/components/BitkitUIHeader/BitkitUIHeader.js.map +1 -0
  41. package/dist/components/BitkitUIHeader/BitriseLogo.d.ts +10 -0
  42. package/dist/components/BitkitUIHeader/BitriseLogo.js +86 -0
  43. package/dist/components/BitkitUIHeader/BitriseLogo.js.map +1 -0
  44. package/dist/components/BitkitUIHeader/UIHeaderAccount.d.ts +26 -0
  45. package/dist/components/BitkitUIHeader/UIHeaderAccount.js +40 -0
  46. package/dist/components/BitkitUIHeader/UIHeaderAccount.js.map +1 -0
  47. package/dist/components/BitkitUIHeader/UIHeaderButton.d.ts +34 -0
  48. package/dist/components/BitkitUIHeader/UIHeaderButton.js +45 -0
  49. package/dist/components/BitkitUIHeader/UIHeaderButton.js.map +1 -0
  50. package/dist/components/BitkitUIHeader/UIHeaderContext.d.ts +13 -0
  51. package/dist/components/BitkitUIHeader/UIHeaderContext.js +11 -0
  52. package/dist/components/BitkitUIHeader/UIHeaderContext.js.map +1 -0
  53. package/dist/components/BitkitUIHeader/UIHeaderControl.d.ts +65 -0
  54. package/dist/components/BitkitUIHeader/UIHeaderControl.js +72 -0
  55. package/dist/components/BitkitUIHeader/UIHeaderControl.js.map +1 -0
  56. package/dist/components/BitkitUIHeader/UIHeaderNavButton.d.ts +20 -0
  57. package/dist/components/BitkitUIHeader/UIHeaderNavButton.js +46 -0
  58. package/dist/components/BitkitUIHeader/UIHeaderNavButton.js.map +1 -0
  59. package/dist/components/BitkitUIHeader/UIHeaderSearch.d.ts +32 -0
  60. package/dist/components/BitkitUIHeader/UIHeaderSearch.js +121 -0
  61. package/dist/components/BitkitUIHeader/UIHeaderSearch.js.map +1 -0
  62. package/dist/components/BitkitUIHeader/uiHeaderPart.d.ts +14 -0
  63. package/dist/components/BitkitUIHeader/uiHeaderPart.js +18 -0
  64. package/dist/components/BitkitUIHeader/uiHeaderPart.js.map +1 -0
  65. package/dist/components/index.d.ts +1 -6
  66. package/dist/main.js +9 -8
  67. package/dist/theme/recipes/UIHeaderButton.recipe.js +103 -0
  68. package/dist/theme/recipes/UIHeaderButton.recipe.js.map +1 -0
  69. package/dist/theme/recipes/index.js +3 -1
  70. package/dist/theme/recipes/index.js.map +1 -1
  71. package/dist/theme/semantic-tokens/index.d.ts +5 -0
  72. package/dist/theme/semantic-tokens/semanticColors.d.ts +5 -0
  73. package/dist/theme/semantic-tokens/semanticColors.js +1 -0
  74. package/dist/theme/semantic-tokens/semanticColors.js.map +1 -1
  75. package/dist/theme/slot-recipes/DatePicker.recipe.d.ts +0 -15
  76. package/dist/theme/slot-recipes/DatePicker.recipe.js +22 -72
  77. package/dist/theme/slot-recipes/DatePicker.recipe.js.map +1 -1
  78. package/dist/theme/slot-recipes/DatePickerSelect.recipe.d.ts +0 -85
  79. package/dist/theme/slot-recipes/DatePickerSelect.recipe.js +10 -35
  80. package/dist/theme/slot-recipes/DatePickerSelect.recipe.js.map +1 -1
  81. package/dist/theme/slot-recipes/Menu.recipe.js +1 -6
  82. package/dist/theme/slot-recipes/Menu.recipe.js.map +1 -1
  83. package/dist/theme/slot-recipes/Tag.recipe.d.ts +1 -1
  84. package/dist/theme/slot-recipes/UIHeader.recipe.d.ts +14 -0
  85. package/dist/theme/slot-recipes/UIHeader.recipe.js +253 -0
  86. package/dist/theme/slot-recipes/UIHeader.recipe.js.map +1 -0
  87. package/dist/theme/slot-recipes/UISideNav.recipe.d.ts +1 -2
  88. package/dist/theme/slot-recipes/UISideNav.recipe.js +2 -3
  89. package/dist/theme/slot-recipes/UISideNav.recipe.js.map +1 -1
  90. package/dist/theme/slot-recipes/index.d.ts +3 -158
  91. package/dist/theme/slot-recipes/index.js +2 -4
  92. package/dist/theme/slot-recipes/index.js.map +1 -1
  93. package/dist/theme/themeUtils.d.ts +6 -0
  94. package/dist/theme/themeUtils.js +7 -1
  95. package/dist/theme/themeUtils.js.map +1 -1
  96. package/dist/utilities/BitkitPortalContext.js +2 -3
  97. package/dist/utilities/BitkitPortalContext.js.map +1 -1
  98. package/docs/v1-to-v2-migration-guide.md +1 -48
  99. package/package.json +1 -1
  100. package/dist/components/BitkitFilter/BitkitFilter.context.d.ts +0 -65
  101. package/dist/components/BitkitFilter/BitkitFilter.context.js +0 -82
  102. package/dist/components/BitkitFilter/BitkitFilter.context.js.map +0 -1
  103. package/dist/components/BitkitFilter/BitkitFilter.d.ts +0 -54
  104. package/dist/components/BitkitFilter/BitkitFilter.js +0 -243
  105. package/dist/components/BitkitFilter/BitkitFilter.js.map +0 -1
  106. package/dist/components/BitkitFilter/BitkitFilter.utils.d.ts +0 -18
  107. package/dist/components/BitkitFilter/BitkitFilter.utils.js +0 -42
  108. package/dist/components/BitkitFilter/BitkitFilter.utils.js.map +0 -1
  109. package/dist/components/BitkitFilter/BitkitFilterAdd.d.ts +0 -18
  110. package/dist/components/BitkitFilter/BitkitFilterAdd.js +0 -200
  111. package/dist/components/BitkitFilter/BitkitFilterAdd.js.map +0 -1
  112. package/dist/components/BitkitFilter/BitkitFilterDateRange.d.ts +0 -26
  113. package/dist/components/BitkitFilter/BitkitFilterDateRange.js +0 -118
  114. package/dist/components/BitkitFilter/BitkitFilterDateRange.js.map +0 -1
  115. package/dist/components/BitkitFilter/BitkitFilterSearch.d.ts +0 -23
  116. package/dist/components/BitkitFilter/BitkitFilterSearch.js +0 -72
  117. package/dist/components/BitkitFilter/BitkitFilterSearch.js.map +0 -1
  118. package/dist/components/BitkitFilter/BitkitFilterSelect.d.ts +0 -61
  119. package/dist/components/BitkitFilter/BitkitFilterSelect.js +0 -149
  120. package/dist/components/BitkitFilter/BitkitFilterSelect.js.map +0 -1
  121. package/dist/components/BitkitFilter/BitkitFilterSwitch.d.ts +0 -28
  122. package/dist/components/BitkitFilter/BitkitFilterSwitch.js +0 -87
  123. package/dist/components/BitkitFilter/BitkitFilterSwitch.js.map +0 -1
  124. package/dist/components/BitkitFilter/components/FilterChip.d.ts +0 -23
  125. package/dist/components/BitkitFilter/components/FilterChip.js +0 -66
  126. package/dist/components/BitkitFilter/components/FilterChip.js.map +0 -1
  127. package/dist/components/BitkitFilter/components/FilterField.d.ts +0 -15
  128. package/dist/components/BitkitFilter/components/FilterField.js +0 -60
  129. package/dist/components/BitkitFilter/components/FilterField.js.map +0 -1
  130. package/dist/components/BitkitFilter/components/FilterPanel.d.ts +0 -42
  131. package/dist/components/BitkitFilter/components/FilterPanel.js +0 -126
  132. package/dist/components/BitkitFilter/components/FilterPanel.js.map +0 -1
  133. package/dist/components/BitkitFilter/components/FilterSearchInput.d.ts +0 -15
  134. package/dist/components/BitkitFilter/components/FilterSearchInput.js +0 -41
  135. package/dist/components/BitkitFilter/components/FilterSearchInput.js.map +0 -1
  136. package/dist/components/BitkitFilter/components/FilterSelectForm.d.ts +0 -45
  137. package/dist/components/BitkitFilter/components/FilterSelectForm.js +0 -342
  138. package/dist/components/BitkitFilter/components/FilterSelectForm.js.map +0 -1
  139. package/dist/theme/recipes/index.d.ts +0 -525
  140. package/dist/theme/slot-recipes/Filter.recipe.d.ts +0 -35
  141. package/dist/theme/slot-recipes/Filter.recipe.js +0 -652
  142. package/dist/theme/slot-recipes/Filter.recipe.js.map +0 -1
  143. package/dist/theme/slot-recipes/Listbox.recipe.d.ts +0 -22
  144. package/dist/theme/slot-recipes/Listbox.recipe.js +0 -108
  145. package/dist/theme/slot-recipes/Listbox.recipe.js.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"BitkitFilter.js","names":[],"sources":["../../../lib/components/BitkitFilter/BitkitFilter.tsx"],"sourcesContent":["import { Box, type BoxProps } from '@chakra-ui/react/box';\nimport { Drawer } from '@chakra-ui/react/drawer';\nimport { Portal } from '@chakra-ui/react/portal';\n// `chakra.button`: the mobile \"Filters\" button carries a count badge, which BitkitButton's string-only label can't.\nimport { chakra, useSlotRecipe } from '@chakra-ui/react/styled-system';\nimport { forwardRef, type ReactNode, useCallback, useEffect, useMemo, useRef, useState } from 'react';\n\nimport useResponsive from '../../hooks/useResponsive';\nimport { IconCross, IconFilter } from '../../icons';\nimport { usePortalContext } from '../../utilities/BitkitPortalContext';\nimport { flattenChildren } from '../../utilities/flattenChildren';\nimport { withSubComponents } from '../../utilities/withSubComponents';\nimport BitkitBadge from '../BitkitBadge/BitkitBadge';\nimport BitkitButton from '../BitkitButton/BitkitButton';\nimport BitkitCloseButton from '../BitkitCloseButton/BitkitCloseButton';\nimport { FilterContextProvider, type FilterStatus } from './BitkitFilter.context';\nimport BitkitFilterAdd from './BitkitFilterAdd';\nimport BitkitFilterDateRange from './BitkitFilterDateRange';\nimport BitkitFilterSearch from './BitkitFilterSearch';\nimport BitkitFilterSelect from './BitkitFilterSelect';\nimport BitkitFilterSwitch from './BitkitFilterSwitch';\n\nexport interface BitkitFilterProps extends Omit<BoxProps, 'children'> {\n /**\n * The filters: `BitkitFilter.Switch`, `.Select`, `.DateRange`, `.Add` and `.Search`, in the\n * order they appear. `.Search` always sits at the end of the bar.\n */\n children: ReactNode;\n /**\n * Where a capped content block sits: `center` over a centred page column, `start` over one that\n * starts against a sidebar. Pass the same alignment as the page header and content below it.\n * Only applies with a `contentWidth` in rems. Desktop only.\n */\n contentAlign?: 'center' | 'start';\n /**\n * Lines the bar up with the page content below it, for a bar under a page header: it gains a\n * bottom border and the page's inline padding. Pass the content column's `max-width` in rems to\n * cap the bar's content with it, or `'full'` for a full-width column. Either is padded 32 on both sides.\n *\n * Without it the bar has no padding of its own: place it with style props, like any block. Desktop only.\n */\n contentWidth?: `${number}rem` | 'full';\n /**\n * Forces the desktop bar or the mobile \"Filters\" dialog. Defaults to the viewport: mobile below the\n * tablet breakpoint. The mobile dialog is made for narrow screens; from tablet up it is a 320px drawer.\n */\n device?: 'desktop' | 'mobile';\n /**\n * Called once on \"Clear filters\" in place of handing each changed filter its `resetValue` — for a\n * page that resets them all in one write, e.g. one URL update. The `resetValue`s still decide when\n * the button shows.\n */\n onClear?: () => void;\n /**\n * Shows \"Clear filters\" while any filter differs from its `resetValue`. It hands each of those\n * filters its `resetValue` back through its own `onValueChange`, or calls `onClear`.\n */\n showClear?: boolean;\n /** Shows the filter icon at the start of the bar. Desktop only. */\n showIcon?: boolean;\n /** `loading`: while the page's data loads, the filters can't be opened, removed or cleared. The search stays usable. */\n state?: 'loading';\n}\n\nconst toggle = (set: ReadonlySet<string>, id: string, isIn: boolean) => {\n if (set.has(id) === isIn) return set;\n const next = new Set(set);\n if (isIn) next.add(id);\n else next.delete(id);\n return next;\n};\n\n/**\n * A bar of filters above a list. Each filter is a controlled sub-component with its own `value` and\n * `onValueChange`; the bar lays them out and offers \"Clear filters\" across all of them.\n *\n * Below the tablet breakpoint the bar collapses into a \"Filters\" button opening a full-screen dialog,\n * where every filter is a field opening its own view. A filter still applies on its view's \"Apply\".\n */\nconst BitkitFilter = forwardRef<HTMLDivElement, BitkitFilterProps>((props, ref) => {\n const {\n children,\n contentAlign,\n contentWidth,\n css,\n device,\n onClear,\n showClear = true,\n showIcon = true,\n state,\n ...rest\n } = props;\n const { isMobile: isMobileViewport } = useResponsive();\n const isMobile = device ? device === 'mobile' : isMobileViewport;\n const isDisabled = state === 'loading';\n const { disablePortal } = usePortalContext();\n const [isDialogOpen, setIsDialogOpen] = useState(false);\n\n const recipe = useSlotRecipe({ key: 'filter' });\n let pageLayout: 'capped' | 'full' | undefined;\n if (contentWidth) pageLayout = contentWidth === 'full' ? 'full' : 'capped';\n const styles = recipe({ contentAlign: pageLayout === 'capped' ? contentAlign : undefined, pageLayout });\n\n const resets = useRef(new Map<string, () => void>());\n const [dirtyIds, setDirtyIds] = useState<ReadonlySet<string>>(new Set());\n const [activeIds, setActiveIds] = useState<ReadonlySet<string>>(new Set());\n\n const register = useCallback((id: string, reset: () => void) => {\n resets.current.set(id, reset);\n }, []);\n\n const setStatus = useCallback((id: string, { isActive, isDirty }: FilterStatus) => {\n setDirtyIds((prev) => toggle(prev, id, isDirty));\n setActiveIds((prev) => toggle(prev, id, isActive));\n }, []);\n\n const unregister = useCallback(\n (id: string) => {\n resets.current.delete(id);\n setStatus(id, { isActive: false, isDirty: false });\n },\n [setStatus],\n );\n\n const context = useMemo(\n () => ({ isDisabled, isMobile, register, setStatus, styles, unregister }),\n [isDisabled, isMobile, register, setStatus, styles, unregister],\n );\n\n const filters: ReactNode[] = [];\n const searches: ReactNode[] = [];\n // Flattened, so a filter grouped in a fragment is still sorted to the right place.\n flattenChildren(children).forEach((child) => {\n if (child.type === BitkitFilterSearch) searches.push(child);\n else filters.push(child);\n });\n\n const isDirty = dirtyIds.size > 0;\n const activeCount = activeIds.size;\n // The \"Clear filters\" button goes away (or is disabled) once nothing differs, so focus then moves\n // to the first control of the cleared filters rather than dropping to the page. Only if it hasn't\n // gone anywhere else meanwhile, e.g. while a slow reset was under way.\n const focusTarget = useRef<HTMLDivElement>(null);\n const isFocusPending = useRef(false);\n const clearAll = () => {\n if (isDisabled) return;\n isFocusPending.current = true;\n // Called off once focus lands anywhere, so a clear that never fully lands can't pull it in later.\n document.addEventListener(\n 'focusin',\n () => {\n isFocusPending.current = false;\n },\n { once: true },\n );\n if (onClear) onClear();\n else dirtyIds.forEach((id) => resets.current.get(id)?.());\n };\n useEffect(() => {\n if (!isFocusPending.current || isDirty) return;\n isFocusPending.current = false;\n const active = document.activeElement;\n if (active && active !== document.body && !active.matches('[data-filter-clear]')) return;\n focusTarget.current?.querySelector<HTMLElement>('button:not(:disabled), input:not(:disabled)')?.focus();\n }, [isDirty]);\n\n // While the page loads, the controls are only `aria-disabled`, so they keep a focus handed to them.\n // `data-disabled` takes them out of the hover and active styles too.\n const disabledProps = { 'aria-disabled': isDisabled || undefined, 'data-disabled': isDisabled ? '' : undefined };\n const clearProps = { ...disabledProps, 'data-filter-clear': '', onClick: clearAll };\n\n if (isMobile) {\n return (\n <FilterContextProvider value={context}>\n <Box css={css} ref={ref} {...rest}>\n <Box css={styles.mobileBar} ref={isDialogOpen ? undefined : focusTarget}>\n <chakra.button\n aria-expanded={isDialogOpen}\n aria-haspopup=\"dialog\"\n aria-label={`Filters, ${activeCount} applied`}\n {...disabledProps}\n css={styles.mobileTrigger}\n onClick={() => !isDisabled && setIsDialogOpen(true)}\n type=\"button\"\n >\n <IconFilter size=\"16\" />\n Filters\n <BitkitBadge colorVariant={activeCount ? 'purple' : 'neutral'}>{activeCount}</BitkitBadge>\n </chakra.button>\n {showClear && isDirty && (\n <BitkitButton {...clearProps} icon={IconCross} size=\"sm\" variant=\"tertiary\">\n Clear filters\n </BitkitButton>\n )}\n </Box>\n {searches}\n {/* Kept mounted while closed: the filters inside register their values with the bar. */}\n <Drawer.Root\n lazyMount={false}\n onOpenChange={({ open }) => setIsDialogOpen(open)}\n open={isDialogOpen}\n unmountOnExit={false}\n >\n <Portal disabled={disablePortal}>\n <Drawer.Positioner>\n <Drawer.Content>\n <Box css={styles.mobileHeader}>\n <Drawer.Title css={styles.mobileTitle} unstyled>\n Filters\n </Drawer.Title>\n <Drawer.CloseTrigger asChild unstyled>\n <BitkitCloseButton size=\"sm\" />\n </Drawer.CloseTrigger>\n </Box>\n <Drawer.Body css={styles.mobileBody} ref={isDialogOpen ? focusTarget : undefined}>\n {filters}\n </Drawer.Body>\n <Drawer.Footer>\n <Box css={styles.mobileFooter}>\n {showClear && (\n <BitkitButton\n {...clearProps}\n size=\"md\"\n state={isDirty ? undefined : 'disabled'}\n variant=\"secondary\"\n >\n Clear filters\n </BitkitButton>\n )}\n <BitkitButton onClick={() => setIsDialogOpen(false)} size=\"md\">\n Show results\n </BitkitButton>\n </Box>\n </Drawer.Footer>\n </Drawer.Content>\n </Drawer.Positioner>\n </Portal>\n </Drawer.Root>\n </Box>\n </FilterContextProvider>\n );\n }\n\n return (\n <FilterContextProvider value={context}>\n <Box css={[styles.root, css]} ref={ref} {...rest}>\n <Box css={styles.content} maxWidth={contentWidth === 'full' ? undefined : contentWidth} ref={focusTarget}>\n <Box css={styles.filters}>\n {/* In the wrapping row with the filters, so a second row starts under the icon, not beside it. */}\n <Box css={styles.items}>\n {showIcon && (\n <Box css={styles.icon}>\n <IconFilter />\n </Box>\n )}\n {filters}\n </Box>\n {showClear && isDirty && (\n <BitkitButton {...clearProps} flexShrink=\"0\" icon={IconCross} size=\"sm\" variant=\"tertiary\">\n Clear filters\n </BitkitButton>\n )}\n </Box>\n {searches.length > 0 && <Box css={styles.searchBlock}>{searches}</Box>}\n </Box>\n </Box>\n </FilterContextProvider>\n );\n});\n\nBitkitFilter.displayName = 'BitkitFilter';\n\nexport default withSubComponents(BitkitFilter, {\n Add: BitkitFilterAdd,\n DateRange: BitkitFilterDateRange,\n Search: BitkitFilterSearch,\n Select: BitkitFilterSelect,\n Switch: BitkitFilterSwitch,\n});\n"],"mappings":"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"}
@@ -1,18 +0,0 @@
1
- export type FilterValue = string | string[] | Date[] | null;
2
- /** Typing in a search settles for this long before its callback fires. */
3
- export declare const SEARCH_DEBOUNCE_MS = 300;
4
- /** Compares option lists as sets, since checking options in another order picks the same ones; dates by position. */
5
- export declare const isEqualFilterValue: (a: FilterValue, b: FilterValue) => boolean;
6
- /** A filter value that holds no selection. */
7
- export declare const isEmptyFilterValue: (value: FilterValue) => boolean;
8
- export declare const isPattern: (value: string) => boolean;
9
- /** Compiles a wildcard `pattern`, where `*` stands for any run of characters, to match whole texts. */
10
- export declare const compilePattern: (pattern: string) => RegExp;
11
- /**
12
- * Lowercases a noun for running text, keeping any word with a capital after its first letter as an
13
- * acronym or name: "CI Providers" reads "CI providers", "PRs" and "iOS apps" stay as they are.
14
- */
15
- export declare const toRunningText: (noun: string) => string;
16
- export declare const formatDateRange: (range: Date[]) => string;
17
- /** The last moment of the day `date` falls on, so a range ending that day includes all of it. */
18
- export declare const endOfDay: (date: Date) => Date;
@@ -1,42 +0,0 @@
1
- //#region lib/components/BitkitFilter/BitkitFilter.utils.ts
2
- /** Compares option lists as sets, since checking options in another order picks the same ones; dates by position. */
3
- var isEqualFilterValue = (a, b) => {
4
- if (Array.isArray(a) && Array.isArray(b)) {
5
- if (a.length !== b.length) return false;
6
- if (a.every((item) => typeof item === "string")) {
7
- const others = new Set(b);
8
- return a.every((item) => others.has(item));
9
- }
10
- return a.every((item, index) => {
11
- const other = b[index];
12
- return item instanceof Date && other instanceof Date ? item.getTime() === other.getTime() : item === other;
13
- });
14
- }
15
- return a === b;
16
- };
17
- /** A filter value that holds no selection. */
18
- var isEmptyFilterValue = (value) => value === null || value === "" || Array.isArray(value) && value.length === 0;
19
- var regExpEscape = (text) => text.replace(/[|\\{}()[\]^$+?.]/g, "\\$&");
20
- var isPattern = (value) => value.includes("*");
21
- /** Compiles a wildcard `pattern`, where `*` stands for any run of characters, to match whole texts. */
22
- var compilePattern = (pattern) => new RegExp(`^${pattern.split(/\*+/).map(regExpEscape).join(".*")}$`);
23
- /**
24
- * Lowercases a noun for running text, keeping any word with a capital after its first letter as an
25
- * acronym or name: "CI Providers" reads "CI providers", "PRs" and "iOS apps" stay as they are.
26
- */
27
- var toRunningText = (noun) => noun.split(" ").map((word) => /[A-Z]/.test(word.slice(1)) ? word : word.toLowerCase()).join(" ");
28
- var dateFormat = new Intl.DateTimeFormat("en-US", {
29
- day: "numeric",
30
- month: "short"
31
- });
32
- var formatDateRange = (range) => range.map((date) => dateFormat.format(date)).join(" – ");
33
- /** The last moment of the day `date` falls on, so a range ending that day includes all of it. */
34
- var endOfDay = (date) => {
35
- const end = new Date(date);
36
- end.setHours(23, 59, 59, 999);
37
- return end;
38
- };
39
- //#endregion
40
- export { compilePattern, endOfDay, formatDateRange, isEmptyFilterValue, isEqualFilterValue, isPattern, toRunningText };
41
-
42
- //# sourceMappingURL=BitkitFilter.utils.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"BitkitFilter.utils.js","names":[],"sources":["../../../lib/components/BitkitFilter/BitkitFilter.utils.ts"],"sourcesContent":["export type FilterValue = string | string[] | Date[] | null;\n\n/** Typing in a search settles for this long before its callback fires. */\nexport const SEARCH_DEBOUNCE_MS = 300;\n\n/** Compares option lists as sets, since checking options in another order picks the same ones; dates by position. */\nexport const isEqualFilterValue = (a: FilterValue, b: FilterValue): boolean => {\n if (Array.isArray(a) && Array.isArray(b)) {\n if (a.length !== b.length) return false;\n if (a.every((item) => typeof item === 'string')) {\n const others = new Set<unknown>(b);\n return a.every((item) => others.has(item));\n }\n return a.every((item, index) => {\n const other = b[index];\n return item instanceof Date && other instanceof Date ? item.getTime() === other.getTime() : item === other;\n });\n }\n return a === b;\n};\n\n/** A filter value that holds no selection. */\nexport const isEmptyFilterValue = (value: FilterValue): boolean =>\n value === null || value === '' || (Array.isArray(value) && value.length === 0);\n\nconst regExpEscape = (text: string) => text.replace(/[|\\\\{}()[\\]^$+?.]/g, '\\\\$&');\n\nexport const isPattern = (value: string) => value.includes('*');\n\n/** Compiles a wildcard `pattern`, where `*` stands for any run of characters, to match whole texts. */\nexport const compilePattern = (pattern: string) => new RegExp(`^${pattern.split(/\\*+/).map(regExpEscape).join('.*')}$`);\n\n/**\n * Lowercases a noun for running text, keeping any word with a capital after its first letter as an\n * acronym or name: \"CI Providers\" reads \"CI providers\", \"PRs\" and \"iOS apps\" stay as they are.\n */\nexport const toRunningText = (noun: string) =>\n noun\n .split(' ')\n .map((word) => (/[A-Z]/.test(word.slice(1)) ? word : word.toLowerCase()))\n .join(' ');\n\nconst dateFormat = new Intl.DateTimeFormat('en-US', { day: 'numeric', month: 'short' });\n\nexport const formatDateRange = (range: Date[]) => range.map((date) => dateFormat.format(date)).join(' – ');\n\n/** The last moment of the day `date` falls on, so a range ending that day includes all of it. */\nexport const endOfDay = (date: Date) => {\n const end = new Date(date);\n end.setHours(23, 59, 59, 999);\n return end;\n};\n"],"mappings":";;AAMA,IAAa,sBAAsB,GAAgB,MAA4B;CAC7E,IAAI,MAAM,QAAQ,CAAC,KAAK,MAAM,QAAQ,CAAC,GAAG;EACxC,IAAI,EAAE,WAAW,EAAE,QAAQ,OAAO;EAClC,IAAI,EAAE,OAAO,SAAS,OAAO,SAAS,QAAQ,GAAG;GAC/C,MAAM,SAAS,IAAI,IAAa,CAAC;GACjC,OAAO,EAAE,OAAO,SAAS,OAAO,IAAI,IAAI,CAAC;EAC3C;EACA,OAAO,EAAE,OAAO,MAAM,UAAU;GAC9B,MAAM,QAAQ,EAAE;GAChB,OAAO,gBAAgB,QAAQ,iBAAiB,OAAO,KAAK,QAAQ,MAAM,MAAM,QAAQ,IAAI,SAAS;EACvG,CAAC;CACH;CACA,OAAO,MAAM;AACf;;AAGA,IAAa,sBAAsB,UACjC,UAAU,QAAQ,UAAU,MAAO,MAAM,QAAQ,KAAK,KAAK,MAAM,WAAW;AAE9E,IAAM,gBAAgB,SAAiB,KAAK,QAAQ,sBAAsB,MAAM;AAEhF,IAAa,aAAa,UAAkB,MAAM,SAAS,GAAG;;AAG9D,IAAa,kBAAkB,YAAoB,IAAI,OAAO,IAAI,QAAQ,MAAM,KAAK,CAAC,CAAC,IAAI,YAAY,CAAC,CAAC,KAAK,IAAI,EAAE,EAAE;;;;;AAMtH,IAAa,iBAAiB,SAC5B,KACG,MAAM,GAAG,CAAC,CACV,KAAK,SAAU,QAAQ,KAAK,KAAK,MAAM,CAAC,CAAC,IAAI,OAAO,KAAK,YAAY,CAAE,CAAC,CACxE,KAAK,GAAG;AAEb,IAAM,aAAa,IAAI,KAAK,eAAe,SAAS;CAAE,KAAK;CAAW,OAAO;AAAQ,CAAC;AAEtF,IAAa,mBAAmB,UAAkB,MAAM,KAAK,SAAS,WAAW,OAAO,IAAI,CAAC,CAAC,CAAC,KAAK,KAAK;;AAGzG,IAAa,YAAY,SAAe;CACtC,MAAM,MAAM,IAAI,KAAK,IAAI;CACzB,IAAI,SAAS,IAAI,IAAI,IAAI,GAAG;CAC5B,OAAO;AACT"}
@@ -1,18 +0,0 @@
1
- import { ReactNode } from 'react';
2
- export interface BitkitFilterAddProps {
3
- /**
4
- * `BitkitFilter.Select` elements, also inside fragments or your own components. Each is listed in
5
- * the menu, in the order they mount, until it holds a value.
6
- */
7
- children: ReactNode;
8
- }
9
- /**
10
- * Optional filters behind an "Add filter" button. A filter picked from its menu opens its popover
11
- * there; once applied it shows as a removable chip, and leaves the menu until it is removed. On
12
- * mobile the menu is a sheet over the filters dialog.
13
- */
14
- declare const BitkitFilterAdd: {
15
- ({ children }: BitkitFilterAddProps): import('react').JSX.Element;
16
- displayName: string;
17
- };
18
- export default BitkitFilterAdd;
@@ -1,200 +0,0 @@
1
- import IconChevronRight from "../../icons/IconChevronRight.js";
2
- import IconInfoCircle from "../../icons/IconInfoCircle.js";
3
- import IconPlus from "../../icons/IconPlus.js";
4
- import { usePortalContext } from "../../utilities/BitkitPortalContext.js";
5
- import BitkitButton from "../BitkitButton/BitkitButton.js";
6
- import BitkitTooltip from "../BitkitTooltip/BitkitTooltip.js";
7
- import { FilterAddContextProvider, useFilterContext } from "./BitkitFilter.context.js";
8
- import { Box } from "@chakra-ui/react/box";
9
- import { chakra } from "@chakra-ui/react/styled-system";
10
- import { useCallback, useEffect, useId, useMemo, useRef, useState } from "react";
11
- import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
12
- import { Portal } from "@chakra-ui/react/portal";
13
- import { Menu } from "@chakra-ui/react/menu";
14
- import { Text } from "@chakra-ui/react/text";
15
- import { Dialog } from "@chakra-ui/react/dialog";
16
- //#region lib/components/BitkitFilter/BitkitFilterAdd.tsx
17
- /**
18
- * Optional filters behind an "Add filter" button. A filter picked from its menu opens its popover
19
- * there; once applied it shows as a removable chip, and leaves the menu until it is removed. On
20
- * mobile the menu is a sheet over the filters dialog.
21
- */
22
- var BitkitFilterAdd = ({ children }) => {
23
- const triggerId = useId();
24
- const { isDisabled, isMobile, styles } = useFilterContext();
25
- const { disablePortal } = usePortalContext();
26
- const [isSheetOpen, setIsSheetOpen] = useState(false);
27
- const [addingId, setAddingId] = useState();
28
- const [highlightedId, setHighlightedId] = useState(null);
29
- const [entries, setEntries] = useState(/* @__PURE__ */ new Map());
30
- const register = useCallback((id, entry) => {
31
- setEntries((prev) => new Map(prev).set(id, entry));
32
- }, []);
33
- const unregister = useCallback((id) => {
34
- setEntries((prev) => {
35
- const next = new Map(prev);
36
- next.delete(id);
37
- return next;
38
- });
39
- }, []);
40
- const getTrigger = useCallback(() => document.getElementById(triggerId), [triggerId]);
41
- const stopAdding = useCallback(() => setAddingId(void 0), []);
42
- const available = [...entries].filter(([, entry]) => entry.isEmpty);
43
- const isEmpty = !available.length;
44
- const focusPendingFrom = useRef(null);
45
- const focusTrigger = useCallback(() => {
46
- const trigger = getTrigger();
47
- if (trigger instanceof HTMLButtonElement && !trigger.disabled) {
48
- trigger.focus();
49
- return;
50
- }
51
- const from = document.activeElement;
52
- focusPendingFrom.current = from;
53
- document.addEventListener("focusin", () => {
54
- if (focusPendingFrom.current === from) focusPendingFrom.current = null;
55
- }, { once: true });
56
- }, [getTrigger]);
57
- useEffect(() => {
58
- const from = focusPendingFrom.current;
59
- if (!from || isEmpty) return;
60
- focusPendingFrom.current = null;
61
- const active = document.activeElement;
62
- if (active === from || active === document.body) getTrigger()?.focus();
63
- }, [getTrigger, isEmpty]);
64
- const context = useMemo(() => ({
65
- addingId,
66
- focusTrigger,
67
- getTrigger,
68
- register,
69
- stopAdding,
70
- unregister
71
- }), [
72
- addingId,
73
- focusTrigger,
74
- getTrigger,
75
- register,
76
- stopAdding,
77
- unregister
78
- ]);
79
- const isDisabledEntry = (entry) => !entry.disabledReason && !entry.hasOptions;
80
- const pick = (id) => {
81
- if (entries.get(id)?.disabledReason) return;
82
- setAddingId(id);
83
- };
84
- const addButton = /* @__PURE__ */ jsx(BitkitButton, {
85
- alignSelf: "flex-start",
86
- "aria-disabled": isDisabled || void 0,
87
- "data-disabled": isDisabled ? "" : void 0,
88
- icon: IconPlus,
89
- id: isMobile ? triggerId : void 0,
90
- onClick: isMobile ? () => !isDisabled && setIsSheetOpen(true) : void 0,
91
- onKeyDownCapture: isDisabled ? (event) => {
92
- if ([
93
- "ArrowDown",
94
- "ArrowUp",
95
- "Enter",
96
- " "
97
- ].includes(event.key)) event.preventDefault();
98
- } : void 0,
99
- size: isMobile ? "md" : "sm",
100
- state: isEmpty ? "disabled" : void 0,
101
- variant: "tertiary",
102
- children: "Add filter"
103
- });
104
- return /* @__PURE__ */ jsxs(FilterAddContextProvider, {
105
- value: context,
106
- children: [children, isMobile ? /* @__PURE__ */ jsxs(Fragment$1, { children: [addButton, /* @__PURE__ */ jsx(Dialog.Root, {
107
- onOpenChange: ({ open }) => setIsSheetOpen(open),
108
- open: isSheetOpen,
109
- unstyled: true,
110
- children: /* @__PURE__ */ jsxs(Portal, {
111
- disabled: disablePortal,
112
- children: [/* @__PURE__ */ jsx(Dialog.Backdrop, { css: styles.addSheetBackdrop }), /* @__PURE__ */ jsx(Dialog.Positioner, {
113
- css: styles.addSheetPositioner,
114
- children: /* @__PURE__ */ jsxs(Dialog.Content, {
115
- css: styles.addSheet,
116
- children: [/* @__PURE__ */ jsx(Dialog.Title, {
117
- css: styles.addMenuLabel,
118
- children: "Select filter"
119
- }), available.map(([id, entry]) => /* @__PURE__ */ jsxs(chakra.button, {
120
- "aria-disabled": entry.disabledReason ? true : void 0,
121
- css: styles.addSheetItem,
122
- "data-not-applicable": entry.disabledReason ? "" : void 0,
123
- disabled: isDisabledEntry(entry),
124
- onClick: () => {
125
- if (entry.disabledReason) return;
126
- setIsSheetOpen(false);
127
- pick(id);
128
- },
129
- type: "button",
130
- children: [/* @__PURE__ */ jsxs(Box, {
131
- as: "span",
132
- flex: "1",
133
- children: [/* @__PURE__ */ jsx(Text, {
134
- as: "span",
135
- display: "block",
136
- children: entry.label
137
- }), !!entry.disabledReason && /* @__PURE__ */ jsx(Text, {
138
- as: "span",
139
- css: styles.addSheetItemReason,
140
- display: "block",
141
- children: entry.disabledReason
142
- })]
143
- }), /* @__PURE__ */ jsx(IconChevronRight, { css: styles.addMenuIcon })]
144
- }, id))]
145
- })
146
- })]
147
- })
148
- })] }) : /* @__PURE__ */ jsxs(Menu.Root, {
149
- ids: { trigger: triggerId },
150
- onHighlightChange: ({ highlightedValue }) => setHighlightedId(highlightedValue),
151
- onOpenChange: ({ open }) => {
152
- if (!open) setHighlightedId(null);
153
- },
154
- onSelect: ({ value }) => pick(value),
155
- unstyled: true,
156
- children: [/* @__PURE__ */ jsx(Menu.Trigger, {
157
- asChild: true,
158
- children: addButton
159
- }), /* @__PURE__ */ jsx(Portal, {
160
- disabled: disablePortal,
161
- children: /* @__PURE__ */ jsx(Menu.Positioner, { children: /* @__PURE__ */ jsx(Menu.Content, {
162
- css: styles.addMenu,
163
- children: available.map(([id, entry]) => /* @__PURE__ */ jsxs(Menu.Item, {
164
- "aria-disabled": entry.disabledReason ? true : void 0,
165
- closeOnSelect: !entry.disabledReason,
166
- css: styles.addMenuItem,
167
- "data-not-applicable": entry.disabledReason ? "" : void 0,
168
- disabled: isDisabledEntry(entry),
169
- value: id,
170
- children: [/* @__PURE__ */ jsxs(Text, {
171
- flex: "1",
172
- children: [entry.label, !!entry.disabledReason && /* @__PURE__ */ jsx(Text, {
173
- as: "span",
174
- srOnly: true,
175
- children: `, ${entry.disabledReason}`
176
- })]
177
- }), entry.disabledReason ? /* @__PURE__ */ jsx(BitkitTooltip, {
178
- open: highlightedId === id,
179
- placement: "right",
180
- text: entry.disabledReason,
181
- children: /* @__PURE__ */ jsx(IconInfoCircle, {
182
- css: styles.addMenuIcon,
183
- size: "16"
184
- })
185
- }) : /* @__PURE__ */ jsx(IconChevronRight, {
186
- css: styles.addMenuIcon,
187
- "data-add-chevron": true,
188
- size: "16"
189
- })]
190
- }, id))
191
- }) })
192
- })]
193
- })]
194
- });
195
- };
196
- BitkitFilterAdd.displayName = "BitkitFilterAdd";
197
- //#endregion
198
- export { BitkitFilterAdd as default };
199
-
200
- //# sourceMappingURL=BitkitFilterAdd.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"BitkitFilterAdd.js","names":["_Fragment"],"sources":["../../../lib/components/BitkitFilter/BitkitFilterAdd.tsx"],"sourcesContent":["import { Box } from '@chakra-ui/react/box';\nimport { Dialog } from '@chakra-ui/react/dialog';\nimport { Menu } from '@chakra-ui/react/menu';\nimport { Portal } from '@chakra-ui/react/portal';\n// `chakra.button`: the mobile sheet's rows are bare buttons styled by the filter recipe.\nimport { chakra } from '@chakra-ui/react/styled-system';\nimport { Text } from '@chakra-ui/react/text';\nimport { type KeyboardEvent, type ReactNode, useCallback, useEffect, useId, useMemo, useRef, useState } from 'react';\n\nimport { IconChevronRight, IconInfoCircle, IconPlus } from '../../icons';\nimport { usePortalContext } from '../../utilities/BitkitPortalContext';\nimport BitkitButton from '../BitkitButton/BitkitButton';\nimport BitkitTooltip from '../BitkitTooltip/BitkitTooltip';\nimport { FilterAddContextProvider, type FilterAddEntry, useFilterContext } from './BitkitFilter.context';\n\nexport interface BitkitFilterAddProps {\n /**\n * `BitkitFilter.Select` elements, also inside fragments or your own components. Each is listed in\n * the menu, in the order they mount, until it holds a value.\n */\n children: ReactNode;\n}\n\n/**\n * Optional filters behind an \"Add filter\" button. A filter picked from its menu opens its popover\n * there; once applied it shows as a removable chip, and leaves the menu until it is removed. On\n * mobile the menu is a sheet over the filters dialog.\n */\nconst BitkitFilterAdd = ({ children }: BitkitFilterAddProps) => {\n // The popovers anchor to the button by id: a ref on a button inside Ark's `asChild` trigger trips React 18.\n const triggerId = useId();\n const { isDisabled, isMobile, styles } = useFilterContext();\n const { disablePortal } = usePortalContext();\n const [isSheetOpen, setIsSheetOpen] = useState(false);\n const [addingId, setAddingId] = useState<string>();\n const [highlightedId, setHighlightedId] = useState<string | null>(null);\n const [entries, setEntries] = useState<ReadonlyMap<string, FilterAddEntry>>(new Map());\n\n const register = useCallback((id: string, entry: FilterAddEntry) => {\n // `set` on a known id keeps its place, so an entry doesn't move when its props change.\n setEntries((prev) => new Map(prev).set(id, entry));\n }, []);\n\n const unregister = useCallback((id: string) => {\n setEntries((prev) => {\n const next = new Map(prev);\n next.delete(id);\n return next;\n });\n }, []);\n\n const getTrigger = useCallback(() => document.getElementById(triggerId), [triggerId]);\n const stopAdding = useCallback(() => setAddingId(undefined), []);\n\n const available = [...entries].filter(([, entry]) => entry.isEmpty);\n const isEmpty = !available.length;\n\n // The button is disabled while every filter is applied, so focus waits until a removal re-enables it.\n // It only moves if it hasn't gone anywhere since: still on the removed control, or dropped to the page.\n // Focus landing anywhere meanwhile (which dropping to the page doesn't count as) calls it off.\n const focusPendingFrom = useRef<Element | null>(null);\n const focusTrigger = useCallback(() => {\n const trigger = getTrigger();\n if (trigger instanceof HTMLButtonElement && !trigger.disabled) {\n trigger.focus();\n return;\n }\n const from = document.activeElement;\n focusPendingFrom.current = from;\n document.addEventListener(\n 'focusin',\n () => {\n if (focusPendingFrom.current === from) focusPendingFrom.current = null;\n },\n { once: true },\n );\n }, [getTrigger]);\n useEffect(() => {\n const from = focusPendingFrom.current;\n if (!from || isEmpty) return;\n focusPendingFrom.current = null;\n const active = document.activeElement;\n if (active === from || active === document.body) getTrigger()?.focus();\n }, [getTrigger, isEmpty]);\n\n const context = useMemo(\n () => ({ addingId, focusTrigger, getTrigger, register, stopAdding, unregister }),\n [addingId, focusTrigger, getTrigger, register, stopAdding, unregister],\n );\n\n // A filter with no options to pick is plainly disabled. One with a `disabledReason` stays reachable,\n // so keyboard and screen-reader users can learn the reason too, but can't be picked.\n const isDisabledEntry = (entry: FilterAddEntry) => !entry.disabledReason && !entry.hasOptions;\n const pick = (id: string) => {\n if (entries.get(id)?.disabledReason) return;\n setAddingId(id);\n };\n\n // While the page loads the button only looks disabled (`aria-disabled`), so it keeps a focus handed to it.\n // `data-disabled` keeps the menu from opening on click, and the keys that open it are refused here.\n const addButton = (\n <BitkitButton\n alignSelf=\"flex-start\"\n aria-disabled={isDisabled || undefined}\n data-disabled={isDisabled ? '' : undefined}\n icon={IconPlus}\n id={isMobile ? triggerId : undefined}\n onClick={isMobile ? () => !isDisabled && setIsSheetOpen(true) : undefined}\n onKeyDownCapture={\n isDisabled\n ? (event: KeyboardEvent) => {\n if (['ArrowDown', 'ArrowUp', 'Enter', ' '].includes(event.key)) event.preventDefault();\n }\n : undefined\n }\n size={isMobile ? 'md' : 'sm'}\n state={isEmpty ? 'disabled' : undefined}\n variant=\"tertiary\"\n >\n Add filter\n </BitkitButton>\n );\n\n return (\n <FilterAddContextProvider value={context}>\n {children}\n {isMobile ? (\n <>\n {addButton}\n <Dialog.Root onOpenChange={({ open }) => setIsSheetOpen(open)} open={isSheetOpen} unstyled>\n <Portal disabled={disablePortal}>\n <Dialog.Backdrop css={styles.addSheetBackdrop} />\n <Dialog.Positioner css={styles.addSheetPositioner}>\n <Dialog.Content css={styles.addSheet}>\n <Dialog.Title css={styles.addMenuLabel}>Select filter</Dialog.Title>\n {available.map(([id, entry]) => (\n <chakra.button\n aria-disabled={entry.disabledReason ? true : undefined}\n css={styles.addSheetItem}\n data-not-applicable={entry.disabledReason ? '' : undefined}\n disabled={isDisabledEntry(entry)}\n key={id}\n onClick={() => {\n if (entry.disabledReason) return;\n setIsSheetOpen(false);\n pick(id);\n }}\n type=\"button\"\n >\n <Box as=\"span\" flex=\"1\">\n <Text as=\"span\" display=\"block\">\n {entry.label}\n </Text>\n {/* A touch screen has no hover to open a tooltip, so the reason is spelled out. */}\n {!!entry.disabledReason && (\n <Text as=\"span\" css={styles.addSheetItemReason} display=\"block\">\n {entry.disabledReason}\n </Text>\n )}\n </Box>\n <IconChevronRight css={styles.addMenuIcon} />\n </chakra.button>\n ))}\n </Dialog.Content>\n </Dialog.Positioner>\n </Portal>\n </Dialog.Root>\n </>\n ) : (\n <Menu.Root\n ids={{ trigger: triggerId }}\n onHighlightChange={({ highlightedValue }) => setHighlightedId(highlightedValue)}\n onOpenChange={({ open }) => {\n if (!open) setHighlightedId(null);\n }}\n onSelect={({ value }) => pick(value)}\n unstyled\n >\n <Menu.Trigger asChild>{addButton}</Menu.Trigger>\n <Portal disabled={disablePortal}>\n <Menu.Positioner>\n <Menu.Content css={styles.addMenu}>\n {available.map(([id, entry]) => (\n <Menu.Item\n aria-disabled={entry.disabledReason ? true : undefined}\n closeOnSelect={!entry.disabledReason}\n css={styles.addMenuItem}\n data-not-applicable={entry.disabledReason ? '' : undefined}\n disabled={isDisabledEntry(entry)}\n key={id}\n value={id}\n >\n <Text flex=\"1\">\n {entry.label}\n {!!entry.disabledReason && <Text as=\"span\" srOnly>{`, ${entry.disabledReason}`}</Text>}\n </Text>\n {entry.disabledReason ? (\n // On the icon rather than the row: as the row's trigger, the tooltip would replace\n // the id its `aria-activedescendant` points at. It opens while the row is highlighted,\n // by pointer or arrow keys, since the menu keeps focus on itself.\n <BitkitTooltip open={highlightedId === id} placement=\"right\" text={entry.disabledReason}>\n <IconInfoCircle css={styles.addMenuIcon} size=\"16\" />\n </BitkitTooltip>\n ) : (\n <IconChevronRight css={styles.addMenuIcon} data-add-chevron size=\"16\" />\n )}\n </Menu.Item>\n ))}\n </Menu.Content>\n </Menu.Positioner>\n </Portal>\n </Menu.Root>\n )}\n </FilterAddContextProvider>\n );\n};\n\nBitkitFilterAdd.displayName = 'BitkitFilterAdd';\n\nexport default BitkitFilterAdd;\n"],"mappings":"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"}
@@ -1,26 +0,0 @@
1
- import { BoxProps } from '@chakra-ui/react/box';
2
- import { BitkitCalendarProps } from '../BitkitCalendar/BitkitCalendar';
3
- export type BitkitFilterDateRangeValue = [start: Date, end: Date] | null;
4
- export interface BitkitFilterDateRangeProps extends Omit<BoxProps, 'children' | 'defaultValue' | 'onChange'> {
5
- /** Says why a day can't be picked, e.g. "Can't select a date in the future". Shown on hover. */
6
- dayTooltip?: BitkitCalendarProps['dayTooltip'];
7
- /** Chip text while no range is applied. Defaults to "All dates". */
8
- emptyLabel?: string;
9
- /** Name of the filter, shown on mobile as the field label and the title of its view. Defaults to "Date". */
10
- label?: string;
11
- /** Latest selectable day. Defaults to today. */
12
- max?: Date;
13
- /** Earliest selectable day. Defaults to two years before today. */
14
- min?: Date;
15
- /**
16
- * Called on "Apply" and "Clear". An applied range is `[start, end]`, where `start` is the first
17
- * day's midnight and `end` the last day's final millisecond, so the range covers both days whole.
18
- */
19
- onValueChange: (value: BitkitFilterDateRangeValue) => void;
20
- /** The value "Clear filters" restores. Defaults to no range. */
21
- resetValue?: BitkitFilterDateRangeValue;
22
- value: BitkitFilterDateRangeValue;
23
- }
24
- /** A filter for a date range, picked on a calendar and applied with "Apply". */
25
- declare const BitkitFilterDateRange: import('react').ForwardRefExoticComponent<BitkitFilterDateRangeProps & import('react').RefAttributes<HTMLButtonElement>>;
26
- export default BitkitFilterDateRange;
@@ -1,118 +0,0 @@
1
- import IconCalendar from "../../icons/IconCalendar.js";
2
- import BitkitButton from "../BitkitButton/BitkitButton.js";
3
- import BitkitCalendar from "../BitkitCalendar/BitkitCalendar.js";
4
- import { endOfDay, formatDateRange, isEqualFilterValue } from "./BitkitFilter.utils.js";
5
- import { useFilterContext, useFilterRegistration } from "./BitkitFilter.context.js";
6
- import FilterChip from "./components/FilterChip.js";
7
- import FilterField from "./components/FilterField.js";
8
- import FilterPanel, { FilterPanelFooter, FilterPanelHeader } from "./components/FilterPanel.js";
9
- import { Box } from "@chakra-ui/react/box";
10
- import { cloneElement, forwardRef, useState } from "react";
11
- import { jsx, jsxs } from "react/jsx-runtime";
12
- //#region lib/components/BitkitFilter/BitkitFilterDateRange.tsx
13
- var today = () => /* @__PURE__ */ new Date();
14
- var twoYearsAgo = () => {
15
- const date = /* @__PURE__ */ new Date();
16
- date.setFullYear(date.getFullYear() - 2);
17
- return date;
18
- };
19
- /** A filter for a date range, picked on a calendar and applied with "Apply". */
20
- var BitkitFilterDateRange = forwardRef((props, ref) => {
21
- const { dayTooltip, emptyLabel = "All dates", label = "Date", max, min, onValueChange, resetValue = null, value, ...rest } = props;
22
- const { isDisabled, isMobile, styles } = useFilterContext();
23
- const [open, setOpen] = useState(false);
24
- const [draft, setDraft] = useState(value);
25
- useFilterRegistration(value, resetValue, onValueChange);
26
- const onOpenChange = (isOpen) => {
27
- if (isOpen && isDisabled) return;
28
- setOpen(isOpen);
29
- if (isOpen) setDraft(value);
30
- };
31
- const apply = (next) => {
32
- onValueChange(next);
33
- setOpen(false);
34
- };
35
- const draftRange = draft?.length === 2 ? [draft[0], endOfDay(draft[1])] : null;
36
- const isApplyDisabled = !draftRange || isEqualFilterValue(draftRange, value);
37
- const valueText = value ? formatDateRange(value) : emptyLabel;
38
- if (isMobile) return /* @__PURE__ */ jsxs(FilterPanel, {
39
- onOpenChange,
40
- open,
41
- trigger: /* @__PURE__ */ jsx(FilterField, {
42
- ...rest,
43
- icon: IconCalendar,
44
- isOpen: open,
45
- label,
46
- onClick: () => onOpenChange(true),
47
- ref,
48
- value: valueText
49
- }),
50
- children: [
51
- /* @__PURE__ */ jsx(FilterPanelHeader, { title: label }),
52
- /* @__PURE__ */ jsx(Box, {
53
- flex: "1",
54
- overflowY: "auto",
55
- children: /* @__PURE__ */ jsx(BitkitCalendar, {
56
- dayTooltip,
57
- device: "mobile",
58
- layout: "1-month",
59
- max: max ?? today(),
60
- min: min ?? twoYearsAgo(),
61
- onValueChange: setDraft,
62
- showOutsideDays: true,
63
- showSelectedDate: true,
64
- value: draft
65
- })
66
- }),
67
- /* @__PURE__ */ jsx(FilterPanelFooter, {
68
- isApplyDisabled,
69
- onApply: () => apply(draftRange),
70
- start: value ? {
71
- label: "Reset",
72
- onClick: () => apply(null)
73
- } : void 0
74
- })
75
- ]
76
- });
77
- return /* @__PURE__ */ jsx(FilterChip, {
78
- icon: IconCalendar,
79
- ref,
80
- renderTrigger: (button) => /* @__PURE__ */ jsx(BitkitCalendar, {
81
- dayTooltip,
82
- footer: /* @__PURE__ */ jsxs(Box, {
83
- css: styles.calendarFooter,
84
- children: [/* @__PURE__ */ jsx(Box, {
85
- flex: "1",
86
- children: !!value && /* @__PURE__ */ jsx(BitkitButton, {
87
- onClick: () => apply(null),
88
- size: "sm",
89
- variant: "tertiary",
90
- children: "Clear"
91
- })
92
- }), /* @__PURE__ */ jsx(BitkitButton, {
93
- onClick: () => apply(draftRange),
94
- size: "sm",
95
- state: isApplyDisabled ? "disabled" : void 0,
96
- children: "Apply"
97
- })]
98
- }),
99
- layout: "1-month",
100
- max: max ?? today(),
101
- min: min ?? twoYearsAgo(),
102
- onOpenChange,
103
- onValueChange: setDraft,
104
- open,
105
- showOutsideDays: true,
106
- showSelectedDate: true,
107
- value: draft,
108
- children: cloneElement(button, { "aria-label": valueText })
109
- }),
110
- value: valueText,
111
- ...rest
112
- });
113
- });
114
- BitkitFilterDateRange.displayName = "BitkitFilterDateRange";
115
- //#endregion
116
- export { BitkitFilterDateRange as default };
117
-
118
- //# sourceMappingURL=BitkitFilterDateRange.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"BitkitFilterDateRange.js","names":[],"sources":["../../../lib/components/BitkitFilter/BitkitFilterDateRange.tsx"],"sourcesContent":["import { Box, type BoxProps } from '@chakra-ui/react/box';\nimport { cloneElement, forwardRef, useState } from 'react';\n\nimport { IconCalendar } from '../../icons';\nimport BitkitButton from '../BitkitButton/BitkitButton';\nimport BitkitCalendar, { type BitkitCalendarProps } from '../BitkitCalendar/BitkitCalendar';\nimport { useFilterContext, useFilterRegistration } from './BitkitFilter.context';\nimport { endOfDay, formatDateRange, isEqualFilterValue } from './BitkitFilter.utils';\nimport FilterChip from './components/FilterChip';\nimport FilterField from './components/FilterField';\nimport FilterPanel, { FilterPanelFooter, FilterPanelHeader } from './components/FilterPanel';\n\nexport type BitkitFilterDateRangeValue = [start: Date, end: Date] | null;\n\nexport interface BitkitFilterDateRangeProps extends Omit<BoxProps, 'children' | 'defaultValue' | 'onChange'> {\n /** Says why a day can't be picked, e.g. \"Can't select a date in the future\". Shown on hover. */\n dayTooltip?: BitkitCalendarProps['dayTooltip'];\n /** Chip text while no range is applied. Defaults to \"All dates\". */\n emptyLabel?: string;\n /** Name of the filter, shown on mobile as the field label and the title of its view. Defaults to \"Date\". */\n label?: string;\n /** Latest selectable day. Defaults to today. */\n max?: Date;\n /** Earliest selectable day. Defaults to two years before today. */\n min?: Date;\n /**\n * Called on \"Apply\" and \"Clear\". An applied range is `[start, end]`, where `start` is the first\n * day's midnight and `end` the last day's final millisecond, so the range covers both days whole.\n */\n onValueChange: (value: BitkitFilterDateRangeValue) => void;\n /** The value \"Clear filters\" restores. Defaults to no range. */\n resetValue?: BitkitFilterDateRangeValue;\n value: BitkitFilterDateRangeValue;\n}\n\nconst today = () => new Date();\n\nconst twoYearsAgo = () => {\n const date = new Date();\n date.setFullYear(date.getFullYear() - 2);\n return date;\n};\n\n/** A filter for a date range, picked on a calendar and applied with \"Apply\". */\n// The ref lands on the chip's (or on mobile the field's) button; other props on its wrapper.\nconst BitkitFilterDateRange = forwardRef<HTMLButtonElement, BitkitFilterDateRangeProps>((props, ref) => {\n const {\n dayTooltip,\n emptyLabel = 'All dates',\n label = 'Date',\n max,\n min,\n onValueChange,\n resetValue = null,\n value,\n ...rest\n } = props;\n const { isDisabled, isMobile, styles } = useFilterContext();\n const [open, setOpen] = useState(false);\n const [draft, setDraft] = useState<Date[] | null>(value);\n\n useFilterRegistration(value, resetValue, onValueChange);\n\n const onOpenChange = (isOpen: boolean) => {\n // The chip is only `aria-disabled` while the page loads, so the calendar refuses to open here.\n if (isOpen && isDisabled) return;\n setOpen(isOpen);\n if (isOpen) setDraft(value);\n };\n\n const apply = (next: BitkitFilterDateRangeValue) => {\n onValueChange(next);\n setOpen(false);\n };\n\n const draftRange: BitkitFilterDateRangeValue = draft?.length === 2 ? [draft[0], endOfDay(draft[1])] : null;\n const isApplyDisabled = !draftRange || isEqualFilterValue(draftRange, value);\n const valueText = value ? formatDateRange(value) : emptyLabel;\n\n if (isMobile) {\n return (\n <FilterPanel\n onOpenChange={onOpenChange}\n open={open}\n trigger={\n <FilterField\n {...rest}\n icon={IconCalendar}\n isOpen={open}\n label={label}\n onClick={() => onOpenChange(true)}\n ref={ref}\n value={valueText}\n />\n }\n >\n <FilterPanelHeader title={label} />\n <Box flex=\"1\" overflowY=\"auto\">\n <BitkitCalendar\n dayTooltip={dayTooltip}\n device=\"mobile\"\n layout=\"1-month\"\n max={max ?? today()}\n min={min ?? twoYearsAgo()}\n onValueChange={setDraft}\n showOutsideDays\n showSelectedDate\n value={draft}\n />\n </Box>\n <FilterPanelFooter\n isApplyDisabled={isApplyDisabled}\n onApply={() => apply(draftRange)}\n start={value ? { label: 'Reset', onClick: () => apply(null) } : undefined}\n />\n </FilterPanel>\n );\n }\n\n return (\n <FilterChip\n icon={IconCalendar}\n ref={ref}\n renderTrigger={(button) => (\n <BitkitCalendar\n dayTooltip={dayTooltip}\n footer={\n <Box css={styles.calendarFooter}>\n <Box flex=\"1\">\n {!!value && (\n <BitkitButton onClick={() => apply(null)} size=\"sm\" variant=\"tertiary\">\n Clear\n </BitkitButton>\n )}\n </Box>\n <BitkitButton\n onClick={() => apply(draftRange)}\n size=\"sm\"\n state={isApplyDisabled ? 'disabled' : undefined}\n >\n Apply\n </BitkitButton>\n </Box>\n }\n layout=\"1-month\"\n max={max ?? today()}\n min={min ?? twoYearsAgo()}\n onOpenChange={onOpenChange}\n onValueChange={setDraft}\n open={open}\n showOutsideDays\n showSelectedDate\n value={draft}\n >\n {/* Zag names the trigger \"Open calendar\"; the chip is named by the text it shows instead. */}\n {cloneElement(button, { 'aria-label': valueText })}\n </BitkitCalendar>\n )}\n value={valueText}\n {...rest}\n />\n );\n});\n\nBitkitFilterDateRange.displayName = 'BitkitFilterDateRange';\n\nexport default BitkitFilterDateRange;\n"],"mappings":";;;;;;;;;;;;AAmCA,IAAM,8BAAc,IAAI,KAAK;AAE7B,IAAM,oBAAoB;CACxB,MAAM,uBAAO,IAAI,KAAK;CACtB,KAAK,YAAY,KAAK,YAAY,IAAI,CAAC;CACvC,OAAO;AACT;;AAIA,IAAM,wBAAwB,YAA2D,OAAO,QAAQ;CACtG,MAAM,EACJ,YACA,aAAa,aACb,QAAQ,QACR,KACA,KACA,eACA,aAAa,MACb,OACA,GAAG,SACD;CACJ,MAAM,EAAE,YAAY,UAAU,WAAW,iBAAiB;CAC1D,MAAM,CAAC,MAAM,WAAW,SAAS,KAAK;CACtC,MAAM,CAAC,OAAO,YAAY,SAAwB,KAAK;CAEvD,sBAAsB,OAAO,YAAY,aAAa;CAEtD,MAAM,gBAAgB,WAAoB;EAExC,IAAI,UAAU,YAAY;EAC1B,QAAQ,MAAM;EACd,IAAI,QAAQ,SAAS,KAAK;CAC5B;CAEA,MAAM,SAAS,SAAqC;EAClD,cAAc,IAAI;EAClB,QAAQ,KAAK;CACf;CAEA,MAAM,aAAyC,OAAO,WAAW,IAAI,CAAC,MAAM,IAAI,SAAS,MAAM,EAAE,CAAC,IAAI;CACtG,MAAM,kBAAkB,CAAC,cAAc,mBAAmB,YAAY,KAAK;CAC3E,MAAM,YAAY,QAAQ,gBAAgB,KAAK,IAAI;CAEnD,IAAI,UACF,OACE,qBAAC,aAAD;EACgB;EACR;EACN,SACE,oBAAC,aAAD;GACE,GAAI;GACJ,MAAM;GACN,QAAQ;GACD;GACP,eAAe,aAAa,IAAI;GAC3B;GACL,OAAO;EACR,CAAA;EAZL,UAAA;GAeE,oBAAC,mBAAD,EAAmB,OAAO,MAAQ,CAAA;GAClC,oBAAC,KAAD;IAAK,MAAK;IAAI,WAAU;IACtB,UAAA,oBAAC,gBAAD;KACc;KACZ,QAAO;KACP,QAAO;KACP,KAAK,OAAO,MAAM;KAClB,KAAK,OAAO,YAAY;KACxB,eAAe;KACf,iBAAA;KACA,kBAAA;KACA,OAAO;IACR,CAAA;GACE,CAAA;GACL,oBAAC,mBAAD;IACmB;IACjB,eAAe,MAAM,UAAU;IAC/B,OAAO,QAAQ;KAAE,OAAO;KAAS,eAAe,MAAM,IAAI;IAAE,IAAI,KAAA;GACjE,CAAA;EACU;;CAIjB,OACE,oBAAC,YAAD;EACE,MAAM;EACD;EACL,gBAAgB,WACd,oBAAC,gBAAD;GACc;GACZ,QACE,qBAAC,KAAD;IAAK,KAAK,OAAO;IAAjB,UAAA,CACE,oBAAC,KAAD;KAAK,MAAK;KACP,UAAA,CAAC,CAAC,SACD,oBAAC,cAAD;MAAc,eAAe,MAAM,IAAI;MAAG,MAAK;MAAK,SAAQ;MAAW,UAAA;KAEzD,CAAA;IAEb,CAAA,GACL,oBAAC,cAAD;KACE,eAAe,MAAM,UAAU;KAC/B,MAAK;KACL,OAAO,kBAAkB,aAAa,KAAA;KACvC,UAAA;IAEa,CAAA,CACX;;GAEP,QAAO;GACP,KAAK,OAAO,MAAM;GAClB,KAAK,OAAO,YAAY;GACV;GACd,eAAe;GACT;GACN,iBAAA;GACA,kBAAA;GACA,OAAO;GAGN,UAAA,aAAa,QAAQ,EAAE,cAAc,UAAU,CAAC;EACnC,CAAA;EAElB,OAAO;EACP,GAAI;CACL,CAAA;AAEL,CAAC;AAED,sBAAsB,cAAc"}
@@ -1,23 +0,0 @@
1
- import { BoxProps } from '@chakra-ui/react/box';
2
- export interface BitkitFilterSearchProps extends Omit<BoxProps, 'children' | 'defaultValue' | 'onChange'> {
3
- /** Accessible name of the input. Defaults to "Search". */
4
- label?: string;
5
- /** Called once typing pauses, and right away on clear. */
6
- onValueChange: (value: string) => void;
7
- /** Defaults to "Search...". */
8
- placeholder?: string;
9
- /** The value "Clear filters" restores. Defaults to an empty search. */
10
- resetValue?: string;
11
- /**
12
- * Says what the search matches, in a few words, e.g. "Build number or commit message". Shown on
13
- * hover, but not while typing, and read out with the field.
14
- */
15
- tooltip?: string;
16
- value: string;
17
- }
18
- /**
19
- * Free-text search, shown at the end of the bar whatever its position among the children — on mobile,
20
- * as a full-width row under the bar.
21
- */
22
- declare const BitkitFilterSearch: import('react').ForwardRefExoticComponent<BitkitFilterSearchProps & import('react').RefAttributes<HTMLInputElement>>;
23
- export default BitkitFilterSearch;
@@ -1,72 +0,0 @@
1
- import BitkitLabelTooltip from "../BitkitLabelTooltip/BitkitLabelTooltip.js";
2
- import { useFilterContext, useFilterRegistration } from "./BitkitFilter.context.js";
3
- import FilterSearchInput from "./components/FilterSearchInput.js";
4
- import { forwardRef, useEffect, useRef, useState } from "react";
5
- import { jsx } from "react/jsx-runtime";
6
- //#region lib/components/BitkitFilter/BitkitFilterSearch.tsx
7
- /**
8
- * Free-text search, shown at the end of the bar whatever its position among the children — on mobile,
9
- * as a full-width row under the bar.
10
- */
11
- var BitkitFilterSearch = forwardRef((props, ref) => {
12
- const { label = "Search", onValueChange, placeholder = "Search...", resetValue = "", tooltip, value, ...rest } = props;
13
- const { isMobile } = useFilterContext();
14
- const [text, setText] = useState(value);
15
- const [isFocused, setIsFocused] = useState(false);
16
- const pending = useRef([]);
17
- const valueRef = useRef(value);
18
- valueRef.current = value;
19
- const send = (next) => {
20
- if (next !== valueRef.current) pending.current.push(next);
21
- onValueChange(next);
22
- };
23
- useFilterRegistration(value, resetValue, (next) => {
24
- setText(next);
25
- send(next);
26
- }, false);
27
- useEffect(() => {
28
- const index = pending.current.indexOf(value);
29
- if (index >= 0) {
30
- pending.current.splice(index, 1);
31
- return;
32
- }
33
- pending.current = [];
34
- setText(value);
35
- }, [value]);
36
- useEffect(() => {
37
- if (text === value || !text) return void 0;
38
- const timeout = setTimeout(() => send(text), 300);
39
- return () => clearTimeout(timeout);
40
- }, [text]);
41
- const onTextChange = (next) => {
42
- setText(next);
43
- if (!next) send("");
44
- };
45
- return /* @__PURE__ */ jsx(FilterSearchInput, {
46
- description: tooltip,
47
- label,
48
- onValueChange: onTextChange,
49
- placeholder,
50
- ref,
51
- renderInput: tooltip && !isMobile ? (input) => /* @__PURE__ */ jsx(BitkitLabelTooltip, {
52
- disabled: isFocused,
53
- text: tooltip,
54
- children: input
55
- }) : void 0,
56
- value: text,
57
- ...rest,
58
- onBlur: (event) => {
59
- setIsFocused(false);
60
- rest.onBlur?.(event);
61
- },
62
- onFocus: (event) => {
63
- setIsFocused(true);
64
- rest.onFocus?.(event);
65
- }
66
- });
67
- });
68
- BitkitFilterSearch.displayName = "BitkitFilterSearch";
69
- //#endregion
70
- export { BitkitFilterSearch as default };
71
-
72
- //# sourceMappingURL=BitkitFilterSearch.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"BitkitFilterSearch.js","names":[],"sources":["../../../lib/components/BitkitFilter/BitkitFilterSearch.tsx"],"sourcesContent":["import { type BoxProps } from '@chakra-ui/react/box';\nimport { forwardRef, useEffect, useRef, useState } from 'react';\n\nimport BitkitLabelTooltip from '../BitkitLabelTooltip/BitkitLabelTooltip';\nimport { useFilterContext, useFilterRegistration } from './BitkitFilter.context';\nimport { SEARCH_DEBOUNCE_MS } from './BitkitFilter.utils';\nimport FilterSearchInput from './components/FilterSearchInput';\n\nexport interface BitkitFilterSearchProps extends Omit<BoxProps, 'children' | 'defaultValue' | 'onChange'> {\n /** Accessible name of the input. Defaults to \"Search\". */\n label?: string;\n /** Called once typing pauses, and right away on clear. */\n onValueChange: (value: string) => void;\n /** Defaults to \"Search...\". */\n placeholder?: string;\n /** The value \"Clear filters\" restores. Defaults to an empty search. */\n resetValue?: string;\n /**\n * Says what the search matches, in a few words, e.g. \"Build number or commit message\". Shown on\n * hover, but not while typing, and read out with the field.\n */\n tooltip?: string;\n value: string;\n}\n\n/**\n * Free-text search, shown at the end of the bar whatever its position among the children — on mobile,\n * as a full-width row under the bar.\n */\nconst BitkitFilterSearch = forwardRef<HTMLInputElement, BitkitFilterSearchProps>((props, ref) => {\n const {\n label = 'Search',\n onValueChange,\n placeholder = 'Search...',\n resetValue = '',\n tooltip,\n value,\n ...rest\n } = props;\n const { isMobile } = useFilterContext();\n const [text, setText] = useState(value);\n // The tooltip stays closed while the field has focus, so it doesn't cover the results being typed for.\n const [isFocused, setIsFocused] = useState(false);\n // What this search sent that hasn't come back as `value` yet. A page may echo a send late, after\n // more has been typed (or sent); such an echo must not overwrite the newer text.\n const pending = useRef<string[]>([]);\n const valueRef = useRef(value);\n valueRef.current = value;\n\n const send = (next: string) => {\n // A send matching `value` changes nothing, so no echo would ever come back to clear it.\n if (next !== valueRef.current) pending.current.push(next);\n onValueChange(next);\n };\n\n useFilterRegistration(\n value,\n resetValue,\n (next: string) => {\n setText(next);\n send(next);\n },\n false,\n );\n\n // Follow outside changes, e.g. a page reset.\n useEffect(() => {\n const index = pending.current.indexOf(value);\n if (index >= 0) {\n // An echo. Only this send has landed: echoes can arrive out of order.\n pending.current.splice(index, 1);\n return;\n }\n pending.current = [];\n setText(value);\n }, [value]);\n\n useEffect(() => {\n // An empty text was already sent right away.\n if (text === value || !text) return undefined;\n const timeout = setTimeout(() => send(text), SEARCH_DEBOUNCE_MS);\n return () => clearTimeout(timeout);\n // `value` is left out on purpose: it changing must not restart a pending debounce.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [text]);\n\n const onTextChange = (next: string) => {\n setText(next);\n // Clearing applies right away rather than after the debounce.\n if (!next) send('');\n };\n\n return (\n <FilterSearchInput\n description={tooltip}\n label={label}\n onValueChange={onTextChange}\n placeholder={placeholder}\n ref={ref}\n // Touch screens have no hover to show a tooltip.\n renderInput={\n tooltip && !isMobile\n ? (input) => (\n <BitkitLabelTooltip disabled={isFocused} text={tooltip}>\n {input}\n </BitkitLabelTooltip>\n )\n : undefined\n }\n value={text}\n {...rest}\n onBlur={(event) => {\n setIsFocused(false);\n rest.onBlur?.(event);\n }}\n onFocus={(event) => {\n setIsFocused(true);\n rest.onFocus?.(event);\n }}\n />\n );\n});\n\nBitkitFilterSearch.displayName = 'BitkitFilterSearch';\n\nexport default BitkitFilterSearch;\n"],"mappings":";;;;;;;;;;AA6BA,IAAM,qBAAqB,YAAuD,OAAO,QAAQ;CAC/F,MAAM,EACJ,QAAQ,UACR,eACA,cAAc,aACd,aAAa,IACb,SACA,OACA,GAAG,SACD;CACJ,MAAM,EAAE,aAAa,iBAAiB;CACtC,MAAM,CAAC,MAAM,WAAW,SAAS,KAAK;CAEtC,MAAM,CAAC,WAAW,gBAAgB,SAAS,KAAK;CAGhD,MAAM,UAAU,OAAiB,CAAC,CAAC;CACnC,MAAM,WAAW,OAAO,KAAK;CAC7B,SAAS,UAAU;CAEnB,MAAM,QAAQ,SAAiB;EAE7B,IAAI,SAAS,SAAS,SAAS,QAAQ,QAAQ,KAAK,IAAI;EACxD,cAAc,IAAI;CACpB;CAEA,sBACE,OACA,aACC,SAAiB;EAChB,QAAQ,IAAI;EACZ,KAAK,IAAI;CACX,GACA,KACF;CAGA,gBAAgB;EACd,MAAM,QAAQ,QAAQ,QAAQ,QAAQ,KAAK;EAC3C,IAAI,SAAS,GAAG;GAEd,QAAQ,QAAQ,OAAO,OAAO,CAAC;GAC/B;EACF;EACA,QAAQ,UAAU,CAAC;EACnB,QAAQ,KAAK;CACf,GAAG,CAAC,KAAK,CAAC;CAEV,gBAAgB;EAEd,IAAI,SAAS,SAAS,CAAC,MAAM,OAAO,KAAA;EACpC,MAAM,UAAU,iBAAiB,KAAK,IAAI,GAAA,GAAqB;EAC/D,aAAa,aAAa,OAAO;CAGnC,GAAG,CAAC,IAAI,CAAC;CAET,MAAM,gBAAgB,SAAiB;EACrC,QAAQ,IAAI;EAEZ,IAAI,CAAC,MAAM,KAAK,EAAE;CACpB;CAEA,OACE,oBAAC,mBAAD;EACE,aAAa;EACN;EACP,eAAe;EACF;EACR;EAEL,aACE,WAAW,CAAC,YACP,UACC,oBAAC,oBAAD;GAAoB,UAAU;GAAW,MAAM;GAC5C,UAAA;EACiB,CAAA,IAEtB,KAAA;EAEN,OAAO;EACP,GAAI;EACJ,SAAS,UAAU;GACjB,aAAa,KAAK;GAClB,KAAK,SAAS,KAAK;EACrB;EACA,UAAU,UAAU;GAClB,aAAa,IAAI;GACjB,KAAK,UAAU,KAAK;EACtB;CACD,CAAA;AAEL,CAAC;AAED,mBAAmB,cAAc"}