@j2-health/design-system 0.6.2 → 0.6.4

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 (30) hide show
  1. package/dist/index.cjs +3 -0
  2. package/dist/index.d.ts +1 -0
  3. package/dist/index.js +2 -1
  4. package/dist/index2.js +2 -1
  5. package/dist/src/components/select/SummarizedSelect.cjs +68 -8
  6. package/dist/src/components/select/SummarizedSelect.cjs.map +1 -1
  7. package/dist/src/components/select/SummarizedSelect.d.ts +19 -1
  8. package/dist/src/components/select/SummarizedSelect.js +68 -8
  9. package/dist/src/components/select/SummarizedSelect.js.map +1 -1
  10. package/dist/src/components/select/SummarizedSelect.module.css.cjs +14 -11
  11. package/dist/src/components/select/SummarizedSelect.module.css.cjs.map +1 -1
  12. package/dist/src/components/select/SummarizedSelect.module.css.js +14 -12
  13. package/dist/src/components/select/SummarizedSelect.module.css.js.map +1 -1
  14. package/dist/src/components/stepDrawer/StepDrawer.cjs +89 -0
  15. package/dist/src/components/stepDrawer/StepDrawer.cjs.map +1 -0
  16. package/dist/src/components/stepDrawer/StepDrawer.d.ts +65 -0
  17. package/dist/src/components/stepDrawer/StepDrawer.js +88 -0
  18. package/dist/src/components/stepDrawer/StepDrawer.js.map +1 -0
  19. package/dist/src/components/stepDrawer/index.d.ts +1 -0
  20. package/dist/src/components/steps/Steps.cjs +4 -3
  21. package/dist/src/components/steps/Steps.cjs.map +1 -1
  22. package/dist/src/components/steps/Steps.d.ts +1 -1
  23. package/dist/src/components/steps/Steps.js +4 -3
  24. package/dist/src/components/steps/Steps.js.map +1 -1
  25. package/dist/src/components/steps/useSteps.cjs +2 -2
  26. package/dist/src/components/steps/useSteps.cjs.map +1 -1
  27. package/dist/src/components/steps/useSteps.js +2 -2
  28. package/dist/src/components/steps/useSteps.js.map +1 -1
  29. package/dist/style.css +34 -28
  30. package/package.json +20 -20
package/dist/index.cjs CHANGED
@@ -52,6 +52,7 @@ const require_Spinner = require("./src/components/spinner/Spinner.cjs");
52
52
  const require_StatusPill = require("./src/components/statusPill/StatusPill.cjs");
53
53
  const require_useSteps = require("./src/components/steps/useSteps.cjs");
54
54
  const require_Steps = require("./src/components/steps/Steps.cjs");
55
+ const require_StepDrawer = require("./src/components/stepDrawer/StepDrawer.cjs");
55
56
  const require_SwatchStrip = require("./src/components/swatchStrip/SwatchStrip.cjs");
56
57
  const require_Switch = require("./src/components/switch/Switch.cjs");
57
58
  const require_Table = require("./src/components/table/Table.cjs");
@@ -111,6 +112,7 @@ exports.SelectAll = require_SelectAll.SelectAll;
111
112
  exports.Skeleton = require_Skeleton.Skeleton;
112
113
  exports.Spinner = require_Spinner.Spinner;
113
114
  exports.StatusPill = require_StatusPill.StatusPill;
115
+ exports.StepDrawer = require_StepDrawer.StepDrawer;
114
116
  exports.Steps = require_Steps.Steps;
115
117
  exports.SummarizedSelect = require_SummarizedSelect.SummarizedSelect;
116
118
  exports.Swatch = require_Swatch.Swatch;
@@ -137,5 +139,6 @@ exports.operatorOptionsFor = require_useFilterField.operatorOptionsFor;
137
139
  exports.tailwindPreset = require_tailwind.tailwindPreset;
138
140
  exports.useMessage = require_useMessage.useMessage;
139
141
  exports.useNotification = require_useNotification.useNotification;
142
+ exports.useStepDrawerSlots = require_StepDrawer.useStepDrawerSlots;
140
143
  exports.useSteps = require_useSteps.useSteps;
141
144
  exports.withDesignSystemPaginationOverrides = require_Pagination.withDesignSystemPaginationOverrides;
package/dist/index.d.ts CHANGED
@@ -37,6 +37,7 @@ export * from './src/components/select/index.js';
37
37
  export * from './src/components/skeleton/index.js';
38
38
  export * from './src/components/spinner/index.js';
39
39
  export * from './src/components/statusPill/index.js';
40
+ export * from './src/components/stepDrawer/index.js';
40
41
  export * from './src/components/steps/index.js';
41
42
  export * from './src/components/swatch/index.js';
42
43
  export * from './src/components/swatchStrip/index.js';
package/dist/index.js CHANGED
@@ -51,6 +51,7 @@ import { Spinner } from "./src/components/spinner/Spinner.js";
51
51
  import { StatusPill } from "./src/components/statusPill/StatusPill.js";
52
52
  import { useSteps } from "./src/components/steps/useSteps.js";
53
53
  import { Steps } from "./src/components/steps/Steps.js";
54
+ import { StepDrawer, useStepDrawerSlots } from "./src/components/stepDrawer/StepDrawer.js";
54
55
  import { SwatchStrip } from "./src/components/swatchStrip/SwatchStrip.js";
55
56
  import { Switch } from "./src/components/switch/Switch.js";
56
57
  import { Table } from "./src/components/table/Table.js";
@@ -60,4 +61,4 @@ import { VennDiagram } from "./src/components/vennDiagram/VennDiagram.js";
60
61
  import { UserProfile } from "./src/components/legacyNavMenu/UserProfile.js";
61
62
  import { LegacyNavMenu } from "./src/components/legacyNavMenu/LegacyNavMenu.js";
62
63
  import { icons } from "./index2.js";
63
- export { Alert, AntdInput, AntdSearchInput, AntdSelect, AppConfigProvider, BarChart, BareButton, Breadcrumb, BulkActionBar, Button, Card, ChangeChip, Checkbox, CheckboxTree, Collapse, Drawer, Dropdown, EmptyState, FiltersForm, Form, HighchartsReact, Input, InputNumber, LegacyNavMenu, ListPopover, ListRow, ManageColumnsPanel, Menu, Modal, MultiValueTextOperatorOptions, NavMenu, NotificationModal, Pagination, Popover, Progress, Radio, RadioGroup, Rate, Select, SelectAll, Skeleton, Spinner, StatusPill, Steps, SummarizedSelect, Swatch, SwatchStrip, Switch, Table, Tabs, Tag, ThresholdBar, Tooltip, UserProfile, VennDiagram, appTheme, icons, isMultiValueTextOperator, mergeProps, normalizeMultiValues, operatorOptionsFor, tailwindPreset, useMessage, useNotification, useSteps, withDesignSystemPaginationOverrides };
64
+ export { Alert, AntdInput, AntdSearchInput, AntdSelect, AppConfigProvider, BarChart, BareButton, Breadcrumb, BulkActionBar, Button, Card, ChangeChip, Checkbox, CheckboxTree, Collapse, Drawer, Dropdown, EmptyState, FiltersForm, Form, HighchartsReact, Input, InputNumber, LegacyNavMenu, ListPopover, ListRow, ManageColumnsPanel, Menu, Modal, MultiValueTextOperatorOptions, NavMenu, NotificationModal, Pagination, Popover, Progress, Radio, RadioGroup, Rate, Select, SelectAll, Skeleton, Spinner, StatusPill, StepDrawer, Steps, SummarizedSelect, Swatch, SwatchStrip, Switch, Table, Tabs, Tag, ThresholdBar, Tooltip, UserProfile, VennDiagram, appTheme, icons, isMultiValueTextOperator, mergeProps, normalizeMultiValues, operatorOptionsFor, tailwindPreset, useMessage, useNotification, useStepDrawerSlots, useSteps, withDesignSystemPaginationOverrides };
package/dist/index2.js CHANGED
@@ -51,6 +51,7 @@ import { Spinner } from "./src/components/spinner/Spinner.js";
51
51
  import { StatusPill } from "./src/components/statusPill/StatusPill.js";
52
52
  import { useSteps } from "./src/components/steps/useSteps.js";
53
53
  import { Steps } from "./src/components/steps/Steps.js";
54
+ import { StepDrawer, useStepDrawerSlots } from "./src/components/stepDrawer/StepDrawer.js";
54
55
  import { SwatchStrip } from "./src/components/swatchStrip/SwatchStrip.js";
55
56
  import { Switch } from "./src/components/switch/Switch.js";
56
57
  import { Table } from "./src/components/table/Table.js";
@@ -60,4 +61,4 @@ import { VennDiagram } from "./src/components/vennDiagram/VennDiagram.js";
60
61
  import { UserProfile } from "./src/components/legacyNavMenu/UserProfile.js";
61
62
  import { LegacyNavMenu } from "./src/components/legacyNavMenu/LegacyNavMenu.js";
62
63
  import * as icons from "@phosphor-icons/react";
63
- export { Alert, AntdInput, AntdSearchInput, AntdSelect, AppConfigProvider, BarChart, BareButton, Breadcrumb, BulkActionBar, Button, Card, ChangeChip, Checkbox, CheckboxTree, Collapse, Drawer, Dropdown, EmptyState, FiltersForm, Form, HighchartsReact, Input, InputNumber, LegacyNavMenu, ListPopover, ListRow, ManageColumnsPanel, Menu, Modal, MultiValueTextOperatorOptions, NavMenu, NotificationModal, Pagination, Popover, Progress, Radio, RadioGroup, Rate, Select, SelectAll, Skeleton, Spinner, StatusPill, Steps, SummarizedSelect, Swatch, SwatchStrip, Switch, Table, Tabs, Tag, ThresholdBar, Tooltip, UserProfile, VennDiagram, appTheme, icons, isMultiValueTextOperator, mergeProps, normalizeMultiValues, operatorOptionsFor, tailwindPreset, useMessage, useNotification, useSteps, withDesignSystemPaginationOverrides };
64
+ export { Alert, AntdInput, AntdSearchInput, AntdSelect, AppConfigProvider, BarChart, BareButton, Breadcrumb, BulkActionBar, Button, Card, ChangeChip, Checkbox, CheckboxTree, Collapse, Drawer, Dropdown, EmptyState, FiltersForm, Form, HighchartsReact, Input, InputNumber, LegacyNavMenu, ListPopover, ListRow, ManageColumnsPanel, Menu, Modal, MultiValueTextOperatorOptions, NavMenu, NotificationModal, Pagination, Popover, Progress, Radio, RadioGroup, Rate, Select, SelectAll, Skeleton, Spinner, StatusPill, StepDrawer, Steps, SummarizedSelect, Swatch, SwatchStrip, Switch, Table, Tabs, Tag, ThresholdBar, Tooltip, UserProfile, VennDiagram, appTheme, icons, isMultiValueTextOperator, mergeProps, normalizeMultiValues, operatorOptionsFor, tailwindPreset, useMessage, useNotification, useStepDrawerSlots, useSteps, withDesignSystemPaginationOverrides };
@@ -17,6 +17,30 @@ var getAllOptions = (options) => {
17
17
  if (!options) return [];
18
18
  return options.flatMap((option) => isGroupOption(option) ? option.options : [option]);
19
19
  };
20
+ var byLabel = (a, b) => String(a.label ?? "").localeCompare(String(b.label ?? ""), void 0, {
21
+ numeric: true,
22
+ sensitivity: "base"
23
+ });
24
+ /** One entry per value, the first one seen, sorted by label. */
25
+ var flattenAndSort = (options) => {
26
+ const seen = /* @__PURE__ */ new Set();
27
+ return getAllOptions(options).filter((option) => {
28
+ if (seen.has(option.value)) return false;
29
+ seen.add(option.value);
30
+ return true;
31
+ }).sort(byLabel);
32
+ };
33
+ var withGroupDividers = (options) => {
34
+ let groupsSeen = 0;
35
+ return options.map((option) => {
36
+ if (!isGroupOption(option)) return option;
37
+ groupsSeen += 1;
38
+ return groupsSeen === 1 ? option : {
39
+ ...option,
40
+ className: (0, classnames.default)(option.className, require_SummarizedSelect_module.default.groupDivider)
41
+ };
42
+ });
43
+ };
20
44
  var filterOptions = (options, searchValue) => {
21
45
  if (!options) return [];
22
46
  if (!searchValue) return options;
@@ -38,7 +62,27 @@ var filterOptions = (options, searchValue) => {
38
62
  */
39
63
  var WEIGHT_CLASS = /(^|\s)font-(thin|extralight|light|normal|medium|semibold|bold|extrabold|black|\[\d+\])(\s|$)/;
40
64
  var hasWeightClass = (className) => Boolean(className && WEIGHT_CLASS.test(className));
41
- function SummarizedSelect({ searchPlaceholder, formControlPlaceholder, loading, multiple, value, onChange, options, renderLabel, rootClassName, popupClassName, variant, trigger = "select", triggerWeight, defaultOpen = false, ...props }) {
65
+ /**
66
+ * Puts the option list back at the top each time it opens and each time it is
67
+ * rebuilt for a search. antd scrolls to the selected option from a timeout
68
+ * started in the list's own effect. This wraps the list, so its effect runs
69
+ * after that one and its timeout lands last. It keys off a count of opens, not
70
+ * the open flag: antd does not re-render a closed popup, so the flag it sees
71
+ * can stay `true` from one open to the next.
72
+ */
73
+ var OpenAtTop = ({ openCount, selectRef, children }) => {
74
+ (0, react.useEffect)(() => {
75
+ const timeoutId = setTimeout(() => {
76
+ selectRef.current?.scrollTo({
77
+ index: 0,
78
+ align: "top"
79
+ });
80
+ });
81
+ return () => clearTimeout(timeoutId);
82
+ }, [openCount, selectRef]);
83
+ return children;
84
+ };
85
+ function SummarizedSelect({ searchPlaceholder, formControlPlaceholder, loading, multiple, value, onChange, options, renderLabel, rootClassName, popupClassName, variant, trigger = "select", triggerWeight, defaultOpen = false, flattenOnSearch = false, scrollToSelectedOnOpen = true, groupDividers = false, ...props }) {
42
86
  const isHeadline = variant === "headlined";
43
87
  const isField = variant === "field";
44
88
  const antVariant = isHeadline ? "borderless" : isField ? "outlined" : variant;
@@ -47,6 +91,7 @@ function SummarizedSelect({ searchPlaceholder, formControlPlaceholder, loading,
47
91
  const [focusTrigger, setFocusTrigger] = (0, react.useState)(defaultOpen ? 1 : 0);
48
92
  const [isOpen, setIsOpen] = (0, react.useState)(defaultOpen);
49
93
  const inputRef = (0, react.useRef)(null);
94
+ const selectRef = (0, react.useRef)(null);
50
95
  const measureSpanRef = (0, react.useRef)(null);
51
96
  const [measuredWidth, setMeasuredWidth] = (0, react.useState)();
52
97
  const displayText = (0, react.useMemo)(() => {
@@ -80,8 +125,15 @@ function SummarizedSelect({ searchPlaceholder, formControlPlaceholder, loading,
80
125
  });
81
126
  }, [focusTrigger]);
82
127
  const filteredOptions = (0, react.useMemo)(() => {
83
- return filterOptions(options, searchValue);
84
- }, [searchValue, options]);
128
+ const filtered = filterOptions(options, searchValue);
129
+ if (flattenOnSearch && searchValue) return flattenAndSort(filtered);
130
+ return groupDividers ? withGroupDividers(filtered) : filtered;
131
+ }, [
132
+ searchValue,
133
+ options,
134
+ flattenOnSearch,
135
+ groupDividers
136
+ ]);
85
137
  const flatFilteredOptions = (0, react.useMemo)(() => {
86
138
  return getAllOptions(filteredOptions);
87
139
  }, [filteredOptions]);
@@ -200,7 +252,11 @@ function SummarizedSelect({ searchPlaceholder, formControlPlaceholder, loading,
200
252
  [require_SummarizedSelect_module.default.headlinedMenuContainer]: isHeadline,
201
253
  [require_SummarizedSelect_module.default.menuContainerMultiple]: multiple
202
254
  }),
203
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react.Fragment, { children: menu }, searchValue)
255
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react.Fragment, { children: scrollToSelectedOnOpen ? menu : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OpenAtTop, {
256
+ openCount: focusTrigger,
257
+ selectRef,
258
+ children: menu
259
+ }) }, searchValue)
204
260
  }),
205
261
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
206
262
  className: "flex justify-between items-center",
@@ -298,10 +354,13 @@ function SummarizedSelect({ searchPlaceholder, formControlPlaceholder, loading,
298
354
  className: "px-3 py-2 text-sm text-[var(--j2-color-text-secondary)]",
299
355
  children: "No options found"
300
356
  }) : filteredOptions.map((opt) => {
301
- if (isGroupOption(opt)) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
302
- className: "px-3 pt-2 pb-1 text-xs font-semibold uppercase tracking-wide text-[var(--j2-color-text-tertiary)]",
303
- children: opt.label
304
- }), opt.options.map(renderDropdownOptionRow)] }, `group-${opt.label}`);
357
+ if (isGroupOption(opt)) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
358
+ className: opt.className,
359
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
360
+ className: "px-3 pt-2 pb-1 text-xs font-semibold uppercase tracking-wide text-[var(--j2-color-text-tertiary)]",
361
+ children: opt.label
362
+ }), opt.options.map(renderDropdownOptionRow)]
363
+ }, `group-${opt.label}`);
305
364
  return renderDropdownOptionRow(opt);
306
365
  })
307
366
  }),
@@ -357,6 +416,7 @@ function SummarizedSelect({ searchPlaceholder, formControlPlaceholder, loading,
357
416
  },
358
417
  children: displayText
359
418
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(antd.Select, {
419
+ ref: selectRef,
360
420
  open: isOpen,
361
421
  showSearch: false,
362
422
  style: { width: isField ? "100%" : measuredWidth },
@@ -1 +1 @@
1
- {"version":3,"file":"SummarizedSelect.cjs","names":["useState","useRef","useMemo","Input","MagnifyingGlassIcon","Tag","cx","styles","Fragment","Spin","LoadingOutlined","CaretDownIcon","AntdDropdown","Typography","_Fragment","Select"],"sources":["../../../../src/components/select/SummarizedSelect.tsx"],"sourcesContent":["import {\n Dropdown as AntdDropdown,\n Select,\n Spin,\n Input,\n SelectProps,\n InputRef,\n Typography,\n} from 'antd'\nimport { DefaultOptionType } from 'antd/es/select'\nimport { Fragment, useState, useMemo, useRef, useEffect } from 'react'\nimport { LoadingOutlined } from '@ant-design/icons'\nimport { CaretDownIcon, MagnifyingGlassIcon } from '@phosphor-icons/react'\nimport { Tag } from '../tag'\nimport cx from 'classnames'\n\nimport styles from './SummarizedSelect.module.css'\n\ntype Option = DefaultOptionType\ntype OptionWithLogo = Option & {\n logo: React.ReactNode\n}\n\ntype GroupOption = {\n label: string\n options: Option[]\n}\n\nexport type SelectOption = Option | GroupOption | OptionWithLogo\n\nexport type SummarizedSelectVariant =\n SelectProps['variant'] | 'headlined' | 'field'\n\nexport type SummarizedSelectTrigger = 'select' | 'dropdown'\n\ntype BaseProps = Omit<\n SelectProps<string | string[], SelectOption>,\n 'mode' | 'value' | 'onChange' | 'variant'\n> & {\n searchPlaceholder?: string\n formControlPlaceholder?: string\n rootClassName?: string\n popupClassName?: string\n /**\n * Look of the `'select'` trigger.\n * - antd's own variants (`'outlined'`, `'borderless'`, ...): the filter-chip\n * look. It sizes to its label and turns into a filled pill once a\n * multiple select has values.\n * - `'headlined'`: a page-title select.\n * - `'field'`: a plain form field. It fills its container and keeps the\n * same look whatever is selected, so it lines up with the inputs and\n * selects around it in a form row.\n */\n variant?: SummarizedSelectVariant\n /**\n * Shape of the control that opens the popup.\n * - `'select'` (default): antd Select, styled by `variant`.\n * - `'dropdown'`: antd Dropdown with a text target (label + caret), for\n * breadcrumbs and inline switchers. Semibold primary text with a duotone\n * caret. A Tailwind weight utility on `rootClassName` (`font-normal`,\n * `font-[450]`) replaces the default; for any other weight, use\n * `triggerWeight`. Ignores `variant`.\n */\n trigger?: SummarizedSelectTrigger\n /**\n * The `'dropdown'` trigger's font weight, e.g. `400` or `'normal'`.\n * Replaces the semibold default and wins over any class. Use it for a\n * weight `rootClassName` can't express as a Tailwind weight utility.\n */\n triggerWeight?: React.CSSProperties['fontWeight']\n defaultOpen?: boolean\n\n loading?: boolean\n}\n\ntype SingleProps = {\n multiple?: false\n renderLabel?: undefined\n value: string | undefined\n onChange: (newValue: string) => void\n}\n\ntype MultiProps = {\n multiple: true\n renderLabel: (count: number) => string\n value: string[]\n onChange: (newValue: string[]) => void\n}\n\nexport type Props = BaseProps & (SingleProps | MultiProps)\n\nconst isGroupOption = (option: SelectOption): option is GroupOption => {\n return 'options' in option\n}\n\nconst getAllOptions = (options: SelectOption[] | undefined): Option[] => {\n if (!options) return []\n\n return options.flatMap((option) =>\n isGroupOption(option) ? option.options : [option]\n )\n}\n\nconst filterOptions = (\n options: SelectOption[] | undefined,\n searchValue: string\n): SelectOption[] => {\n if (!options) return []\n if (!searchValue) return options\n\n return options\n .map((option) => {\n if (isGroupOption(option)) {\n const filteredSubOptions = option.options.filter((subOption) =>\n subOption.label\n ?.toString()\n .toLowerCase()\n .includes(searchValue.toLowerCase())\n )\n return filteredSubOptions.length > 0\n ? { ...option, options: filteredSubOptions }\n : null\n } else {\n return option.label\n ?.toString()\n .toLowerCase()\n .includes(searchValue.toLowerCase())\n ? option\n : null\n }\n })\n .filter((option): option is SelectOption => option !== null)\n}\n\n/**\n * An unprefixed Tailwind font-weight utility: a named weight (`font-normal`,\n * `font-bold`) or an arbitrary numeric one (`font-[450]`). Arbitrary values\n * must be numeric, since `font-['Inter']` sets the family. Custom weight names\n * can't be told apart from family names, so they need `triggerWeight`.\n */\nconst WEIGHT_CLASS =\n /(^|\\s)font-(thin|extralight|light|normal|medium|semibold|bold|extrabold|black|\\[\\d+\\])(\\s|$)/\n\nconst hasWeightClass = (className?: string) =>\n Boolean(className && WEIGHT_CLASS.test(className))\n\nexport function SummarizedSelect({\n searchPlaceholder,\n formControlPlaceholder,\n loading,\n multiple,\n value,\n onChange,\n options,\n renderLabel,\n rootClassName,\n popupClassName,\n variant,\n trigger = 'select',\n triggerWeight,\n defaultOpen = false,\n ...props\n}: Props) {\n const isHeadline = variant === 'headlined'\n const isField = variant === 'field'\n const antVariant = isHeadline ? 'borderless' : isField ? 'outlined' : variant\n const isChip = !isField && variant !== 'underlined' && !isHeadline\n const [searchValue, setSearchValue] = useState('')\n const [focusTrigger, setFocusTrigger] = useState(defaultOpen ? 1 : 0)\n const [isOpen, setIsOpen] = useState(defaultOpen)\n const inputRef = useRef<InputRef>(null)\n const measureSpanRef = useRef<HTMLSpanElement>(null)\n const [measuredWidth, setMeasuredWidth] = useState<number>()\n\n const displayText = useMemo(() => {\n if (multiple && value.length > 0) {\n return renderLabel(value.length)\n }\n if (!multiple && value) {\n const option = getAllOptions(options).find((opt) => opt.value === value)\n return option ? String(option.label) : String(value)\n }\n return formControlPlaceholder ?? ''\n }, [multiple, value, options, renderLabel, formControlPlaceholder])\n\n useEffect(() => {\n if (!measureSpanRef.current) return\n // padding (22px = 11px each side) + arrow (18px) + border (6px)\n setMeasuredWidth(Math.ceil(measureSpanRef.current.offsetWidth) + 46)\n }, [displayText])\n\n useEffect(() => {\n if (focusTrigger > 0) {\n requestAnimationFrame(() => {\n requestAnimationFrame(() => {\n setTimeout(() => {\n // `preventScroll`: on first open the popup is still at its initial\n // spot when this runs, and a plain focus scrolls the page to it.\n inputRef.current?.focus({ cursor: 'end', preventScroll: true })\n }, 100)\n })\n })\n }\n }, [focusTrigger])\n\n const filteredOptions = useMemo(() => {\n return filterOptions(options, searchValue)\n }, [searchValue, options])\n\n const flatFilteredOptions = useMemo(() => {\n return getAllOptions(filteredOptions)\n }, [filteredOptions])\n\n const allFlatOptions = useMemo(() => {\n return getAllOptions(options)\n }, [options])\n\n const handleTagClose = (removedValue: string) => {\n if (!multiple) return\n\n onChange(value.filter((x) => x !== removedValue))\n }\n\n const handleToggleAll = () => {\n if (!multiple) return\n\n const allFilteredValues = getAllOptions(filteredOptions).map(\n (opt) => opt.value as string\n )\n const someSelected = allFilteredValues.some((val) => value.includes(val))\n\n if (someSelected) {\n onChange(value.filter((val) => !allFilteredValues.includes(val)))\n }\n }\n\n const handleSelectFirstOption = () => {\n if (flatFilteredOptions.length === 0) return\n\n const firstOption = flatFilteredOptions[0]\n if (!firstOption) return\n\n const optionValue = firstOption.value as string\n\n if (multiple) {\n if (value.includes(optionValue)) {\n onChange(value.filter((v) => v !== optionValue))\n } else {\n onChange([...value, optionValue])\n }\n } else {\n onChange(optionValue)\n handleOpenChange(false)\n }\n }\n\n const handleOptionClick = (optionValue: string) => {\n if (multiple) {\n if (value.includes(optionValue)) {\n onChange(value.filter((v) => v !== optionValue))\n } else {\n onChange([...value, optionValue])\n }\n } else {\n onChange(optionValue)\n handleOpenChange(false)\n }\n }\n\n const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {\n // Allow these navigation keys to bubble up to Select's native keyboard handler\n const navigationKeys = ['ArrowUp', 'ArrowDown']\n\n // Handle Enter - select first filtered option (custom behavior)\n if (e.key === 'Enter') {\n e.preventDefault()\n e.stopPropagation()\n handleSelectFirstOption()\n return\n }\n\n // Handle Escape - close dropdown (custom behavior)\n if (e.key === 'Escape') {\n e.preventDefault()\n e.stopPropagation()\n handleOpenChange(false)\n return\n }\n\n // For arrow keys, prevent cursor movement but allow propagation to Select\n if (navigationKeys.includes(e.key)) {\n e.preventDefault() // Prevents cursor from moving to start/end of input text\n // Do NOT call stopPropagation() - let event bubble to Select component\n return\n }\n\n // For all other keys (including Backspace), stop propagation\n // This prevents backspace from removing tags in multiple mode\n e.stopPropagation()\n }\n\n const optionRender: NonNullable<\n SelectProps<string | string[], SelectOption>['optionRender']\n > = (option) => {\n if ('logo' in option.data && option.data.logo) {\n return (\n <div className=\"flex items-center gap-2\">\n <div className=\"h-4 shrink-0\">{option.data.logo}</div>\n <span className=\"overflow-ellipsis overflow-hidden\">\n {option.label}\n </span>\n </div>\n )\n }\n return <div>{option.label}</div>\n }\n\n const popupRender = (menu: React.ReactElement) => {\n const valueToLabel = (val: string) => {\n const option = allFlatOptions.find((opt) => opt.value === val)\n return option ? (option.label as string) : val\n }\n\n return (\n <div>\n <div className=\"mb-2 px-2 pb-2 border-b -mx-1 mt-1\">\n <Input\n ref={inputRef}\n key={focusTrigger}\n placeholder={searchPlaceholder || 'Search...'}\n value={searchValue}\n onChange={(e) => {\n setSearchValue(e.target.value)\n e.stopPropagation()\n e.preventDefault()\n }}\n onKeyDown={handleKeyDown}\n onClick={(e) => e.stopPropagation()}\n prefix={\n <MagnifyingGlassIcon\n size={16}\n className=\"text-j2-text-placeholder\"\n />\n }\n allowClear\n />\n </div>\n {multiple && value.length > 0 ? (\n <div className=\"px-2 pb-2\">\n <div className=\"flex flex-wrap gap-y-1\">\n {(value as string[]).map((value) => (\n <Tag\n className=\"opacity-100 z-10 flex max-w-full\"\n key={value}\n status=\"default\"\n closable\n onClose={() => handleTagClose(value)}\n >\n <span\n className=\"overflow-ellipsis overflow-hidden\"\n title={valueToLabel(value)}\n >\n {valueToLabel(value)}\n </span>\n </Tag>\n ))}\n </div>\n </div>\n ) : null}\n <div\n className={cx(styles.menuContainer, 'rounded-lg', {\n [styles.headlinedMenuContainer]: isHeadline,\n [styles.menuContainerMultiple]: multiple,\n })}\n >\n {/* Keyed on the search so antd rebuilds its option list per\n keystroke. Focusing the search input blurs the Select, antd\n starts a close, cancels it because focus stayed in the popup,\n and never clears its `lockOptions` flag -- a locked list keeps\n showing the options it had when the popup opened. */}\n <Fragment key={searchValue}>{menu}</Fragment>\n </div>\n <div className=\"flex justify-between items-center\">\n {multiple && (\n <div className=\"w-full mt-1 pt-1 border-t border-j2-border-secondary\">\n <div\n aria-selected=\"false\"\n className={cx(\n 'ant-select-item ant-select-item-option w-full',\n styles.clearAllButton\n )}\n title=\"Clear all\"\n onClick={handleToggleAll}\n >\n <div className=\"ant-select-item-option-content text-center font-semibold\">\n Clear all\n </div>\n </div>\n </div>\n )}\n </div>\n </div>\n )\n }\n\n const handleOpenChange = (open: boolean) => {\n setIsOpen(open)\n if (open) {\n // Trigger focus by updating the counter\n setFocusTrigger((prev) => prev + 1)\n } else {\n setSearchValue('')\n }\n }\n\n const renderDropdownOptionRow = (option: Option) => {\n const optionValue = option.value as string\n const isSelected = multiple\n ? value.includes(optionValue)\n : value === optionValue\n const hasLogo = 'logo' in option && (option as OptionWithLogo).logo\n return (\n <div\n key={optionValue}\n role=\"option\"\n aria-selected={isSelected}\n onClick={() => handleOptionClick(optionValue)}\n className={cx(\n 'px-3 py-1.5 rounded cursor-pointer text-sm',\n isSelected\n ? 'bg-[var(--j2-color-primary-bg-hover)] font-semibold text-[var(--j2-color-primary)] hover:bg-[var(--j2-color-primary-bg-hover)]'\n : 'hover:bg-[var(--j2-color-bg-hover)]'\n )}\n >\n {hasLogo ? (\n <div className=\"flex items-center gap-2\">\n <div className=\"h-4 shrink-0\">\n {(option as OptionWithLogo).logo}\n </div>\n <span className=\"overflow-hidden overflow-ellipsis\">\n {option.label}\n </span>\n </div>\n ) : (\n option.label\n )}\n </div>\n )\n }\n\n const dropdownPopupRender = () => {\n const valueToLabel = (val: string) => {\n const option = allFlatOptions.find((opt) => opt.value === val)\n return option ? (option.label as string) : val\n }\n\n return (\n <div className=\"bg-white rounded-lg shadow-md p-2 min-w-[240px]\">\n <div className=\"pb-2 border-b border-j2-border-secondary\">\n <Input\n ref={inputRef}\n key={focusTrigger}\n placeholder={searchPlaceholder || 'Search...'}\n value={searchValue}\n onChange={(e) => {\n setSearchValue(e.target.value)\n e.stopPropagation()\n }}\n onKeyDown={handleKeyDown}\n onClick={(e) => e.stopPropagation()}\n prefix={\n <MagnifyingGlassIcon\n size={16}\n className=\"text-j2-text-placeholder\"\n />\n }\n allowClear\n />\n </div>\n {multiple && value.length > 0 && (\n <div className=\"px-1 pt-2\">\n <div className=\"flex flex-wrap gap-y-1\">\n {(value as string[]).map((v) => (\n <Tag\n className=\"opacity-100 z-10 flex max-w-full\"\n key={v}\n status=\"default\"\n closable\n onClose={() => handleTagClose(v)}\n >\n <span\n className=\"overflow-ellipsis overflow-hidden\"\n title={valueToLabel(v)}\n >\n {valueToLabel(v)}\n </span>\n </Tag>\n ))}\n </div>\n </div>\n )}\n <div\n className=\"max-h-72 overflow-y-auto py-1\"\n role=\"listbox\"\n aria-multiselectable={multiple || undefined}\n >\n {filteredOptions.length === 0 ? (\n <div className=\"px-3 py-2 text-sm text-[var(--j2-color-text-secondary)]\">\n No options found\n </div>\n ) : (\n filteredOptions.map((opt) => {\n if (isGroupOption(opt)) {\n return (\n <div key={`group-${opt.label}`}>\n <div className=\"px-3 pt-2 pb-1 text-xs font-semibold uppercase tracking-wide text-[var(--j2-color-text-tertiary)]\">\n {opt.label}\n </div>\n {opt.options.map(renderDropdownOptionRow)}\n </div>\n )\n }\n return renderDropdownOptionRow(opt)\n })\n )}\n </div>\n {multiple && value.length > 0 && (\n <div className=\"mt-1 pt-1 border-t border-j2-border-secondary\">\n <div\n role=\"button\"\n aria-label=\"Clear all\"\n title=\"Clear all\"\n onClick={handleToggleAll}\n className={cx(\n 'text-center font-semibold text-sm py-1.5 rounded cursor-pointer',\n styles.clearAllButton\n )}\n >\n Clear all\n </div>\n </div>\n )}\n </div>\n )\n }\n\n if (trigger === 'dropdown') {\n const hasSelection = multiple ? value.length > 0 : Boolean(value)\n const triggerContent = (\n <span className=\"inline-flex items-center gap-1.5\">\n {hasSelection ? displayText : (formControlPlaceholder ?? 'Select')}\n {loading ? (\n <Spin\n indicator={<LoadingOutlined spin />}\n size=\"small\"\n data-testid=\"loading-spinner\"\n />\n ) : (\n <CaretDownIcon weight=\"duotone\" data-testid=\"caret-down\" />\n )}\n </span>\n )\n\n return (\n <AntdDropdown\n open={isOpen}\n onOpenChange={handleOpenChange}\n trigger={['click']}\n disabled={props.disabled}\n popupRender={dropdownPopupRender}\n >\n <Typography.Link\n disabled={props.disabled}\n className={cx(\n 'inline-flex',\n styles.dropdownTrigger,\n triggerWeight === undefined &&\n !hasWeightClass(rootClassName) &&\n 'font-semibold',\n rootClassName\n )}\n style={\n triggerWeight === undefined\n ? undefined\n : { fontWeight: triggerWeight }\n }\n >\n {triggerContent}\n </Typography.Link>\n </AntdDropdown>\n )\n }\n\n return (\n <>\n <span\n ref={measureSpanRef}\n aria-hidden\n style={{\n position: 'absolute',\n visibility: 'hidden',\n whiteSpace: 'nowrap',\n fontSize: 'inherit',\n }}\n >\n {displayText}\n </span>\n <Select<string | string[], SelectOption>\n open={isOpen}\n showSearch={false}\n style={{ width: isField ? '100%' : measuredWidth }}\n suffixIcon={\n loading ? (\n <Spin\n indicator={<LoadingOutlined spin />}\n size=\"small\"\n data-testid=\"loading-spinner\"\n />\n ) : (\n <CaretDownIcon size={14} data-testid=\"caret-down\" />\n )\n }\n placeholder={formControlPlaceholder}\n mode={multiple ? 'multiple' : undefined}\n value={value}\n maxTagCount={0}\n maxTagPlaceholder={multiple ? () => renderLabel(value.length) : value}\n options={filteredOptions}\n onChange={(val) => {\n if (multiple) {\n if (Array.isArray(val)) {\n onChange(val)\n } else {\n throw new Error('Value should be an array in multiple mode')\n }\n } else {\n if (typeof val === 'string') {\n onChange(val)\n } else {\n throw new Error(\n 'Value should be a string in single selection mode'\n )\n }\n }\n }}\n popupRender={popupRender}\n optionRender={optionRender}\n onOpenChange={handleOpenChange}\n classNames={{\n root: cx(rootClassName, styles.summarizedSelect, {\n [styles.hemisphericSelect]: isChip,\n [styles.isActive]: isChip && multiple && value.length > 0,\n [styles.headlinedSelect]: isHeadline,\n [styles.fieldSelect]: isField,\n }),\n popup: { root: popupClassName },\n }}\n variant={antVariant}\n {...props}\n />\n </>\n )\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"SummarizedSelect.cjs","names":["cx","styles","useState","useRef","useMemo","Input","MagnifyingGlassIcon","Tag","Fragment","Spin","LoadingOutlined","CaretDownIcon","AntdDropdown","Typography","_Fragment","Select"],"sources":["../../../../src/components/select/SummarizedSelect.tsx"],"sourcesContent":["import {\n Dropdown as AntdDropdown,\n Select,\n Spin,\n Input,\n SelectProps,\n InputRef,\n Typography,\n} from 'antd'\nimport { DefaultOptionType, type RefSelectProps } from 'antd/es/select'\nimport { Fragment, useState, useMemo, useRef, useEffect } from 'react'\nimport { LoadingOutlined } from '@ant-design/icons'\nimport { CaretDownIcon, MagnifyingGlassIcon } from '@phosphor-icons/react'\nimport { Tag } from '../tag'\nimport cx from 'classnames'\n\nimport styles from './SummarizedSelect.module.css'\n\ntype Option = DefaultOptionType\ntype OptionWithLogo = Option & {\n logo: React.ReactNode\n}\n\ntype GroupOption = {\n label: string\n options: Option[]\n className?: string\n}\n\nexport type SelectOption = Option | GroupOption | OptionWithLogo\n\nexport type SummarizedSelectVariant =\n SelectProps['variant'] | 'headlined' | 'field'\n\nexport type SummarizedSelectTrigger = 'select' | 'dropdown'\n\ntype BaseProps = Omit<\n SelectProps<string | string[], SelectOption>,\n 'mode' | 'value' | 'onChange' | 'variant'\n> & {\n searchPlaceholder?: string\n formControlPlaceholder?: string\n rootClassName?: string\n popupClassName?: string\n /**\n * Look of the `'select'` trigger.\n * - antd's own variants (`'outlined'`, `'borderless'`, ...): the filter-chip\n * look. It sizes to its label and turns into a filled pill once a\n * multiple select has values.\n * - `'headlined'`: a page-title select.\n * - `'field'`: a plain form field. It fills its container and keeps the\n * same look whatever is selected, so it lines up with the inputs and\n * selects around it in a form row.\n */\n variant?: SummarizedSelectVariant\n /**\n * Shape of the control that opens the popup.\n * - `'select'` (default): antd Select, styled by `variant`.\n * - `'dropdown'`: antd Dropdown with a text target (label + caret), for\n * breadcrumbs and inline switchers. Semibold primary text with a duotone\n * caret. A Tailwind weight utility on `rootClassName` (`font-normal`,\n * `font-[450]`) replaces the default; for any other weight, use\n * `triggerWeight`. Ignores `variant`.\n */\n trigger?: SummarizedSelectTrigger\n /**\n * The `'dropdown'` trigger's font weight, e.g. `400` or `'normal'`.\n * Replaces the semibold default and wins over any class. Use it for a\n * weight `rootClassName` can't express as a Tailwind weight utility.\n */\n triggerWeight?: React.CSSProperties['fontWeight']\n defaultOpen?: boolean\n /**\n * While searching, list the matches as one list sorted by label: groups are\n * dropped, and a value that appears in more than one group shows once. Lets\n * a list that repeats options in a shortcut group (Recent, say) above the\n * full list search as a single list. Groups come back when the search is\n * cleared.\n */\n flattenOnSearch?: boolean\n /**\n * `false` opens the `'select'` trigger's list at the top every time instead\n * of scrolled to the selected option, for a list whose first rows matter\n * more than where the selection is. The `'dropdown'` trigger always opens\n * at the top.\n */\n scrollToSelectedOnOpen?: boolean\n /** A line between option groups, above the title of every group after the first. */\n groupDividers?: boolean\n\n loading?: boolean\n}\n\ntype SingleProps = {\n multiple?: false\n renderLabel?: undefined\n value: string | undefined\n onChange: (newValue: string) => void\n}\n\ntype MultiProps = {\n multiple: true\n renderLabel: (count: number) => string\n value: string[]\n onChange: (newValue: string[]) => void\n}\n\nexport type Props = BaseProps & (SingleProps | MultiProps)\n\nconst isGroupOption = (option: SelectOption): option is GroupOption => {\n return 'options' in option\n}\n\nconst getAllOptions = (options: SelectOption[] | undefined): Option[] => {\n if (!options) return []\n\n return options.flatMap((option) =>\n isGroupOption(option) ? option.options : [option]\n )\n}\n\nconst byLabel = (a: Option, b: Option) =>\n String(a.label ?? '').localeCompare(String(b.label ?? ''), undefined, {\n numeric: true,\n sensitivity: 'base',\n })\n\n/** One entry per value, the first one seen, sorted by label. */\nconst flattenAndSort = (options: SelectOption[]): Option[] => {\n const seen = new Set<unknown>()\n return getAllOptions(options)\n .filter((option) => {\n if (seen.has(option.value)) return false\n seen.add(option.value)\n return true\n })\n .sort(byLabel)\n}\n\nconst withGroupDividers = (options: SelectOption[]): SelectOption[] => {\n let groupsSeen = 0\n return options.map((option) => {\n if (!isGroupOption(option)) return option\n groupsSeen += 1\n return groupsSeen === 1\n ? option\n : {\n ...option,\n className: cx(option.className, styles.groupDivider),\n }\n })\n}\n\nconst filterOptions = (\n options: SelectOption[] | undefined,\n searchValue: string\n): SelectOption[] => {\n if (!options) return []\n if (!searchValue) return options\n\n return options\n .map((option) => {\n if (isGroupOption(option)) {\n const filteredSubOptions = option.options.filter((subOption) =>\n subOption.label\n ?.toString()\n .toLowerCase()\n .includes(searchValue.toLowerCase())\n )\n return filteredSubOptions.length > 0\n ? { ...option, options: filteredSubOptions }\n : null\n } else {\n return option.label\n ?.toString()\n .toLowerCase()\n .includes(searchValue.toLowerCase())\n ? option\n : null\n }\n })\n .filter((option): option is SelectOption => option !== null)\n}\n\n/**\n * An unprefixed Tailwind font-weight utility: a named weight (`font-normal`,\n * `font-bold`) or an arbitrary numeric one (`font-[450]`). Arbitrary values\n * must be numeric, since `font-['Inter']` sets the family. Custom weight names\n * can't be told apart from family names, so they need `triggerWeight`.\n */\nconst WEIGHT_CLASS =\n /(^|\\s)font-(thin|extralight|light|normal|medium|semibold|bold|extrabold|black|\\[\\d+\\])(\\s|$)/\n\nconst hasWeightClass = (className?: string) =>\n Boolean(className && WEIGHT_CLASS.test(className))\n\n/**\n * Puts the option list back at the top each time it opens and each time it is\n * rebuilt for a search. antd scrolls to the selected option from a timeout\n * started in the list's own effect. This wraps the list, so its effect runs\n * after that one and its timeout lands last. It keys off a count of opens, not\n * the open flag: antd does not re-render a closed popup, so the flag it sees\n * can stay `true` from one open to the next.\n */\nconst OpenAtTop = ({\n openCount,\n selectRef,\n children,\n}: {\n openCount: number\n selectRef: React.RefObject<RefSelectProps | null>\n children: React.ReactElement\n}) => {\n useEffect(() => {\n const timeoutId = setTimeout(() => {\n selectRef.current?.scrollTo({ index: 0, align: 'top' })\n })\n return () => clearTimeout(timeoutId)\n }, [openCount, selectRef])\n return children\n}\n\nexport function SummarizedSelect({\n searchPlaceholder,\n formControlPlaceholder,\n loading,\n multiple,\n value,\n onChange,\n options,\n renderLabel,\n rootClassName,\n popupClassName,\n variant,\n trigger = 'select',\n triggerWeight,\n defaultOpen = false,\n flattenOnSearch = false,\n scrollToSelectedOnOpen = true,\n groupDividers = false,\n ...props\n}: Props) {\n const isHeadline = variant === 'headlined'\n const isField = variant === 'field'\n const antVariant = isHeadline ? 'borderless' : isField ? 'outlined' : variant\n const isChip = !isField && variant !== 'underlined' && !isHeadline\n const [searchValue, setSearchValue] = useState('')\n const [focusTrigger, setFocusTrigger] = useState(defaultOpen ? 1 : 0)\n const [isOpen, setIsOpen] = useState(defaultOpen)\n const inputRef = useRef<InputRef>(null)\n const selectRef = useRef<RefSelectProps>(null)\n const measureSpanRef = useRef<HTMLSpanElement>(null)\n const [measuredWidth, setMeasuredWidth] = useState<number>()\n\n const displayText = useMemo(() => {\n if (multiple && value.length > 0) {\n return renderLabel(value.length)\n }\n if (!multiple && value) {\n const option = getAllOptions(options).find((opt) => opt.value === value)\n return option ? String(option.label) : String(value)\n }\n return formControlPlaceholder ?? ''\n }, [multiple, value, options, renderLabel, formControlPlaceholder])\n\n useEffect(() => {\n if (!measureSpanRef.current) return\n // padding (22px = 11px each side) + arrow (18px) + border (6px)\n setMeasuredWidth(Math.ceil(measureSpanRef.current.offsetWidth) + 46)\n }, [displayText])\n\n useEffect(() => {\n if (focusTrigger > 0) {\n requestAnimationFrame(() => {\n requestAnimationFrame(() => {\n setTimeout(() => {\n // `preventScroll`: on first open the popup is still at its initial\n // spot when this runs, and a plain focus scrolls the page to it.\n inputRef.current?.focus({ cursor: 'end', preventScroll: true })\n }, 100)\n })\n })\n }\n }, [focusTrigger])\n\n const filteredOptions = useMemo(() => {\n const filtered = filterOptions(options, searchValue)\n if (flattenOnSearch && searchValue) return flattenAndSort(filtered)\n return groupDividers ? withGroupDividers(filtered) : filtered\n }, [searchValue, options, flattenOnSearch, groupDividers])\n\n const flatFilteredOptions = useMemo(() => {\n return getAllOptions(filteredOptions)\n }, [filteredOptions])\n\n const allFlatOptions = useMemo(() => {\n return getAllOptions(options)\n }, [options])\n\n const handleTagClose = (removedValue: string) => {\n if (!multiple) return\n\n onChange(value.filter((x) => x !== removedValue))\n }\n\n const handleToggleAll = () => {\n if (!multiple) return\n\n const allFilteredValues = getAllOptions(filteredOptions).map(\n (opt) => opt.value as string\n )\n const someSelected = allFilteredValues.some((val) => value.includes(val))\n\n if (someSelected) {\n onChange(value.filter((val) => !allFilteredValues.includes(val)))\n }\n }\n\n const handleSelectFirstOption = () => {\n if (flatFilteredOptions.length === 0) return\n\n const firstOption = flatFilteredOptions[0]\n if (!firstOption) return\n\n const optionValue = firstOption.value as string\n\n if (multiple) {\n if (value.includes(optionValue)) {\n onChange(value.filter((v) => v !== optionValue))\n } else {\n onChange([...value, optionValue])\n }\n } else {\n onChange(optionValue)\n handleOpenChange(false)\n }\n }\n\n const handleOptionClick = (optionValue: string) => {\n if (multiple) {\n if (value.includes(optionValue)) {\n onChange(value.filter((v) => v !== optionValue))\n } else {\n onChange([...value, optionValue])\n }\n } else {\n onChange(optionValue)\n handleOpenChange(false)\n }\n }\n\n const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {\n // Allow these navigation keys to bubble up to Select's native keyboard handler\n const navigationKeys = ['ArrowUp', 'ArrowDown']\n\n // Handle Enter - select first filtered option (custom behavior)\n if (e.key === 'Enter') {\n e.preventDefault()\n e.stopPropagation()\n handleSelectFirstOption()\n return\n }\n\n // Handle Escape - close dropdown (custom behavior)\n if (e.key === 'Escape') {\n e.preventDefault()\n e.stopPropagation()\n handleOpenChange(false)\n return\n }\n\n // For arrow keys, prevent cursor movement but allow propagation to Select\n if (navigationKeys.includes(e.key)) {\n e.preventDefault() // Prevents cursor from moving to start/end of input text\n // Do NOT call stopPropagation() - let event bubble to Select component\n return\n }\n\n // For all other keys (including Backspace), stop propagation\n // This prevents backspace from removing tags in multiple mode\n e.stopPropagation()\n }\n\n const optionRender: NonNullable<\n SelectProps<string | string[], SelectOption>['optionRender']\n > = (option) => {\n if ('logo' in option.data && option.data.logo) {\n return (\n <div className=\"flex items-center gap-2\">\n <div className=\"h-4 shrink-0\">{option.data.logo}</div>\n <span className=\"overflow-ellipsis overflow-hidden\">\n {option.label}\n </span>\n </div>\n )\n }\n return <div>{option.label}</div>\n }\n\n const popupRender = (menu: React.ReactElement) => {\n const valueToLabel = (val: string) => {\n const option = allFlatOptions.find((opt) => opt.value === val)\n return option ? (option.label as string) : val\n }\n\n return (\n <div>\n <div className=\"mb-2 px-2 pb-2 border-b -mx-1 mt-1\">\n <Input\n ref={inputRef}\n key={focusTrigger}\n placeholder={searchPlaceholder || 'Search...'}\n value={searchValue}\n onChange={(e) => {\n setSearchValue(e.target.value)\n e.stopPropagation()\n e.preventDefault()\n }}\n onKeyDown={handleKeyDown}\n onClick={(e) => e.stopPropagation()}\n prefix={\n <MagnifyingGlassIcon\n size={16}\n className=\"text-j2-text-placeholder\"\n />\n }\n allowClear\n />\n </div>\n {multiple && value.length > 0 ? (\n <div className=\"px-2 pb-2\">\n <div className=\"flex flex-wrap gap-y-1\">\n {(value as string[]).map((value) => (\n <Tag\n className=\"opacity-100 z-10 flex max-w-full\"\n key={value}\n status=\"default\"\n closable\n onClose={() => handleTagClose(value)}\n >\n <span\n className=\"overflow-ellipsis overflow-hidden\"\n title={valueToLabel(value)}\n >\n {valueToLabel(value)}\n </span>\n </Tag>\n ))}\n </div>\n </div>\n ) : null}\n <div\n className={cx(styles.menuContainer, 'rounded-lg', {\n [styles.headlinedMenuContainer]: isHeadline,\n [styles.menuContainerMultiple]: multiple,\n })}\n >\n {/* Keyed on the search so antd rebuilds its option list per\n keystroke. Focusing the search input blurs the Select, antd\n starts a close, cancels it because focus stayed in the popup,\n and never clears its `lockOptions` flag -- a locked list keeps\n showing the options it had when the popup opened. */}\n <Fragment key={searchValue}>\n {scrollToSelectedOnOpen ? (\n menu\n ) : (\n <OpenAtTop openCount={focusTrigger} selectRef={selectRef}>\n {menu}\n </OpenAtTop>\n )}\n </Fragment>\n </div>\n <div className=\"flex justify-between items-center\">\n {multiple && (\n <div className=\"w-full mt-1 pt-1 border-t border-j2-border-secondary\">\n <div\n aria-selected=\"false\"\n className={cx(\n 'ant-select-item ant-select-item-option w-full',\n styles.clearAllButton\n )}\n title=\"Clear all\"\n onClick={handleToggleAll}\n >\n <div className=\"ant-select-item-option-content text-center font-semibold\">\n Clear all\n </div>\n </div>\n </div>\n )}\n </div>\n </div>\n )\n }\n\n const handleOpenChange = (open: boolean) => {\n setIsOpen(open)\n if (open) {\n // Trigger focus by updating the counter\n setFocusTrigger((prev) => prev + 1)\n } else {\n setSearchValue('')\n }\n }\n\n const renderDropdownOptionRow = (option: Option) => {\n const optionValue = option.value as string\n const isSelected = multiple\n ? value.includes(optionValue)\n : value === optionValue\n const hasLogo = 'logo' in option && (option as OptionWithLogo).logo\n return (\n <div\n key={optionValue}\n role=\"option\"\n aria-selected={isSelected}\n onClick={() => handleOptionClick(optionValue)}\n className={cx(\n 'px-3 py-1.5 rounded cursor-pointer text-sm',\n isSelected\n ? 'bg-[var(--j2-color-primary-bg-hover)] font-semibold text-[var(--j2-color-primary)] hover:bg-[var(--j2-color-primary-bg-hover)]'\n : 'hover:bg-[var(--j2-color-bg-hover)]'\n )}\n >\n {hasLogo ? (\n <div className=\"flex items-center gap-2\">\n <div className=\"h-4 shrink-0\">\n {(option as OptionWithLogo).logo}\n </div>\n <span className=\"overflow-hidden overflow-ellipsis\">\n {option.label}\n </span>\n </div>\n ) : (\n option.label\n )}\n </div>\n )\n }\n\n const dropdownPopupRender = () => {\n const valueToLabel = (val: string) => {\n const option = allFlatOptions.find((opt) => opt.value === val)\n return option ? (option.label as string) : val\n }\n\n return (\n <div className=\"bg-white rounded-lg shadow-md p-2 min-w-[240px]\">\n <div className=\"pb-2 border-b border-j2-border-secondary\">\n <Input\n ref={inputRef}\n key={focusTrigger}\n placeholder={searchPlaceholder || 'Search...'}\n value={searchValue}\n onChange={(e) => {\n setSearchValue(e.target.value)\n e.stopPropagation()\n }}\n onKeyDown={handleKeyDown}\n onClick={(e) => e.stopPropagation()}\n prefix={\n <MagnifyingGlassIcon\n size={16}\n className=\"text-j2-text-placeholder\"\n />\n }\n allowClear\n />\n </div>\n {multiple && value.length > 0 && (\n <div className=\"px-1 pt-2\">\n <div className=\"flex flex-wrap gap-y-1\">\n {(value as string[]).map((v) => (\n <Tag\n className=\"opacity-100 z-10 flex max-w-full\"\n key={v}\n status=\"default\"\n closable\n onClose={() => handleTagClose(v)}\n >\n <span\n className=\"overflow-ellipsis overflow-hidden\"\n title={valueToLabel(v)}\n >\n {valueToLabel(v)}\n </span>\n </Tag>\n ))}\n </div>\n </div>\n )}\n <div\n className=\"max-h-72 overflow-y-auto py-1\"\n role=\"listbox\"\n aria-multiselectable={multiple || undefined}\n >\n {filteredOptions.length === 0 ? (\n <div className=\"px-3 py-2 text-sm text-[var(--j2-color-text-secondary)]\">\n No options found\n </div>\n ) : (\n filteredOptions.map((opt) => {\n if (isGroupOption(opt)) {\n return (\n <div key={`group-${opt.label}`} className={opt.className}>\n <div className=\"px-3 pt-2 pb-1 text-xs font-semibold uppercase tracking-wide text-[var(--j2-color-text-tertiary)]\">\n {opt.label}\n </div>\n {opt.options.map(renderDropdownOptionRow)}\n </div>\n )\n }\n return renderDropdownOptionRow(opt)\n })\n )}\n </div>\n {multiple && value.length > 0 && (\n <div className=\"mt-1 pt-1 border-t border-j2-border-secondary\">\n <div\n role=\"button\"\n aria-label=\"Clear all\"\n title=\"Clear all\"\n onClick={handleToggleAll}\n className={cx(\n 'text-center font-semibold text-sm py-1.5 rounded cursor-pointer',\n styles.clearAllButton\n )}\n >\n Clear all\n </div>\n </div>\n )}\n </div>\n )\n }\n\n if (trigger === 'dropdown') {\n const hasSelection = multiple ? value.length > 0 : Boolean(value)\n const triggerContent = (\n <span className=\"inline-flex items-center gap-1.5\">\n {hasSelection ? displayText : (formControlPlaceholder ?? 'Select')}\n {loading ? (\n <Spin\n indicator={<LoadingOutlined spin />}\n size=\"small\"\n data-testid=\"loading-spinner\"\n />\n ) : (\n <CaretDownIcon weight=\"duotone\" data-testid=\"caret-down\" />\n )}\n </span>\n )\n\n return (\n <AntdDropdown\n open={isOpen}\n onOpenChange={handleOpenChange}\n trigger={['click']}\n disabled={props.disabled}\n popupRender={dropdownPopupRender}\n >\n <Typography.Link\n disabled={props.disabled}\n className={cx(\n 'inline-flex',\n styles.dropdownTrigger,\n triggerWeight === undefined &&\n !hasWeightClass(rootClassName) &&\n 'font-semibold',\n rootClassName\n )}\n style={\n triggerWeight === undefined\n ? undefined\n : { fontWeight: triggerWeight }\n }\n >\n {triggerContent}\n </Typography.Link>\n </AntdDropdown>\n )\n }\n\n return (\n <>\n <span\n ref={measureSpanRef}\n aria-hidden\n style={{\n position: 'absolute',\n visibility: 'hidden',\n whiteSpace: 'nowrap',\n fontSize: 'inherit',\n }}\n >\n {displayText}\n </span>\n <Select<string | string[], SelectOption>\n ref={selectRef}\n open={isOpen}\n showSearch={false}\n style={{ width: isField ? '100%' : measuredWidth }}\n suffixIcon={\n loading ? (\n <Spin\n indicator={<LoadingOutlined spin />}\n size=\"small\"\n data-testid=\"loading-spinner\"\n />\n ) : (\n <CaretDownIcon size={14} data-testid=\"caret-down\" />\n )\n }\n placeholder={formControlPlaceholder}\n mode={multiple ? 'multiple' : undefined}\n value={value}\n maxTagCount={0}\n maxTagPlaceholder={multiple ? () => renderLabel(value.length) : value}\n options={filteredOptions}\n onChange={(val) => {\n if (multiple) {\n if (Array.isArray(val)) {\n onChange(val)\n } else {\n throw new Error('Value should be an array in multiple mode')\n }\n } else {\n if (typeof val === 'string') {\n onChange(val)\n } else {\n throw new Error(\n 'Value should be a string in single selection mode'\n )\n }\n }\n }}\n popupRender={popupRender}\n optionRender={optionRender}\n onOpenChange={handleOpenChange}\n classNames={{\n root: cx(rootClassName, styles.summarizedSelect, {\n [styles.hemisphericSelect]: isChip,\n [styles.isActive]: isChip && multiple && value.length > 0,\n [styles.headlinedSelect]: isHeadline,\n [styles.fieldSelect]: isField,\n }),\n popup: { root: popupClassName },\n }}\n variant={antVariant}\n {...props}\n />\n </>\n )\n}\n"],"mappings":"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"}
@@ -7,6 +7,7 @@ type OptionWithLogo = Option & {
7
7
  type GroupOption = {
8
8
  label: string;
9
9
  options: Option[];
10
+ className?: string;
10
11
  };
11
12
  export type SelectOption = Option | GroupOption | OptionWithLogo;
12
13
  export type SummarizedSelectVariant = SelectProps['variant'] | 'headlined' | 'field';
@@ -44,6 +45,23 @@ type BaseProps = Omit<SelectProps<string | string[], SelectOption>, 'mode' | 'va
44
45
  */
45
46
  triggerWeight?: React.CSSProperties['fontWeight'];
46
47
  defaultOpen?: boolean;
48
+ /**
49
+ * While searching, list the matches as one list sorted by label: groups are
50
+ * dropped, and a value that appears in more than one group shows once. Lets
51
+ * a list that repeats options in a shortcut group (Recent, say) above the
52
+ * full list search as a single list. Groups come back when the search is
53
+ * cleared.
54
+ */
55
+ flattenOnSearch?: boolean;
56
+ /**
57
+ * `false` opens the `'select'` trigger's list at the top every time instead
58
+ * of scrolled to the selected option, for a list whose first rows matter
59
+ * more than where the selection is. The `'dropdown'` trigger always opens
60
+ * at the top.
61
+ */
62
+ scrollToSelectedOnOpen?: boolean;
63
+ /** A line between option groups, above the title of every group after the first. */
64
+ groupDividers?: boolean;
47
65
  loading?: boolean;
48
66
  };
49
67
  type SingleProps = {
@@ -59,5 +77,5 @@ type MultiProps = {
59
77
  onChange: (newValue: string[]) => void;
60
78
  };
61
79
  export type Props = BaseProps & (SingleProps | MultiProps);
62
- export declare function SummarizedSelect({ searchPlaceholder, formControlPlaceholder, loading, multiple, value, onChange, options, renderLabel, rootClassName, popupClassName, variant, trigger, triggerWeight, defaultOpen, ...props }: Props): import('react').JSX.Element;
80
+ export declare function SummarizedSelect({ searchPlaceholder, formControlPlaceholder, loading, multiple, value, onChange, options, renderLabel, rootClassName, popupClassName, variant, trigger, triggerWeight, defaultOpen, flattenOnSearch, scrollToSelectedOnOpen, groupDividers, ...props }: Props): import('react').JSX.Element;
63
81
  export {};
@@ -14,6 +14,30 @@ var getAllOptions = (options) => {
14
14
  if (!options) return [];
15
15
  return options.flatMap((option) => isGroupOption(option) ? option.options : [option]);
16
16
  };
17
+ var byLabel = (a, b) => String(a.label ?? "").localeCompare(String(b.label ?? ""), void 0, {
18
+ numeric: true,
19
+ sensitivity: "base"
20
+ });
21
+ /** One entry per value, the first one seen, sorted by label. */
22
+ var flattenAndSort = (options) => {
23
+ const seen = /* @__PURE__ */ new Set();
24
+ return getAllOptions(options).filter((option) => {
25
+ if (seen.has(option.value)) return false;
26
+ seen.add(option.value);
27
+ return true;
28
+ }).sort(byLabel);
29
+ };
30
+ var withGroupDividers = (options) => {
31
+ let groupsSeen = 0;
32
+ return options.map((option) => {
33
+ if (!isGroupOption(option)) return option;
34
+ groupsSeen += 1;
35
+ return groupsSeen === 1 ? option : {
36
+ ...option,
37
+ className: cx(option.className, SummarizedSelect_module_default.groupDivider)
38
+ };
39
+ });
40
+ };
17
41
  var filterOptions = (options, searchValue) => {
18
42
  if (!options) return [];
19
43
  if (!searchValue) return options;
@@ -35,7 +59,27 @@ var filterOptions = (options, searchValue) => {
35
59
  */
36
60
  var WEIGHT_CLASS = /(^|\s)font-(thin|extralight|light|normal|medium|semibold|bold|extrabold|black|\[\d+\])(\s|$)/;
37
61
  var hasWeightClass = (className) => Boolean(className && WEIGHT_CLASS.test(className));
38
- function SummarizedSelect({ searchPlaceholder, formControlPlaceholder, loading, multiple, value, onChange, options, renderLabel, rootClassName, popupClassName, variant, trigger = "select", triggerWeight, defaultOpen = false, ...props }) {
62
+ /**
63
+ * Puts the option list back at the top each time it opens and each time it is
64
+ * rebuilt for a search. antd scrolls to the selected option from a timeout
65
+ * started in the list's own effect. This wraps the list, so its effect runs
66
+ * after that one and its timeout lands last. It keys off a count of opens, not
67
+ * the open flag: antd does not re-render a closed popup, so the flag it sees
68
+ * can stay `true` from one open to the next.
69
+ */
70
+ var OpenAtTop = ({ openCount, selectRef, children }) => {
71
+ useEffect(() => {
72
+ const timeoutId = setTimeout(() => {
73
+ selectRef.current?.scrollTo({
74
+ index: 0,
75
+ align: "top"
76
+ });
77
+ });
78
+ return () => clearTimeout(timeoutId);
79
+ }, [openCount, selectRef]);
80
+ return children;
81
+ };
82
+ function SummarizedSelect({ searchPlaceholder, formControlPlaceholder, loading, multiple, value, onChange, options, renderLabel, rootClassName, popupClassName, variant, trigger = "select", triggerWeight, defaultOpen = false, flattenOnSearch = false, scrollToSelectedOnOpen = true, groupDividers = false, ...props }) {
39
83
  const isHeadline = variant === "headlined";
40
84
  const isField = variant === "field";
41
85
  const antVariant = isHeadline ? "borderless" : isField ? "outlined" : variant;
@@ -44,6 +88,7 @@ function SummarizedSelect({ searchPlaceholder, formControlPlaceholder, loading,
44
88
  const [focusTrigger, setFocusTrigger] = useState(defaultOpen ? 1 : 0);
45
89
  const [isOpen, setIsOpen] = useState(defaultOpen);
46
90
  const inputRef = useRef(null);
91
+ const selectRef = useRef(null);
47
92
  const measureSpanRef = useRef(null);
48
93
  const [measuredWidth, setMeasuredWidth] = useState();
49
94
  const displayText = useMemo(() => {
@@ -77,8 +122,15 @@ function SummarizedSelect({ searchPlaceholder, formControlPlaceholder, loading,
77
122
  });
78
123
  }, [focusTrigger]);
79
124
  const filteredOptions = useMemo(() => {
80
- return filterOptions(options, searchValue);
81
- }, [searchValue, options]);
125
+ const filtered = filterOptions(options, searchValue);
126
+ if (flattenOnSearch && searchValue) return flattenAndSort(filtered);
127
+ return groupDividers ? withGroupDividers(filtered) : filtered;
128
+ }, [
129
+ searchValue,
130
+ options,
131
+ flattenOnSearch,
132
+ groupDividers
133
+ ]);
82
134
  const flatFilteredOptions = useMemo(() => {
83
135
  return getAllOptions(filteredOptions);
84
136
  }, [filteredOptions]);
@@ -197,7 +249,11 @@ function SummarizedSelect({ searchPlaceholder, formControlPlaceholder, loading,
197
249
  [SummarizedSelect_module_default.headlinedMenuContainer]: isHeadline,
198
250
  [SummarizedSelect_module_default.menuContainerMultiple]: multiple
199
251
  }),
200
- children: /* @__PURE__ */ jsx(Fragment, { children: menu }, searchValue)
252
+ children: /* @__PURE__ */ jsx(Fragment, { children: scrollToSelectedOnOpen ? menu : /* @__PURE__ */ jsx(OpenAtTop, {
253
+ openCount: focusTrigger,
254
+ selectRef,
255
+ children: menu
256
+ }) }, searchValue)
201
257
  }),
202
258
  /* @__PURE__ */ jsx("div", {
203
259
  className: "flex justify-between items-center",
@@ -295,10 +351,13 @@ function SummarizedSelect({ searchPlaceholder, formControlPlaceholder, loading,
295
351
  className: "px-3 py-2 text-sm text-[var(--j2-color-text-secondary)]",
296
352
  children: "No options found"
297
353
  }) : filteredOptions.map((opt) => {
298
- if (isGroupOption(opt)) return /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("div", {
299
- className: "px-3 pt-2 pb-1 text-xs font-semibold uppercase tracking-wide text-[var(--j2-color-text-tertiary)]",
300
- children: opt.label
301
- }), opt.options.map(renderDropdownOptionRow)] }, `group-${opt.label}`);
354
+ if (isGroupOption(opt)) return /* @__PURE__ */ jsxs("div", {
355
+ className: opt.className,
356
+ children: [/* @__PURE__ */ jsx("div", {
357
+ className: "px-3 pt-2 pb-1 text-xs font-semibold uppercase tracking-wide text-[var(--j2-color-text-tertiary)]",
358
+ children: opt.label
359
+ }), opt.options.map(renderDropdownOptionRow)]
360
+ }, `group-${opt.label}`);
302
361
  return renderDropdownOptionRow(opt);
303
362
  })
304
363
  }),
@@ -354,6 +413,7 @@ function SummarizedSelect({ searchPlaceholder, formControlPlaceholder, loading,
354
413
  },
355
414
  children: displayText
356
415
  }), /* @__PURE__ */ jsx(Select, {
416
+ ref: selectRef,
357
417
  open: isOpen,
358
418
  showSearch: false,
359
419
  style: { width: isField ? "100%" : measuredWidth },