prime-ui-kit 0.7.4 → 0.7.8

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 (33) hide show
  1. package/dist/components/index.css +529 -34
  2. package/dist/components/index.css.map +4 -4
  3. package/dist/components/index.d.ts +2 -0
  4. package/dist/components/index.d.ts.map +1 -1
  5. package/dist/components/index.js +1116 -110
  6. package/dist/components/index.js.map +4 -4
  7. package/dist/components/popover/Popover.d.ts +6 -1
  8. package/dist/components/popover/Popover.d.ts.map +1 -1
  9. package/dist/components/select/Select.d.ts +26 -9
  10. package/dist/components/select/Select.d.ts.map +1 -1
  11. package/dist/components/select/examples/pattern-multiple.d.ts +3 -0
  12. package/dist/components/select/examples/pattern-multiple.d.ts.map +1 -0
  13. package/dist/components/tag-select/TagSelect.d.ts +60 -0
  14. package/dist/components/tag-select/TagSelect.d.ts.map +1 -0
  15. package/dist/components/tag-select/examples/pattern-canonical.d.ts +6 -0
  16. package/dist/components/tag-select/examples/pattern-canonical.d.ts.map +1 -0
  17. package/dist/components/tag-select/examples/pattern-features.d.ts +3 -0
  18. package/dist/components/tag-select/examples/pattern-features.d.ts.map +1 -0
  19. package/dist/hooks/usePosition.d.ts.map +1 -1
  20. package/dist/index.css +531 -36
  21. package/dist/index.css.map +4 -4
  22. package/dist/index.js +1116 -110
  23. package/dist/index.js.map +4 -4
  24. package/dist/tokens/semantic.d.ts +1 -1
  25. package/package.json +1 -1
  26. package/src/components/select/COMPONENT.md +91 -32
  27. package/src/components/select/examples/pattern-multiple.tsx +30 -0
  28. package/src/components/tag-select/COMPONENT.md +154 -0
  29. package/src/components/tag-select/examples/examples.module.css +14 -0
  30. package/src/components/tag-select/examples/pattern-canonical.tsx +69 -0
  31. package/src/components/tag-select/examples/pattern-features.tsx +68 -0
  32. package/src/styles/theme-dark.css +1 -1
  33. package/src/styles/theme-light.css +1 -1
@@ -3832,11 +3832,15 @@ function usePosition(anchorRef, contentRef, options = {}) {
3832
3832
  (pos) => {
3833
3833
  const content = contentRef.current;
3834
3834
  if (!content) return;
3835
- content.style.position = pos.position;
3836
- content.style.top = `${pos.top}px`;
3837
- content.style.left = `${pos.left}px`;
3838
- content.style.minWidth = pos.minWidth !== void 0 ? `${pos.minWidth}px` : "";
3839
- content.style.maxHeight = pos.maxHeight !== void 0 ? `${pos.maxHeight}px` : "";
3835
+ const nextTop = `${pos.top}px`;
3836
+ const nextLeft = `${pos.left}px`;
3837
+ const nextMinW = pos.minWidth !== void 0 ? `${pos.minWidth}px` : "";
3838
+ const nextMaxH = pos.maxHeight !== void 0 ? `${pos.maxHeight}px` : "";
3839
+ if (content.style.position !== pos.position) content.style.position = pos.position;
3840
+ if (content.style.top !== nextTop) content.style.top = nextTop;
3841
+ if (content.style.left !== nextLeft) content.style.left = nextLeft;
3842
+ if (content.style.minWidth !== nextMinW) content.style.minWidth = nextMinW;
3843
+ if (content.style.maxHeight !== nextMaxH) content.style.maxHeight = nextMaxH;
3840
3844
  },
3841
3845
  [contentRef]
3842
3846
  );
@@ -3854,7 +3858,7 @@ function usePosition(anchorRef, contentRef, options = {}) {
3854
3858
  window.innerHeight,
3855
3859
  { preferredSide, align, offset, viewportPad, flip, matchTriggerMinWidth }
3856
3860
  );
3857
- setResolvedSide(pos.resolvedSide);
3861
+ setResolvedSide((prev) => pos.resolvedSide === prev ? prev : pos.resolvedSide);
3858
3862
  applyPositionStyle({
3859
3863
  position: "fixed",
3860
3864
  top: pos.top,
@@ -3877,6 +3881,21 @@ function usePosition(anchorRef, contentRef, options = {}) {
3877
3881
  return { resolvedSide, update };
3878
3882
  }
3879
3883
 
3884
+ // src/internal/scrollAncestors.ts
3885
+ var SCROLLABLE = /^(auto|scroll|overlay)$/;
3886
+ function getScrollContainers(node) {
3887
+ const out = [window];
3888
+ if (!node || typeof window === "undefined") return out;
3889
+ for (let el = node.parentElement; el; el = el.parentElement) {
3890
+ const { overflowX, overflowY } = window.getComputedStyle(el);
3891
+ if (SCROLLABLE.test(overflowY) || SCROLLABLE.test(overflowX)) {
3892
+ out.push(el);
3893
+ }
3894
+ }
3895
+ if (window.visualViewport) out.push(window.visualViewport);
3896
+ return out;
3897
+ }
3898
+
3880
3899
  // src/components/select/Select.module.css
3881
3900
  var Select_default = {
3882
3901
  trigger: "Select_trigger2",
@@ -3958,13 +3977,22 @@ function handleSelectListboxKeyDown(e, ctx) {
3958
3977
 
3959
3978
  // src/components/select/Select.tsx
3960
3979
  import { jsx as jsx26, jsxs as jsxs9 } from "react/jsx-runtime";
3980
+ var SELECT_LISTBOX_POSITION_OPTS = {
3981
+ side: "bottom",
3982
+ align: "start"
3983
+ };
3961
3984
  var [SelectProvider, useSelectContext] = createComponentContext("Select");
3962
3985
  function SelectRoot(props) {
3963
- const { native = false, ...rest } = props;
3964
- if (native) {
3965
- return /* @__PURE__ */ jsx26(SelectNativeRoot, { ...rest });
3986
+ if (props.multiple === true) {
3987
+ if (props.native === true) {
3988
+ return /* @__PURE__ */ jsx26(SelectNativeMultiRoot, { ...props });
3989
+ }
3990
+ return /* @__PURE__ */ jsx26(SelectComboboxMultiRoot, { ...props });
3991
+ }
3992
+ if (props.native === true) {
3993
+ return /* @__PURE__ */ jsx26(SelectNativeRoot, { ...props });
3966
3994
  }
3967
- return /* @__PURE__ */ jsx26(SelectComboboxRoot, { ...rest });
3995
+ return /* @__PURE__ */ jsx26(SelectComboboxRoot, { ...props });
3968
3996
  }
3969
3997
  SelectRoot.displayName = "SelectRoot";
3970
3998
  function SelectComboboxRoot({
@@ -4012,32 +4040,138 @@ function SelectComboboxRoot({
4012
4040
  );
4013
4041
  const onClose = React38.useCallback(() => setIsOpen(false), []);
4014
4042
  const onOpen = React38.useCallback(() => setIsOpen(true), []);
4015
- return /* @__PURE__ */ jsx26(
4016
- SelectProvider,
4017
- {
4018
- value: {
4019
- size,
4020
- hasError,
4021
- isOpen,
4022
- selectedValue,
4023
- selectedLabelBinding,
4024
- onSelect,
4025
- onClose,
4026
- onOpen,
4027
- highlightedValue,
4028
- setHighlightedValue,
4029
- triggerId,
4030
- listboxId,
4031
- triggerRef,
4032
- disabled,
4033
- placeholder,
4034
- onInitLabel
4035
- },
4036
- children: /* @__PURE__ */ jsx26(ControlSizeProvider, { value: size, children })
4037
- }
4043
+ const contextValue = React38.useMemo(
4044
+ () => ({
4045
+ size,
4046
+ hasError,
4047
+ isOpen,
4048
+ multiple: false,
4049
+ selectedValue,
4050
+ selectedLabelBinding,
4051
+ selectedValues: [],
4052
+ labelsByValue: {},
4053
+ onSelect,
4054
+ onClose,
4055
+ onOpen,
4056
+ highlightedValue,
4057
+ setHighlightedValue,
4058
+ triggerId,
4059
+ listboxId,
4060
+ triggerRef,
4061
+ disabled,
4062
+ placeholder,
4063
+ onInitLabel
4064
+ }),
4065
+ [
4066
+ size,
4067
+ hasError,
4068
+ isOpen,
4069
+ selectedValue,
4070
+ selectedLabelBinding,
4071
+ onSelect,
4072
+ onClose,
4073
+ onOpen,
4074
+ highlightedValue,
4075
+ triggerId,
4076
+ listboxId,
4077
+ disabled,
4078
+ placeholder,
4079
+ onInitLabel
4080
+ ]
4038
4081
  );
4082
+ return /* @__PURE__ */ jsx26(SelectProvider, { value: contextValue, children: /* @__PURE__ */ jsx26(ControlSizeProvider, { value: size, children }) });
4039
4083
  }
4040
4084
  SelectComboboxRoot.displayName = "SelectComboboxRoot";
4085
+ function SelectComboboxMultiRoot({
4086
+ size = "m",
4087
+ value,
4088
+ defaultValue,
4089
+ onChange,
4090
+ disabled,
4091
+ placeholder,
4092
+ hasError = false,
4093
+ children
4094
+ }) {
4095
+ const handleChange = React38.useCallback(
4096
+ (next) => {
4097
+ onChange?.(next);
4098
+ },
4099
+ [onChange]
4100
+ );
4101
+ const [selectedValues, setSelectedValues] = useControllableState({
4102
+ value,
4103
+ defaultValue: defaultValue ?? [],
4104
+ onChange: handleChange
4105
+ });
4106
+ const [labelsByValue, setLabelsByValue] = React38.useState({});
4107
+ const [isOpen, setIsOpen] = React38.useState(false);
4108
+ const [highlightedValue, setHighlightedValue] = React38.useState(void 0);
4109
+ const generatedId = React38.useId();
4110
+ const triggerId = `${generatedId}-trigger`;
4111
+ const listboxId = `${generatedId}-listbox`;
4112
+ const triggerRef = React38.useRef(null);
4113
+ const onInitLabel = React38.useCallback((val, label) => {
4114
+ setLabelsByValue((prev) => {
4115
+ if (prev[val] === label) return prev;
4116
+ return { ...prev, [val]: label };
4117
+ });
4118
+ }, []);
4119
+ const onSelect = React38.useCallback(
4120
+ (val, label) => {
4121
+ setLabelsByValue((prev) => ({ ...prev, [val]: label }));
4122
+ setSelectedValues((prev) => {
4123
+ if (prev.includes(val)) {
4124
+ return prev.filter((x) => x !== val);
4125
+ }
4126
+ return [...prev, val];
4127
+ });
4128
+ },
4129
+ [setSelectedValues]
4130
+ );
4131
+ const onClose = React38.useCallback(() => setIsOpen(false), []);
4132
+ const onOpen = React38.useCallback(() => setIsOpen(true), []);
4133
+ const contextValue = React38.useMemo(
4134
+ () => ({
4135
+ size,
4136
+ hasError,
4137
+ isOpen,
4138
+ multiple: true,
4139
+ selectedValue: void 0,
4140
+ selectedLabelBinding: void 0,
4141
+ selectedValues,
4142
+ labelsByValue,
4143
+ onSelect,
4144
+ onClose,
4145
+ onOpen,
4146
+ highlightedValue,
4147
+ setHighlightedValue,
4148
+ triggerId,
4149
+ listboxId,
4150
+ triggerRef,
4151
+ disabled,
4152
+ placeholder,
4153
+ onInitLabel
4154
+ }),
4155
+ [
4156
+ size,
4157
+ hasError,
4158
+ isOpen,
4159
+ selectedValues,
4160
+ labelsByValue,
4161
+ onSelect,
4162
+ onClose,
4163
+ onOpen,
4164
+ highlightedValue,
4165
+ triggerId,
4166
+ listboxId,
4167
+ disabled,
4168
+ placeholder,
4169
+ onInitLabel
4170
+ ]
4171
+ );
4172
+ return /* @__PURE__ */ jsx26(SelectProvider, { value: contextValue, children: /* @__PURE__ */ jsx26(ControlSizeProvider, { value: size, children }) });
4173
+ }
4174
+ SelectComboboxMultiRoot.displayName = "SelectComboboxMultiRoot";
4041
4175
  var SelectTrigger = React38.forwardRef(
4042
4176
  ({ className, children, onClick, onKeyDown, ...rest }, forwardedRef) => {
4043
4177
  const { isOpen, onOpen, onClose, triggerId, listboxId, disabled, size, hasError, triggerRef } = useSelectContext();
@@ -4092,7 +4226,20 @@ var SelectTrigger = React38.forwardRef(
4092
4226
  );
4093
4227
  SelectTrigger.displayName = "SelectTrigger";
4094
4228
  function SelectValue({ className }) {
4095
- const { selectedLabelBinding, selectedValue, placeholder } = useSelectContext();
4229
+ const ctx = useSelectContext();
4230
+ if (ctx.multiple) {
4231
+ const { selectedValues, labelsByValue, placeholder: placeholder2 } = ctx;
4232
+ const display2 = selectedValues.length === 0 ? placeholder2 : selectedValues.map((v) => labelsByValue[v] ?? v).join(", ");
4233
+ return /* @__PURE__ */ jsx26(
4234
+ "span",
4235
+ {
4236
+ className: cx(Select_default.triggerValue, className),
4237
+ ...toDataAttributes({ placeholder: display2 == null || display2 === "" }),
4238
+ children: display2
4239
+ }
4240
+ );
4241
+ }
4242
+ const { selectedLabelBinding, selectedValue, placeholder } = ctx;
4096
4243
  const display = selectedLabelBinding && selectedLabelBinding.value === selectedValue ? selectedLabelBinding.label : selectedValue ?? placeholder;
4097
4244
  return /* @__PURE__ */ jsx26(
4098
4245
  "span",
@@ -4119,67 +4266,109 @@ function SelectContent({ className, children }) {
4119
4266
  highlightedValue,
4120
4267
  setHighlightedValue,
4121
4268
  selectedValue,
4269
+ selectedValues,
4270
+ multiple,
4122
4271
  size
4123
4272
  } = useSelectContext();
4273
+ const selectedValueRef = React38.useRef(selectedValue);
4274
+ selectedValueRef.current = selectedValue;
4275
+ const selectedValuesRef = React38.useRef(selectedValues);
4276
+ selectedValuesRef.current = selectedValues;
4277
+ const multipleRef = React38.useRef(multiple);
4278
+ multipleRef.current = multiple;
4124
4279
  const overlayPortalLayer = useOverlayPortalLayer();
4125
4280
  const contentRef = React38.useRef(null);
4126
- const { resolvedSide, update } = usePosition(triggerRef, contentRef, {
4127
- side: "bottom",
4128
- align: "start"
4129
- });
4281
+ const { resolvedSide, update } = usePosition(
4282
+ triggerRef,
4283
+ contentRef,
4284
+ SELECT_LISTBOX_POSITION_OPTS
4285
+ );
4286
+ const updateRef = React38.useRef(update);
4287
+ updateRef.current = update;
4130
4288
  const getItems = React38.useCallback(() => queryEnabledSelectOptions(contentRef.current), []);
4131
4289
  React38.useLayoutEffect(() => {
4132
4290
  if (!isOpen) return;
4133
- update();
4134
- const rafId = requestAnimationFrame(() => update());
4291
+ updateRef.current();
4292
+ const rafId = requestAnimationFrame(() => updateRef.current());
4135
4293
  return () => cancelAnimationFrame(rafId);
4136
- }, [isOpen, update]);
4294
+ }, [isOpen]);
4137
4295
  React38.useEffect(() => {
4138
4296
  if (!isOpen) {
4139
4297
  setHighlightedValue(void 0);
4140
4298
  return;
4141
4299
  }
4142
- const reposition = () => {
4143
- requestAnimationFrame(() => update());
4144
- };
4145
4300
  const bootstrap = () => {
4146
4301
  requestAnimationFrame(() => {
4147
4302
  const el = contentRef.current;
4148
4303
  if (!el) return;
4149
4304
  el.focus({ preventScroll: true });
4150
4305
  const items = queryEnabledSelectOptions(el);
4151
- const selectedIndex = items.findIndex((i) => i.dataset.value === selectedValue);
4152
- if (selectedIndex >= 0 && selectedValue) {
4153
- setHighlightedValue(selectedValue);
4306
+ if (multipleRef.current) {
4307
+ const sv = selectedValuesRef.current;
4308
+ const firstSelected = sv.find((v) => items.some((i) => i.dataset.value === v));
4309
+ setHighlightedValue(firstSelected ?? void 0);
4310
+ } else {
4311
+ const sv = selectedValueRef.current;
4312
+ const selectedIndex = items.findIndex((i) => i.dataset.value === sv);
4313
+ if (selectedIndex >= 0 && sv) {
4314
+ setHighlightedValue(sv);
4315
+ }
4154
4316
  }
4155
4317
  });
4156
4318
  };
4157
4319
  bootstrap();
4158
- window.addEventListener("resize", reposition);
4320
+ }, [isOpen, setHighlightedValue]);
4321
+ React38.useEffect(() => {
4322
+ if (!isOpen) return;
4323
+ let rafCoalesce = 0;
4324
+ const schedule = () => {
4325
+ cancelAnimationFrame(rafCoalesce);
4326
+ rafCoalesce = requestAnimationFrame(() => updateRef.current());
4327
+ };
4328
+ window.addEventListener("resize", schedule);
4329
+ const scrollTargets = getScrollContainers(triggerRef.current);
4330
+ for (const t of scrollTargets) {
4331
+ t.addEventListener("scroll", schedule, { passive: true });
4332
+ }
4159
4333
  const vv = window.visualViewport;
4160
- vv?.addEventListener("resize", reposition);
4334
+ vv?.addEventListener("resize", schedule);
4335
+ const panel = contentRef.current;
4336
+ let ro = null;
4337
+ if (typeof ResizeObserver !== "undefined" && panel) {
4338
+ ro = new ResizeObserver(schedule);
4339
+ ro.observe(panel);
4340
+ }
4161
4341
  return () => {
4162
- window.removeEventListener("resize", reposition);
4163
- vv?.removeEventListener("resize", reposition);
4342
+ cancelAnimationFrame(rafCoalesce);
4343
+ window.removeEventListener("resize", schedule);
4344
+ for (const t of scrollTargets) {
4345
+ t.removeEventListener("scroll", schedule);
4346
+ }
4347
+ vv?.removeEventListener("resize", schedule);
4348
+ ro?.disconnect();
4164
4349
  };
4165
- }, [isOpen, update, selectedValue, setHighlightedValue]);
4350
+ }, [isOpen, triggerRef]);
4166
4351
  useEscapeKey({ enabled: isOpen, onEscape: onClose });
4167
4352
  useOutsideClick({ refs: [triggerRef, contentRef], enabled: isOpen, onOutsideClick: onClose });
4168
- const handleKeyDown = (e) => {
4169
- handleSelectListboxKeyDown(e, {
4170
- items: getItems(),
4171
- highlightedValue,
4172
- setHighlightedValue,
4173
- onSelect,
4174
- onClose
4175
- });
4176
- };
4353
+ const handleKeyDown = React38.useCallback(
4354
+ (e) => {
4355
+ handleSelectListboxKeyDown(e, {
4356
+ items: getItems(),
4357
+ highlightedValue,
4358
+ setHighlightedValue,
4359
+ onSelect,
4360
+ onClose
4361
+ });
4362
+ },
4363
+ [getItems, highlightedValue, setHighlightedValue, onSelect, onClose]
4364
+ );
4177
4365
  return /* @__PURE__ */ jsx26(Portal, { children: /* @__PURE__ */ jsx26(
4178
4366
  ScrollContainer,
4179
4367
  {
4180
4368
  ref: contentRef,
4181
4369
  id: listboxId,
4182
4370
  role: "listbox",
4371
+ "aria-multiselectable": multiple ? true : void 0,
4183
4372
  "aria-labelledby": triggerId,
4184
4373
  "aria-hidden": !isOpen,
4185
4374
  tabIndex: -1,
@@ -4198,6 +4387,17 @@ function SelectItemIcon({ className, children, ...rest }) {
4198
4387
  return /* @__PURE__ */ jsx26("span", { className: cx(Select_default.itemIcon, className), ...rest, children });
4199
4388
  }
4200
4389
  SelectItemIcon.displayName = "SelectItemIcon";
4390
+ var SELECT_ITEM_ICON_MARKER = "__primeSelectItemIcon";
4391
+ Object.assign(SelectItemIcon, { [SELECT_ITEM_ICON_MARKER]: true });
4392
+ function isSelectItemIconType(type) {
4393
+ if (type === SelectItemIcon) return true;
4394
+ if (typeof type === "function") {
4395
+ const fn = type;
4396
+ if (fn[SELECT_ITEM_ICON_MARKER] === true) return true;
4397
+ if (fn.displayName === "SelectItemIcon") return true;
4398
+ }
4399
+ return false;
4400
+ }
4201
4401
  function selectItemTextFromRest(rest) {
4202
4402
  const parts = [];
4203
4403
  for (const node of rest) {
@@ -4212,7 +4412,7 @@ function partitionSelectItemChildren(children) {
4212
4412
  const icons = [];
4213
4413
  const rest = [];
4214
4414
  React38.Children.forEach(children, (child) => {
4215
- if (React38.isValidElement(child) && child.type === SelectItemIcon) {
4415
+ if (React38.isValidElement(child) && isSelectItemIconType(child.type)) {
4216
4416
  icons.push(child);
4217
4417
  } else if (child != null && child !== false) {
4218
4418
  rest.push(child);
@@ -4222,9 +4422,18 @@ function partitionSelectItemChildren(children) {
4222
4422
  }
4223
4423
  var SelectItem = React38.forwardRef(
4224
4424
  ({ value, label, disabled, className, children }, ref) => {
4225
- const { selectedValue, highlightedValue, setHighlightedValue, onSelect, onInitLabel } = useSelectContext();
4425
+ const {
4426
+ multiple,
4427
+ size,
4428
+ selectedValue,
4429
+ selectedValues,
4430
+ highlightedValue,
4431
+ setHighlightedValue,
4432
+ onSelect,
4433
+ onInitLabel
4434
+ } = useSelectContext();
4226
4435
  const { icons, rest } = partitionSelectItemChildren(children);
4227
- const isSelected = selectedValue === value;
4436
+ const isSelected = multiple ? selectedValues.includes(value) : selectedValue === value;
4228
4437
  const isHighlighted = highlightedValue === value;
4229
4438
  const resolvedLabel = label ?? selectItemTextFromRest(rest) ?? (typeof children === "string" ? children : void 0) ?? value;
4230
4439
  React38.useEffect(() => {
@@ -4259,7 +4468,8 @@ var SelectItem = React38.forwardRef(
4259
4468
  label: resolvedLabel,
4260
4469
  selected: isSelected,
4261
4470
  highlighted: isHighlighted,
4262
- disabled: Boolean(disabled)
4471
+ disabled: Boolean(disabled),
4472
+ size
4263
4473
  }),
4264
4474
  children: [
4265
4475
  icons.map(
@@ -4280,7 +4490,8 @@ function SelectGroup({ className, ...rest }) {
4280
4490
  }
4281
4491
  SelectGroup.displayName = "SelectGroup";
4282
4492
  function SelectGroupLabel({ className, ...rest }) {
4283
- return /* @__PURE__ */ jsx26("div", { className: cx(Select_default.groupLabel, className), ...rest });
4493
+ const { size } = useSelectContext();
4494
+ return /* @__PURE__ */ jsx26("div", { className: cx(Select_default.groupLabel, className), ...rest, ...toDataAttributes({ size }) });
4284
4495
  }
4285
4496
  SelectGroupLabel.displayName = "SelectGroupLabel";
4286
4497
  function SelectSeparator({ className, ...rest }) {
@@ -4416,6 +4627,49 @@ function SelectNativeRoot({
4416
4627
  ) });
4417
4628
  }
4418
4629
  SelectNativeRoot.displayName = "SelectNativeRoot";
4630
+ function SelectNativeMultiRoot({
4631
+ size = "m",
4632
+ value,
4633
+ defaultValue,
4634
+ onChange,
4635
+ disabled,
4636
+ hasError = false,
4637
+ children
4638
+ }) {
4639
+ const handleChange = React38.useCallback(
4640
+ (next) => {
4641
+ onChange?.(next);
4642
+ },
4643
+ [onChange]
4644
+ );
4645
+ const [selectedValues, setSelectedValues] = useControllableState({
4646
+ value,
4647
+ defaultValue: defaultValue ?? [],
4648
+ onChange: handleChange
4649
+ });
4650
+ const { nodes: optionNodes } = React38.useMemo(() => walkNativeOptions(children), [children]);
4651
+ const handleNativeChange = React38.useCallback(
4652
+ (e) => {
4653
+ const next = Array.from(e.target.selectedOptions, (o) => o.value);
4654
+ setSelectedValues(next);
4655
+ },
4656
+ [setSelectedValues]
4657
+ );
4658
+ return /* @__PURE__ */ jsx26(ControlSizeProvider, { value: size, children: /* @__PURE__ */ jsx26(
4659
+ "select",
4660
+ {
4661
+ className: Select_default.nativeSelect,
4662
+ "data-multiple": "true",
4663
+ disabled,
4664
+ multiple: true,
4665
+ value: selectedValues,
4666
+ onChange: handleNativeChange,
4667
+ ...toDataAttributes({ size, "has-error": hasError }),
4668
+ children: optionNodes
4669
+ }
4670
+ ) });
4671
+ }
4672
+ SelectNativeMultiRoot.displayName = "SelectNativeMultiRoot";
4419
4673
  var Select = {
4420
4674
  Root: SelectRoot,
4421
4675
  Trigger: SelectTrigger,
@@ -7068,21 +7322,6 @@ function handleMenuNavigationKeyDown(e, container) {
7068
7322
  // src/components/dropdown/useDropdownPosition.ts
7069
7323
  import * as React48 from "react";
7070
7324
 
7071
- // src/internal/scrollAncestors.ts
7072
- var SCROLLABLE = /^(auto|scroll|overlay)$/;
7073
- function getScrollContainers(node) {
7074
- const out = [window];
7075
- if (!node || typeof window === "undefined") return out;
7076
- for (let el = node.parentElement; el; el = el.parentElement) {
7077
- const { overflowX, overflowY } = window.getComputedStyle(el);
7078
- if (SCROLLABLE.test(overflowY) || SCROLLABLE.test(overflowX)) {
7079
- out.push(el);
7080
- }
7081
- }
7082
- if (window.visualViewport) out.push(window.visualViewport);
7083
- return out;
7084
- }
7085
-
7086
7325
  // src/components/dropdown/dropdownGeometry.ts
7087
7326
  var DROPDOWN_MIN_MAX_HEIGHT = 120;
7088
7327
  function getDropdownMaxHeightForAnchorSide(anchor, side, viewportHeight, panelOffsetPx, viewportPadPx) {
@@ -8891,6 +9130,7 @@ function PopoverContent({
8891
9130
  trapFocus = false,
8892
9131
  insetPadding = "none",
8893
9132
  insetGap = "pad",
9133
+ stackAboveDropdown = false,
8894
9134
  children,
8895
9135
  className
8896
9136
  }) {
@@ -8928,6 +9168,7 @@ function PopoverContent({
8928
9168
  "aria-labelledby": triggerId,
8929
9169
  "data-react-aria-top-layer": "true",
8930
9170
  "data-overlay-portal-layer": overlayPortalLayer,
9171
+ "data-overlay-stack": stackAboveDropdown ? "above-dropdown" : void 0,
8931
9172
  "data-side": layout?.resolvedSide ?? side,
8932
9173
  "data-size": size,
8933
9174
  "data-inset-padding": insetPadding,
@@ -10146,9 +10387,773 @@ function TagIcon({ children, className }) {
10146
10387
  TagIcon.displayName = "TagIcon";
10147
10388
  var Tag = { Root: TagRoot, Icon: TagIcon };
10148
10389
 
10149
- // src/components/textarea/Textarea.tsx
10390
+ // src/components/tag-select/TagSelect.tsx
10150
10391
  import * as React71 from "react";
10151
10392
 
10393
+ // src/components/tag-select/TagSelect.module.css
10394
+ var TagSelect_default = {
10395
+ control: "TagSelect_control2",
10396
+ chips: "TagSelect_chips2",
10397
+ chip: "TagSelect_chip2",
10398
+ chipBadge: "TagSelect_chipBadge2",
10399
+ chipLabel: "TagSelect_chipLabel2",
10400
+ chipRemove: "TagSelect_chipRemove2",
10401
+ removeIcon: "TagSelect_removeIcon2",
10402
+ input: "TagSelect_input2",
10403
+ inputCollapsed: "TagSelect_inputCollapsed2",
10404
+ chevronSlot: "TagSelect_chevronSlot2",
10405
+ chevron: "TagSelect_chevron2",
10406
+ panelInner: "TagSelect_panelInner2",
10407
+ hint: "TagSelect_hint2",
10408
+ optionRow: "TagSelect_optionRow2",
10409
+ createRow: "TagSelect_createRow2",
10410
+ createLabel: "TagSelect_createLabel2",
10411
+ optionRowWrap: "TagSelect_optionRowWrap2",
10412
+ optionRowSelect: "TagSelect_optionRowSelect2",
10413
+ optionMenuTrigger: "TagSelect_optionMenuTrigger2",
10414
+ optionMenuDots: "TagSelect_optionMenuDots2",
10415
+ managePopoverSurface: "TagSelect_managePopoverSurface2",
10416
+ managePopoverShell: "TagSelect_managePopoverShell2",
10417
+ manageDelete: "TagSelect_manageDelete2",
10418
+ manageDeleteIcon: "TagSelect_manageDeleteIcon2",
10419
+ manageSeparator: "TagSelect_manageSeparator2",
10420
+ manageColorsHeading: "TagSelect_manageColorsHeading2",
10421
+ manageColorList: "TagSelect_manageColorList2",
10422
+ manageColorRow: "TagSelect_manageColorRow2",
10423
+ manageColorSwatch: "TagSelect_manageColorSwatch2",
10424
+ manageColorLabel: "TagSelect_manageColorLabel2",
10425
+ manageCheck: "TagSelect_manageCheck2",
10426
+ manageCheckPlaceholder: "TagSelect_manageCheckPlaceholder2"
10427
+ };
10428
+
10429
+ // src/components/tag-select/TagSelect.tsx
10430
+ import { Fragment as Fragment6, jsx as jsx57, jsxs as jsxs31 } from "react/jsx-runtime";
10431
+ var CREATE_VALUE = "__prime_tag_select_create__";
10432
+ var TAG_COLOR_CHOICES = [
10433
+ { color: "gray", label: "Default" },
10434
+ { color: "red", label: "Red" },
10435
+ { color: "orange", label: "Orange" },
10436
+ { color: "yellow", label: "Yellow" },
10437
+ { color: "green", label: "Green" },
10438
+ { color: "blue", label: "Blue" },
10439
+ { color: "purple", label: "Purple" },
10440
+ { color: "pink", label: "Pink" },
10441
+ { color: "sky", label: "Sky" },
10442
+ { color: "teal", label: "Teal" }
10443
+ ];
10444
+ function normalizeList(selected, options, defaultTagColor) {
10445
+ return selected.map((v) => {
10446
+ const o = options.find((x) => x.value === v);
10447
+ return {
10448
+ value: v,
10449
+ label: o?.label ?? v,
10450
+ color: o?.color ?? defaultTagColor
10451
+ };
10452
+ });
10453
+ }
10454
+ function filterOptions(options, query) {
10455
+ const q = query.trim().toLowerCase();
10456
+ if (q.length === 0) return options;
10457
+ return options.filter((o) => {
10458
+ if (o.disabled) return false;
10459
+ return o.label.toLowerCase().includes(q) || o.value.toLowerCase().includes(q);
10460
+ });
10461
+ }
10462
+ function optionsForList(options, query, selected) {
10463
+ return filterOptions(options, query).filter((o) => !selected.includes(o.value));
10464
+ }
10465
+ function shouldShowCreate(creatable, inputTrim, selected, options) {
10466
+ if (!creatable || inputTrim.length === 0) return false;
10467
+ if (selected.includes(inputTrim)) return false;
10468
+ const lower = inputTrim.toLowerCase();
10469
+ const exists = options.some(
10470
+ (o) => o.value === inputTrim || o.label.toLowerCase() === lower || o.value.toLowerCase() === lower
10471
+ );
10472
+ return !exists;
10473
+ }
10474
+ function mergeOptionsWithCreated(options, created) {
10475
+ const existing = new Set(options.map((o) => o.value));
10476
+ const extra = created.filter((c) => !existing.has(c.value));
10477
+ return extra.length === 0 ? options : [...options, ...extra];
10478
+ }
10479
+ function TagOptionManagePopover({
10480
+ option,
10481
+ open,
10482
+ onOpenChange,
10483
+ defaultTagColor,
10484
+ management,
10485
+ disabled
10486
+ }) {
10487
+ const inputRef = React71.useRef(null);
10488
+ const resolvedColor = option.color ?? defaultTagColor;
10489
+ const [draftLabel, setDraftLabel] = React71.useState(option.label);
10490
+ React71.useEffect(() => {
10491
+ if (open) {
10492
+ setDraftLabel(option.label);
10493
+ }
10494
+ }, [open, option.label]);
10495
+ React71.useEffect(() => {
10496
+ if (!open) return;
10497
+ const id = requestAnimationFrame(() => inputRef.current?.focus());
10498
+ return () => cancelAnimationFrame(id);
10499
+ }, [open]);
10500
+ const commitLabel = () => {
10501
+ const next = draftLabel.trim();
10502
+ if (next.length > 0 && next !== option.label) {
10503
+ management.onUpdate(option.value, { label: next });
10504
+ }
10505
+ if (next.length === 0) {
10506
+ setDraftLabel(option.label);
10507
+ }
10508
+ };
10509
+ const colorsSectionLabel = management.colorsSectionLabel ?? "Colors";
10510
+ const deleteLabel = management.deleteLabel ?? "Delete";
10511
+ const menuPrefix = management.editMenuAriaLabelPrefix ?? "Edit tag";
10512
+ return /* @__PURE__ */ jsxs31(Popover.Root, { open, onOpenChange, children: [
10513
+ /* @__PURE__ */ jsx57(Popover.Trigger, { asChild: true, children: /* @__PURE__ */ jsx57(
10514
+ "button",
10515
+ {
10516
+ type: "button",
10517
+ className: TagSelect_default.optionMenuTrigger,
10518
+ "aria-label": `${menuPrefix} ${option.label}`,
10519
+ disabled,
10520
+ onMouseDown: (e) => {
10521
+ e.preventDefault();
10522
+ e.stopPropagation();
10523
+ },
10524
+ onClick: (e) => {
10525
+ e.stopPropagation();
10526
+ },
10527
+ children: /* @__PURE__ */ jsxs31(
10528
+ "svg",
10529
+ {
10530
+ className: TagSelect_default.optionMenuDots,
10531
+ viewBox: "0 0 16 16",
10532
+ fill: "currentColor",
10533
+ "aria-hidden": true,
10534
+ children: [
10535
+ /* @__PURE__ */ jsx57("circle", { cx: "4", cy: "8", r: "1.5" }),
10536
+ /* @__PURE__ */ jsx57("circle", { cx: "8", cy: "8", r: "1.5" }),
10537
+ /* @__PURE__ */ jsx57("circle", { cx: "12", cy: "8", r: "1.5" })
10538
+ ]
10539
+ }
10540
+ )
10541
+ }
10542
+ ) }),
10543
+ /* @__PURE__ */ jsx57(
10544
+ Popover.Content,
10545
+ {
10546
+ side: "bottom",
10547
+ align: "end",
10548
+ trapFocus: false,
10549
+ insetPadding: "none",
10550
+ insetGap: "none",
10551
+ size: "s",
10552
+ stackAboveDropdown: true,
10553
+ className: TagSelect_default.managePopoverSurface,
10554
+ children: /* @__PURE__ */ jsxs31(
10555
+ "fieldset",
10556
+ {
10557
+ className: TagSelect_default.managePopoverShell,
10558
+ onKeyDown: (e) => {
10559
+ e.stopPropagation();
10560
+ },
10561
+ children: [
10562
+ /* @__PURE__ */ jsx57(Input.Root, { size: "s", children: /* @__PURE__ */ jsx57(Input.Wrapper, { children: /* @__PURE__ */ jsx57(
10563
+ Input.Field,
10564
+ {
10565
+ ref: inputRef,
10566
+ value: draftLabel,
10567
+ onChange: (e) => setDraftLabel(e.target.value),
10568
+ onBlur: commitLabel,
10569
+ onKeyDown: (e) => {
10570
+ if (e.key === "Enter") {
10571
+ e.preventDefault();
10572
+ commitLabel();
10573
+ onOpenChange(false);
10574
+ }
10575
+ },
10576
+ "aria-label": "Tag name"
10577
+ }
10578
+ ) }) }),
10579
+ /* @__PURE__ */ jsxs31(
10580
+ Button.Root,
10581
+ {
10582
+ type: "button",
10583
+ variant: "error",
10584
+ mode: "ghost",
10585
+ size: "s",
10586
+ className: TagSelect_default.manageDelete,
10587
+ onClick: () => {
10588
+ management.onDelete(option.value);
10589
+ onOpenChange(false);
10590
+ },
10591
+ children: [
10592
+ /* @__PURE__ */ jsx57("svg", { className: TagSelect_default.manageDeleteIcon, viewBox: "0 0 16 16", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ jsx57(
10593
+ "path",
10594
+ {
10595
+ d: "M4 4h8M6 4V3h4v1m2 0v9a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V4h10zM6 7v4M10 7v4",
10596
+ stroke: "currentColor",
10597
+ strokeWidth: "1.25",
10598
+ strokeLinecap: "round",
10599
+ strokeLinejoin: "round"
10600
+ }
10601
+ ) }),
10602
+ deleteLabel
10603
+ ]
10604
+ }
10605
+ ),
10606
+ /* @__PURE__ */ jsx57("hr", { className: TagSelect_default.manageSeparator }),
10607
+ /* @__PURE__ */ jsx57("span", { className: TagSelect_default.manageColorsHeading, children: colorsSectionLabel }),
10608
+ /* @__PURE__ */ jsx57("div", { className: TagSelect_default.manageColorList, children: TAG_COLOR_CHOICES.map((row) => {
10609
+ const selected = resolvedColor === row.color;
10610
+ return /* @__PURE__ */ jsxs31(
10611
+ "button",
10612
+ {
10613
+ type: "button",
10614
+ className: TagSelect_default.manageColorRow,
10615
+ onClick: () => {
10616
+ management.onUpdate(option.value, { color: row.color });
10617
+ },
10618
+ children: [
10619
+ /* @__PURE__ */ jsx57(
10620
+ "span",
10621
+ {
10622
+ className: TagSelect_default.manageColorSwatch,
10623
+ "aria-hidden": true,
10624
+ ...toDataAttributes({ color: row.color })
10625
+ }
10626
+ ),
10627
+ /* @__PURE__ */ jsx57("span", { className: TagSelect_default.manageColorLabel, children: row.label }),
10628
+ selected ? /* @__PURE__ */ jsx57(Fragment6, { children: /* @__PURE__ */ jsx57("svg", { className: TagSelect_default.manageCheck, viewBox: "0 0 16 16", "aria-hidden": true, children: /* @__PURE__ */ jsx57(
10629
+ "path",
10630
+ {
10631
+ d: "M3 8l3 3 7-7",
10632
+ stroke: "currentColor",
10633
+ strokeWidth: "1.75",
10634
+ fill: "none",
10635
+ strokeLinecap: "round",
10636
+ strokeLinejoin: "round"
10637
+ }
10638
+ ) }) }) : /* @__PURE__ */ jsx57("span", { className: TagSelect_default.manageCheckPlaceholder, "aria-hidden": true })
10639
+ ]
10640
+ },
10641
+ `${row.label}-${row.color}`
10642
+ );
10643
+ }) })
10644
+ ]
10645
+ }
10646
+ )
10647
+ }
10648
+ )
10649
+ ] });
10650
+ }
10651
+ function TagSelectRoot({
10652
+ options,
10653
+ value: valueProp,
10654
+ defaultValue = [],
10655
+ onValueChange,
10656
+ creatable = false,
10657
+ onCreated,
10658
+ defaultTagColor = "gray",
10659
+ hint = "Select an option or create one",
10660
+ createActionLabel = "Create",
10661
+ disabled = false,
10662
+ placeholder = "",
10663
+ hasError = false,
10664
+ size = "m",
10665
+ optionManagement,
10666
+ id: idProp,
10667
+ className,
10668
+ "aria-label": ariaLabel,
10669
+ "aria-labelledby": ariaLabelledBy
10670
+ }) {
10671
+ const generatedId = React71.useId();
10672
+ const rootId = idProp ?? generatedId;
10673
+ const listboxId = `${rootId}-listbox`;
10674
+ const inputId = `${rootId}-input`;
10675
+ const [selected, setSelected] = useControllableState({
10676
+ value: valueProp,
10677
+ defaultValue,
10678
+ onChange: onValueChange
10679
+ });
10680
+ const [inputValue, setInputValue] = React71.useState("");
10681
+ const [inputFocused, setInputFocused] = React71.useState(false);
10682
+ const [open, setOpen] = React71.useState(false);
10683
+ const [highlightedValue, setHighlightedValue] = React71.useState(void 0);
10684
+ const [manageOpenValue, setManageOpenValue] = React71.useState(null);
10685
+ const [createdOptions, setCreatedOptions] = React71.useState([]);
10686
+ const triggerRef = React71.useRef(null);
10687
+ const inputRef = React71.useRef(null);
10688
+ const listboxRef = React71.useRef(null);
10689
+ const overlayPortalLayer = useOverlayPortalLayer();
10690
+ const { resolvedSide, update } = usePosition(triggerRef, listboxRef, {
10691
+ side: "bottom",
10692
+ align: "start"
10693
+ });
10694
+ const updateRef = React71.useRef(update);
10695
+ updateRef.current = update;
10696
+ const inputTrim = inputValue.trim();
10697
+ const mergedOptions = React71.useMemo(
10698
+ () => mergeOptionsWithCreated(options, createdOptions),
10699
+ [options, createdOptions]
10700
+ );
10701
+ React71.useEffect(() => {
10702
+ setCreatedOptions((prev) => {
10703
+ const next = prev.filter((c) => !options.some((o) => o.value === c.value));
10704
+ return next.length === prev.length ? prev : next;
10705
+ });
10706
+ }, [options]);
10707
+ const filtered = React71.useMemo(
10708
+ () => optionsForList(mergedOptions, inputValue, selected),
10709
+ [mergedOptions, inputValue, selected]
10710
+ );
10711
+ const showCreate = shouldShowCreate(creatable, inputTrim, selected, mergedOptions);
10712
+ const resolvedOptionManagement = React71.useMemo(() => {
10713
+ if (!optionManagement) return void 0;
10714
+ return {
10715
+ ...optionManagement,
10716
+ onUpdate: (value, updates) => {
10717
+ const inProps = options.some((o) => o.value === value);
10718
+ if (!inProps) {
10719
+ setCreatedOptions(
10720
+ (prev) => prev.map(
10721
+ (o) => o.value === value ? {
10722
+ ...o,
10723
+ ...updates.label !== void 0 ? { label: updates.label } : {},
10724
+ ...updates.color !== void 0 ? { color: updates.color } : {}
10725
+ } : o
10726
+ )
10727
+ );
10728
+ }
10729
+ optionManagement.onUpdate(value, updates);
10730
+ },
10731
+ onDelete: (value) => {
10732
+ setSelected((prev) => prev.filter((x) => x !== value));
10733
+ setCreatedOptions((prev) => prev.filter((o) => o.value !== value));
10734
+ optionManagement.onDelete(value);
10735
+ }
10736
+ };
10737
+ }, [optionManagement, options, setSelected]);
10738
+ const hasPanelContent = filtered.length > 0 || showCreate;
10739
+ const flatOptionValues = React71.useMemo(() => {
10740
+ const v = [];
10741
+ if (showCreate) v.push(CREATE_VALUE);
10742
+ for (const o of filtered) {
10743
+ if (!o.disabled) v.push(o.value);
10744
+ }
10745
+ return v;
10746
+ }, [filtered, showCreate]);
10747
+ React71.useLayoutEffect(() => {
10748
+ if (!open) return;
10749
+ updateRef.current();
10750
+ const raf = requestAnimationFrame(() => updateRef.current());
10751
+ return () => cancelAnimationFrame(raf);
10752
+ }, [open]);
10753
+ React71.useEffect(() => {
10754
+ if (!open) return;
10755
+ let rafCoalesce = 0;
10756
+ const schedule = () => {
10757
+ cancelAnimationFrame(rafCoalesce);
10758
+ rafCoalesce = requestAnimationFrame(() => updateRef.current());
10759
+ };
10760
+ window.addEventListener("resize", schedule);
10761
+ const scrollTargets = getScrollContainers(triggerRef.current);
10762
+ for (const t of scrollTargets) {
10763
+ t.addEventListener("scroll", schedule, { passive: true });
10764
+ }
10765
+ const vv = window.visualViewport;
10766
+ vv?.addEventListener("resize", schedule);
10767
+ const panel = listboxRef.current;
10768
+ let ro = null;
10769
+ if (typeof ResizeObserver !== "undefined" && panel) {
10770
+ ro = new ResizeObserver(schedule);
10771
+ ro.observe(panel);
10772
+ }
10773
+ return () => {
10774
+ cancelAnimationFrame(rafCoalesce);
10775
+ window.removeEventListener("resize", schedule);
10776
+ for (const t of scrollTargets) {
10777
+ t.removeEventListener("scroll", schedule);
10778
+ }
10779
+ vv?.removeEventListener("resize", schedule);
10780
+ ro?.disconnect();
10781
+ };
10782
+ }, [open]);
10783
+ React71.useEffect(() => {
10784
+ if (!open) {
10785
+ setHighlightedValue(void 0);
10786
+ return;
10787
+ }
10788
+ if (flatOptionValues.length === 0) {
10789
+ setHighlightedValue(void 0);
10790
+ return;
10791
+ }
10792
+ setHighlightedValue(
10793
+ (prev) => prev && flatOptionValues.includes(prev) ? prev : flatOptionValues[0]
10794
+ );
10795
+ }, [open, flatOptionValues]);
10796
+ React71.useEffect(() => {
10797
+ if (!open) return;
10798
+ if (!hasPanelContent) setOpen(false);
10799
+ }, [open, hasPanelContent]);
10800
+ React71.useEffect(() => {
10801
+ if (!open) setManageOpenValue(null);
10802
+ }, [open]);
10803
+ useEscapeKey({ enabled: open && manageOpenValue === null, onEscape: () => setOpen(false) });
10804
+ useOutsideClick({
10805
+ refs: [triggerRef, listboxRef],
10806
+ enabled: open,
10807
+ onOutsideClick: () => setOpen(false),
10808
+ shouldSuppressOutsideClick: (target) => {
10809
+ if (!(target instanceof Element)) return false;
10810
+ const dialog = target.closest('[role="dialog"][data-react-aria-top-layer="true"]');
10811
+ if (!dialog) return false;
10812
+ return dialog !== listboxRef.current;
10813
+ }
10814
+ });
10815
+ const toggleValue = React71.useCallback(
10816
+ (value) => {
10817
+ setSelected((prev) => {
10818
+ if (prev.includes(value)) {
10819
+ return prev.filter((x) => x !== value);
10820
+ }
10821
+ return [...prev, value];
10822
+ });
10823
+ },
10824
+ [setSelected]
10825
+ );
10826
+ const handleSelectFromList = React71.useCallback(
10827
+ (rawValue) => {
10828
+ if (rawValue === CREATE_VALUE) {
10829
+ const v = inputTrim;
10830
+ if (v.length === 0) return;
10831
+ setSelected((prev) => {
10832
+ if (prev.includes(v)) return prev;
10833
+ onCreated?.(v);
10834
+ return [...prev, v];
10835
+ });
10836
+ setCreatedOptions((prev) => {
10837
+ if (prev.some((o) => o.value === v) || options.some((o) => o.value === v)) {
10838
+ return prev;
10839
+ }
10840
+ return [...prev, { value: v, label: v, color: defaultTagColor }];
10841
+ });
10842
+ setInputValue("");
10843
+ return;
10844
+ }
10845
+ toggleValue(rawValue);
10846
+ setInputValue("");
10847
+ },
10848
+ [defaultTagColor, inputTrim, onCreated, options, setSelected, toggleValue]
10849
+ );
10850
+ const getItems = React71.useCallback(() => queryEnabledSelectOptions(listboxRef.current), []);
10851
+ const onListboxKeyDown = (e) => {
10852
+ handleSelectListboxKeyDown(e, {
10853
+ items: getItems(),
10854
+ highlightedValue,
10855
+ setHighlightedValue,
10856
+ onSelect: (v) => {
10857
+ handleSelectFromList(v);
10858
+ },
10859
+ onClose: () => setOpen(false)
10860
+ });
10861
+ };
10862
+ const onInputKeyDown = (e) => {
10863
+ if (disabled) return;
10864
+ if (e.key === "Backspace" && inputValue.length === 0 && selected.length > 0) {
10865
+ e.preventDefault();
10866
+ setSelected((prev) => prev.slice(0, -1));
10867
+ return;
10868
+ }
10869
+ if (e.key === "Escape") {
10870
+ if (open) {
10871
+ e.preventDefault();
10872
+ setOpen(false);
10873
+ }
10874
+ return;
10875
+ }
10876
+ if (e.key === "ArrowDown" || e.key === "ArrowUp" || e.key === "Enter" || e.key === " ") {
10877
+ if (!open) {
10878
+ if (e.key === "ArrowDown" || e.key === "ArrowUp") {
10879
+ e.preventDefault();
10880
+ if (filtered.length > 0 || showCreate) setOpen(true);
10881
+ }
10882
+ return;
10883
+ }
10884
+ if (flatOptionValues.length === 0) return;
10885
+ if (e.key === "Enter" || e.key === " ") {
10886
+ e.preventDefault();
10887
+ const hv = highlightedValue ?? flatOptionValues[0];
10888
+ if (hv === CREATE_VALUE) {
10889
+ handleSelectFromList(CREATE_VALUE);
10890
+ } else if (hv) {
10891
+ const o = mergedOptions.find((x) => x.value === hv);
10892
+ if (o && !o.disabled) {
10893
+ handleSelectFromList(o.value);
10894
+ }
10895
+ }
10896
+ return;
10897
+ }
10898
+ handleSelectListboxKeyDown(e, {
10899
+ items: getItems(),
10900
+ highlightedValue,
10901
+ setHighlightedValue,
10902
+ onSelect: (v) => handleSelectFromList(v),
10903
+ onClose: () => setOpen(false)
10904
+ });
10905
+ }
10906
+ };
10907
+ const chips = normalizeList(selected, mergedOptions, defaultTagColor);
10908
+ const isEmpty = selected.length === 0 && inputTrim.length === 0;
10909
+ const inputCollapsed = !inputFocused && inputTrim.length === 0 && selected.length > 0;
10910
+ const focusInput = () => {
10911
+ inputRef.current?.focus();
10912
+ };
10913
+ const handleInputBlur = React71.useCallback((e) => {
10914
+ const next = e.relatedTarget;
10915
+ if (next instanceof Node && triggerRef.current?.contains(next)) {
10916
+ return;
10917
+ }
10918
+ window.requestAnimationFrame(() => {
10919
+ if (triggerRef.current?.contains(document.activeElement)) {
10920
+ return;
10921
+ }
10922
+ setInputFocused(false);
10923
+ setInputValue("");
10924
+ });
10925
+ }, []);
10926
+ return /* @__PURE__ */ jsxs31(ControlSizeProvider, { value: size, children: [
10927
+ /* @__PURE__ */ jsxs31(
10928
+ "div",
10929
+ {
10930
+ ref: triggerRef,
10931
+ className: cx(TagSelect_default.control, className),
10932
+ onClick: () => {
10933
+ if (!disabled) {
10934
+ focusInput();
10935
+ if (hasPanelContent) setOpen(true);
10936
+ }
10937
+ },
10938
+ ...toDataAttributes({
10939
+ size,
10940
+ open,
10941
+ empty: isEmpty ? true : void 0,
10942
+ disabled: disabled ? true : void 0,
10943
+ "has-error": hasError ? true : void 0
10944
+ }),
10945
+ children: [
10946
+ /* @__PURE__ */ jsxs31("div", { className: TagSelect_default.chips, children: [
10947
+ chips.map((c) => /* @__PURE__ */ jsx57("span", { className: TagSelect_default.chip, children: /* @__PURE__ */ jsxs31(Badge.Root, { color: c.color, variant: "filled", className: TagSelect_default.chipBadge, children: [
10948
+ /* @__PURE__ */ jsx57("span", { className: TagSelect_default.chipLabel, children: c.label }),
10949
+ /* @__PURE__ */ jsx57(
10950
+ "button",
10951
+ {
10952
+ type: "button",
10953
+ className: TagSelect_default.chipRemove,
10954
+ "aria-label": `Remove ${c.label}`,
10955
+ disabled,
10956
+ onMouseDown: (e) => {
10957
+ e.preventDefault();
10958
+ },
10959
+ onClick: (e) => {
10960
+ e.stopPropagation();
10961
+ setSelected((prev) => prev.filter((x) => x !== c.value));
10962
+ },
10963
+ children: /* @__PURE__ */ jsx57(
10964
+ "svg",
10965
+ {
10966
+ className: TagSelect_default.removeIcon,
10967
+ viewBox: "0 0 12 12",
10968
+ fill: "none",
10969
+ "aria-hidden": "true",
10970
+ children: /* @__PURE__ */ jsx57(
10971
+ "path",
10972
+ {
10973
+ d: "M2 2l8 8M10 2l-8 8",
10974
+ stroke: "currentColor",
10975
+ strokeWidth: "1.5",
10976
+ strokeLinecap: "round"
10977
+ }
10978
+ )
10979
+ }
10980
+ )
10981
+ }
10982
+ )
10983
+ ] }) }, c.value)),
10984
+ /* @__PURE__ */ jsx57(
10985
+ "input",
10986
+ {
10987
+ ref: inputRef,
10988
+ id: inputId,
10989
+ type: "text",
10990
+ role: "combobox",
10991
+ "aria-expanded": open,
10992
+ "aria-controls": listboxId,
10993
+ "aria-autocomplete": "list",
10994
+ "aria-label": ariaLabel,
10995
+ "aria-labelledby": ariaLabelledBy,
10996
+ disabled,
10997
+ placeholder: selected.length === 0 ? placeholder : void 0,
10998
+ className: cx(TagSelect_default.input, inputCollapsed && TagSelect_default.inputCollapsed),
10999
+ value: inputValue,
11000
+ onChange: (e) => {
11001
+ const next = e.target.value;
11002
+ setInputValue(next);
11003
+ const nextTrim = next.trim();
11004
+ const nextMerged = mergeOptionsWithCreated(options, createdOptions);
11005
+ const nextFiltered = optionsForList(nextMerged, next, selected);
11006
+ const nextShowCreate = shouldShowCreate(creatable, nextTrim, selected, nextMerged);
11007
+ if (nextFiltered.length > 0 || nextShowCreate) {
11008
+ setOpen(true);
11009
+ } else {
11010
+ setOpen(false);
11011
+ }
11012
+ },
11013
+ onKeyDown: onInputKeyDown,
11014
+ onFocus: () => {
11015
+ setInputFocused(true);
11016
+ if (!disabled && hasPanelContent) setOpen(true);
11017
+ },
11018
+ onBlur: handleInputBlur
11019
+ }
11020
+ )
11021
+ ] }),
11022
+ /* @__PURE__ */ jsx57("span", { className: TagSelect_default.chevronSlot, "aria-hidden": true, children: /* @__PURE__ */ jsx57("span", { className: TagSelect_default.chevron }) })
11023
+ ]
11024
+ }
11025
+ ),
11026
+ /* @__PURE__ */ jsx57(Portal, { children: /* @__PURE__ */ jsxs31(
11027
+ ScrollContainer,
11028
+ {
11029
+ ref: listboxRef,
11030
+ id: listboxId,
11031
+ role: "listbox",
11032
+ "aria-multiselectable": "true",
11033
+ "aria-hidden": !open,
11034
+ tabIndex: -1,
11035
+ "data-react-aria-top-layer": "true",
11036
+ "data-overlay-portal-layer": overlayPortalLayer,
11037
+ className: cx(Select_default.content, TagSelect_default.panelInner),
11038
+ onKeyDown: onListboxKeyDown,
11039
+ style: { display: open ? void 0 : "none" },
11040
+ ...toDataAttributes({ side: resolvedSide, size }),
11041
+ children: [
11042
+ hint ? /* @__PURE__ */ jsx57("div", { className: TagSelect_default.hint, children: /* @__PURE__ */ jsx57(Typography.Root, { as: "div", variant: "body-small", tone: "muted", children: hint }) }) : null,
11043
+ showCreate ? /* @__PURE__ */ jsxs31(
11044
+ "button",
11045
+ {
11046
+ type: "button",
11047
+ role: "option",
11048
+ "aria-selected": false,
11049
+ className: TagSelect_default.createRow,
11050
+ ...toDataAttributes({
11051
+ value: CREATE_VALUE,
11052
+ label: inputTrim,
11053
+ highlighted: highlightedValue === CREATE_VALUE,
11054
+ disabled: false
11055
+ }),
11056
+ onMouseDown: (e) => {
11057
+ e.preventDefault();
11058
+ },
11059
+ onMouseEnter: () => setHighlightedValue(CREATE_VALUE),
11060
+ onClick: () => handleSelectFromList(CREATE_VALUE),
11061
+ children: [
11062
+ /* @__PURE__ */ jsx57(
11063
+ Typography.Root,
11064
+ {
11065
+ as: "span",
11066
+ variant: "body-small",
11067
+ tone: "muted",
11068
+ className: TagSelect_default.createLabel,
11069
+ children: createActionLabel
11070
+ }
11071
+ ),
11072
+ /* @__PURE__ */ jsx57(Badge.Root, { color: defaultTagColor, variant: "filled", children: inputTrim })
11073
+ ]
11074
+ },
11075
+ CREATE_VALUE
11076
+ ) : null,
11077
+ resolvedOptionManagement ? filtered.map((o) => /* @__PURE__ */ jsxs31(
11078
+ "div",
11079
+ {
11080
+ role: "option",
11081
+ tabIndex: -1,
11082
+ "aria-selected": false,
11083
+ className: TagSelect_default.optionRowWrap,
11084
+ ...toDataAttributes({
11085
+ value: o.value,
11086
+ label: o.label,
11087
+ highlighted: highlightedValue === o.value,
11088
+ selected: false,
11089
+ disabled: Boolean(o.disabled)
11090
+ }),
11091
+ onMouseEnter: () => !o.disabled && setHighlightedValue(o.value),
11092
+ children: [
11093
+ /* @__PURE__ */ jsx57(
11094
+ "button",
11095
+ {
11096
+ type: "button",
11097
+ className: TagSelect_default.optionRowSelect,
11098
+ disabled: o.disabled,
11099
+ onMouseDown: (e) => {
11100
+ if (!o.disabled) e.preventDefault();
11101
+ },
11102
+ onClick: () => !o.disabled && handleSelectFromList(o.value),
11103
+ children: /* @__PURE__ */ jsx57(Badge.Root, { color: o.color ?? defaultTagColor, variant: "filled", children: o.label })
11104
+ }
11105
+ ),
11106
+ !o.disabled ? /* @__PURE__ */ jsx57(
11107
+ TagOptionManagePopover,
11108
+ {
11109
+ option: o,
11110
+ open: manageOpenValue === o.value,
11111
+ onOpenChange: (next) => setManageOpenValue(next ? o.value : null),
11112
+ defaultTagColor,
11113
+ management: resolvedOptionManagement,
11114
+ disabled
11115
+ }
11116
+ ) : null
11117
+ ]
11118
+ },
11119
+ o.value
11120
+ )) : filtered.map((o) => /* @__PURE__ */ jsx57(
11121
+ "button",
11122
+ {
11123
+ type: "button",
11124
+ role: "option",
11125
+ "aria-selected": false,
11126
+ disabled: o.disabled,
11127
+ className: TagSelect_default.optionRow,
11128
+ ...toDataAttributes({
11129
+ value: o.value,
11130
+ label: o.label,
11131
+ highlighted: highlightedValue === o.value,
11132
+ selected: false,
11133
+ disabled: Boolean(o.disabled)
11134
+ }),
11135
+ onMouseDown: (e) => {
11136
+ if (!o.disabled) e.preventDefault();
11137
+ },
11138
+ onMouseEnter: () => !o.disabled && setHighlightedValue(o.value),
11139
+ onClick: () => !o.disabled && handleSelectFromList(o.value),
11140
+ children: /* @__PURE__ */ jsx57(Badge.Root, { color: o.color ?? defaultTagColor, variant: "filled", children: o.label })
11141
+ },
11142
+ o.value
11143
+ ))
11144
+ ]
11145
+ }
11146
+ ) })
11147
+ ] });
11148
+ }
11149
+ TagSelectRoot.displayName = "TagSelectRoot";
11150
+ var TagSelect = {
11151
+ Root: TagSelectRoot
11152
+ };
11153
+
11154
+ // src/components/textarea/Textarea.tsx
11155
+ import * as React72 from "react";
11156
+
10152
11157
  // src/components/textarea/Textarea.module.css
10153
11158
  var Textarea_default = {
10154
11159
  field: "Textarea_field2",
@@ -10164,11 +11169,11 @@ var Textarea_default = {
10164
11169
  };
10165
11170
 
10166
11171
  // src/components/textarea/Textarea.tsx
10167
- import { jsx as jsx57, jsxs as jsxs31 } from "react/jsx-runtime";
11172
+ import { jsx as jsx58, jsxs as jsxs32 } from "react/jsx-runtime";
10168
11173
  var [TextareaProvider, useTextareaContext] = createComponentContext("Textarea");
10169
11174
  function TextareaCharCounter({ current, max }) {
10170
11175
  const overflow = current > max;
10171
- return /* @__PURE__ */ jsxs31(
11176
+ return /* @__PURE__ */ jsxs32(
10172
11177
  "span",
10173
11178
  {
10174
11179
  className: Textarea_default.charCounter,
@@ -10186,8 +11191,8 @@ TextareaCharCounter.displayName = "Textarea.CharCounter";
10186
11191
  function partitionTextareaChildren(children) {
10187
11192
  const counters = [];
10188
11193
  const rest = [];
10189
- React71.Children.forEach(children, (child) => {
10190
- if (React71.isValidElement(child) && child.type === TextareaCharCounter) {
11194
+ React72.Children.forEach(children, (child) => {
11195
+ if (React72.isValidElement(child) && child.type === TextareaCharCounter) {
10191
11196
  counters.push(child);
10192
11197
  } else if (child != null && child !== false) {
10193
11198
  rest.push(child);
@@ -10195,7 +11200,7 @@ function partitionTextareaChildren(children) {
10195
11200
  });
10196
11201
  return { counters, rest };
10197
11202
  }
10198
- var TextareaRoot = React71.forwardRef(
11203
+ var TextareaRoot = React72.forwardRef(
10199
11204
  ({
10200
11205
  id,
10201
11206
  className,
@@ -10212,12 +11217,12 @@ var TextareaRoot = React71.forwardRef(
10212
11217
  children,
10213
11218
  ...rest
10214
11219
  }, ref) => {
10215
- const rawId = React71.useId();
11220
+ const rawId = React72.useId();
10216
11221
  const inputId = id ?? rawId;
10217
11222
  const hintId = `${inputId}-hint`;
10218
11223
  const errorId = `${inputId}-error`;
10219
- const [hasHint, setHasHint] = React71.useState(false);
10220
- const [hasError, setHasError] = React71.useState(false);
11224
+ const [hasHint, setHasHint] = React72.useState(false);
11225
+ const [hasError, setHasError] = React72.useState(false);
10221
11226
  const invalid = variant === "error" || hasError;
10222
11227
  const resolvedAriaInvalid = ariaInvalid ?? (invalid || void 0);
10223
11228
  const parts = [
@@ -10226,25 +11231,25 @@ var TextareaRoot = React71.forwardRef(
10226
11231
  hasError ? errorId : void 0
10227
11232
  ].filter(Boolean);
10228
11233
  const describedBy = parts.length > 0 ? parts.join(" ") : void 0;
10229
- const registerHint = React71.useCallback(() => setHasHint(true), []);
10230
- const unregisterHint = React71.useCallback(() => setHasHint(false), []);
10231
- const registerError = React71.useCallback(() => setHasError(true), []);
10232
- const unregisterError = React71.useCallback(() => setHasError(false), []);
10233
- const wrapperRef = React71.useRef(null);
11234
+ const registerHint = React72.useCallback(() => setHasHint(true), []);
11235
+ const unregisterHint = React72.useCallback(() => setHasHint(false), []);
11236
+ const registerError = React72.useCallback(() => setHasError(true), []);
11237
+ const unregisterError = React72.useCallback(() => setHasError(false), []);
11238
+ const wrapperRef = React72.useRef(null);
10234
11239
  const { counters: counterChildren, rest: otherChildren } = partitionTextareaChildren(children);
10235
11240
  const showFooter = counterChildren.length > 0;
10236
- React71.useLayoutEffect(() => {
11241
+ React72.useLayoutEffect(() => {
10237
11242
  if (!autoResize || !wrapperRef.current) return;
10238
11243
  const textarea = wrapperRef.current.querySelector("textarea");
10239
11244
  if (textarea) {
10240
11245
  wrapperRef.current.dataset.value = textarea.value;
10241
11246
  }
10242
11247
  }, [autoResize]);
10243
- React71.useEffect(() => {
11248
+ React72.useEffect(() => {
10244
11249
  if (!autoResize || !wrapperRef.current || typeof value !== "string") return;
10245
11250
  wrapperRef.current.dataset.value = value;
10246
11251
  }, [autoResize, value]);
10247
- const handleInput = React71.useCallback(
11252
+ const handleInput = React72.useCallback(
10248
11253
  (e) => {
10249
11254
  if (autoResize && wrapperRef.current) {
10250
11255
  wrapperRef.current.dataset.value = e.currentTarget.value;
@@ -10253,7 +11258,7 @@ var TextareaRoot = React71.forwardRef(
10253
11258
  },
10254
11259
  [autoResize, onInput]
10255
11260
  );
10256
- const ctxValue = React71.useMemo(
11261
+ const ctxValue = React72.useMemo(
10257
11262
  () => ({
10258
11263
  hintId,
10259
11264
  errorId,
@@ -10277,7 +11282,7 @@ var TextareaRoot = React71.forwardRef(
10277
11282
  unregisterError
10278
11283
  ]
10279
11284
  );
10280
- const textareaEl = /* @__PURE__ */ jsx57(
11285
+ const textareaEl = /* @__PURE__ */ jsx58(
10281
11286
  "textarea",
10282
11287
  {
10283
11288
  ref,
@@ -10293,9 +11298,9 @@ var TextareaRoot = React71.forwardRef(
10293
11298
  ...rest
10294
11299
  }
10295
11300
  );
10296
- const textareaBlock = autoResize ? /* @__PURE__ */ jsx57("div", { ref: wrapperRef, className: Textarea_default.autoResize, children: textareaEl }) : textareaEl;
10297
- return /* @__PURE__ */ jsx57(TextareaProvider, { value: ctxValue, children: /* @__PURE__ */ jsx57(ControlSizeProvider, { value: size, children: /* @__PURE__ */ jsxs31("div", { className: Textarea_default.field, ...toDataAttributes({ size }), children: [
10298
- /* @__PURE__ */ jsx57(
11301
+ const textareaBlock = autoResize ? /* @__PURE__ */ jsx58("div", { ref: wrapperRef, className: Textarea_default.autoResize, children: textareaEl }) : textareaEl;
11302
+ return /* @__PURE__ */ jsx58(TextareaProvider, { value: ctxValue, children: /* @__PURE__ */ jsx58(ControlSizeProvider, { value: size, children: /* @__PURE__ */ jsxs32("div", { className: Textarea_default.field, ...toDataAttributes({ size }), children: [
11303
+ /* @__PURE__ */ jsx58(
10299
11304
  "label",
10300
11305
  {
10301
11306
  htmlFor: inputId,
@@ -10306,9 +11311,9 @@ var TextareaRoot = React71.forwardRef(
10306
11311
  readonly: Boolean(readOnly),
10307
11312
  size
10308
11313
  }),
10309
- children: /* @__PURE__ */ jsxs31("div", { className: Textarea_default.controlStack, children: [
10310
- /* @__PURE__ */ jsx57("div", { className: Textarea_default.textareaRegion, children: textareaBlock }),
10311
- showFooter ? /* @__PURE__ */ jsx57("div", { className: Textarea_default.controlFooter, children: counterChildren }) : null
11314
+ children: /* @__PURE__ */ jsxs32("div", { className: Textarea_default.controlStack, children: [
11315
+ /* @__PURE__ */ jsx58("div", { className: Textarea_default.textareaRegion, children: textareaBlock }),
11316
+ showFooter ? /* @__PURE__ */ jsx58("div", { className: Textarea_default.controlFooter, children: counterChildren }) : null
10312
11317
  ] })
10313
11318
  }
10314
11319
  ),
@@ -10319,13 +11324,13 @@ var TextareaRoot = React71.forwardRef(
10319
11324
  TextareaRoot.displayName = "Textarea.Root";
10320
11325
  function TextareaHint({ children, className, ...rest }) {
10321
11326
  const { hintId, registerHint, unregisterHint, size, disabled, readOnly } = useTextareaContext();
10322
- React71.useLayoutEffect(() => {
11327
+ React72.useLayoutEffect(() => {
10323
11328
  registerHint();
10324
11329
  return () => {
10325
11330
  unregisterHint();
10326
11331
  };
10327
11332
  }, [registerHint, unregisterHint]);
10328
- return /* @__PURE__ */ jsx57(
11333
+ return /* @__PURE__ */ jsx58(
10329
11334
  Hint.Root,
10330
11335
  {
10331
11336
  id: hintId,
@@ -10340,13 +11345,13 @@ function TextareaHint({ children, className, ...rest }) {
10340
11345
  TextareaHint.displayName = "Textarea.Hint";
10341
11346
  function TextareaError({ children, className, ...rest }) {
10342
11347
  const { errorId, registerError, unregisterError, size } = useTextareaContext();
10343
- React71.useLayoutEffect(() => {
11348
+ React72.useLayoutEffect(() => {
10344
11349
  registerError();
10345
11350
  return () => {
10346
11351
  unregisterError();
10347
11352
  };
10348
11353
  }, [registerError, unregisterError]);
10349
- return /* @__PURE__ */ jsx57(
11354
+ return /* @__PURE__ */ jsx58(
10350
11355
  Hint.Root,
10351
11356
  {
10352
11357
  id: errorId,
@@ -10419,6 +11424,7 @@ export {
10419
11424
  Switch,
10420
11425
  Tabs,
10421
11426
  Tag,
11427
+ TagSelect,
10422
11428
  Textarea,
10423
11429
  Tooltip,
10424
11430
  Typography,