prime-ui-kit 0.7.7 → 0.7.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -9130,6 +9130,7 @@ function PopoverContent({
9130
9130
  trapFocus = false,
9131
9131
  insetPadding = "none",
9132
9132
  insetGap = "pad",
9133
+ stackAboveDropdown = false,
9133
9134
  children,
9134
9135
  className
9135
9136
  }) {
@@ -9167,6 +9168,7 @@ function PopoverContent({
9167
9168
  "aria-labelledby": triggerId,
9168
9169
  "data-react-aria-top-layer": "true",
9169
9170
  "data-overlay-portal-layer": overlayPortalLayer,
9171
+ "data-overlay-stack": stackAboveDropdown ? "above-dropdown" : void 0,
9170
9172
  "data-side": layout?.resolvedSide ?? side,
9171
9173
  "data-size": size,
9172
9174
  "data-inset-padding": insetPadding,
@@ -10405,12 +10407,40 @@ var TagSelect_default = {
10405
10407
  hint: "TagSelect_hint",
10406
10408
  optionRow: "TagSelect_optionRow",
10407
10409
  createRow: "TagSelect_createRow",
10408
- createLabel: "TagSelect_createLabel"
10410
+ createLabel: "TagSelect_createLabel",
10411
+ optionRowWrap: "TagSelect_optionRowWrap",
10412
+ optionRowSelect: "TagSelect_optionRowSelect",
10413
+ optionMenuTrigger: "TagSelect_optionMenuTrigger",
10414
+ optionMenuDots: "TagSelect_optionMenuDots",
10415
+ managePopoverSurface: "TagSelect_managePopoverSurface",
10416
+ managePopoverShell: "TagSelect_managePopoverShell",
10417
+ manageDelete: "TagSelect_manageDelete",
10418
+ manageDeleteIcon: "TagSelect_manageDeleteIcon",
10419
+ manageSeparator: "TagSelect_manageSeparator",
10420
+ manageColorsHeading: "TagSelect_manageColorsHeading",
10421
+ manageColorList: "TagSelect_manageColorList",
10422
+ manageColorRow: "TagSelect_manageColorRow",
10423
+ manageColorSwatch: "TagSelect_manageColorSwatch",
10424
+ manageColorLabel: "TagSelect_manageColorLabel",
10425
+ manageCheck: "TagSelect_manageCheck",
10426
+ manageCheckPlaceholder: "TagSelect_manageCheckPlaceholder"
10409
10427
  };
10410
10428
 
10411
10429
  // src/components/tag-select/TagSelect.tsx
10412
- import { jsx as jsx57, jsxs as jsxs31 } from "react/jsx-runtime";
10430
+ import { Fragment as Fragment6, jsx as jsx57, jsxs as jsxs31 } from "react/jsx-runtime";
10413
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
+ ];
10414
10444
  function normalizeList(selected, options, defaultTagColor) {
10415
10445
  return selected.map((v) => {
10416
10446
  const o = options.find((x) => x.value === v);
@@ -10441,6 +10471,183 @@ function shouldShowCreate(creatable, inputTrim, selected, options) {
10441
10471
  );
10442
10472
  return !exists;
10443
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
+ }
10444
10651
  function TagSelectRoot({
10445
10652
  options,
10446
10653
  value: valueProp,
@@ -10455,6 +10662,7 @@ function TagSelectRoot({
10455
10662
  placeholder = "",
10456
10663
  hasError = false,
10457
10664
  size = "m",
10665
+ optionManagement,
10458
10666
  id: idProp,
10459
10667
  className,
10460
10668
  "aria-label": ariaLabel,
@@ -10473,6 +10681,8 @@ function TagSelectRoot({
10473
10681
  const [inputFocused, setInputFocused] = React71.useState(false);
10474
10682
  const [open, setOpen] = React71.useState(false);
10475
10683
  const [highlightedValue, setHighlightedValue] = React71.useState(void 0);
10684
+ const [manageOpenValue, setManageOpenValue] = React71.useState(null);
10685
+ const [createdOptions, setCreatedOptions] = React71.useState([]);
10476
10686
  const triggerRef = React71.useRef(null);
10477
10687
  const inputRef = React71.useRef(null);
10478
10688
  const listboxRef = React71.useRef(null);
@@ -10484,11 +10694,47 @@ function TagSelectRoot({
10484
10694
  const updateRef = React71.useRef(update);
10485
10695
  updateRef.current = update;
10486
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]);
10487
10707
  const filtered = React71.useMemo(
10488
- () => optionsForList(options, inputValue, selected),
10489
- [options, inputValue, selected]
10708
+ () => optionsForList(mergedOptions, inputValue, selected),
10709
+ [mergedOptions, inputValue, selected]
10490
10710
  );
10491
- const showCreate = shouldShowCreate(creatable, inputTrim, selected, options);
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]);
10492
10738
  const hasPanelContent = filtered.length > 0 || showCreate;
10493
10739
  const flatOptionValues = React71.useMemo(() => {
10494
10740
  const v = [];
@@ -10551,11 +10797,20 @@ function TagSelectRoot({
10551
10797
  if (!open) return;
10552
10798
  if (!hasPanelContent) setOpen(false);
10553
10799
  }, [open, hasPanelContent]);
10554
- useEscapeKey({ enabled: open, onEscape: () => setOpen(false) });
10800
+ React71.useEffect(() => {
10801
+ if (!open) setManageOpenValue(null);
10802
+ }, [open]);
10803
+ useEscapeKey({ enabled: open && manageOpenValue === null, onEscape: () => setOpen(false) });
10555
10804
  useOutsideClick({
10556
10805
  refs: [triggerRef, listboxRef],
10557
10806
  enabled: open,
10558
- onOutsideClick: () => setOpen(false)
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
+ }
10559
10814
  });
10560
10815
  const toggleValue = React71.useCallback(
10561
10816
  (value) => {
@@ -10578,13 +10833,19 @@ function TagSelectRoot({
10578
10833
  onCreated?.(v);
10579
10834
  return [...prev, v];
10580
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
+ });
10581
10842
  setInputValue("");
10582
10843
  return;
10583
10844
  }
10584
10845
  toggleValue(rawValue);
10585
10846
  setInputValue("");
10586
10847
  },
10587
- [inputTrim, onCreated, setSelected, toggleValue]
10848
+ [defaultTagColor, inputTrim, onCreated, options, setSelected, toggleValue]
10588
10849
  );
10589
10850
  const getItems = React71.useCallback(() => queryEnabledSelectOptions(listboxRef.current), []);
10590
10851
  const onListboxKeyDown = (e) => {
@@ -10627,7 +10888,7 @@ function TagSelectRoot({
10627
10888
  if (hv === CREATE_VALUE) {
10628
10889
  handleSelectFromList(CREATE_VALUE);
10629
10890
  } else if (hv) {
10630
- const o = options.find((x) => x.value === hv);
10891
+ const o = mergedOptions.find((x) => x.value === hv);
10631
10892
  if (o && !o.disabled) {
10632
10893
  handleSelectFromList(o.value);
10633
10894
  }
@@ -10643,7 +10904,7 @@ function TagSelectRoot({
10643
10904
  });
10644
10905
  }
10645
10906
  };
10646
- const chips = normalizeList(selected, options, defaultTagColor);
10907
+ const chips = normalizeList(selected, mergedOptions, defaultTagColor);
10647
10908
  const isEmpty = selected.length === 0 && inputTrim.length === 0;
10648
10909
  const inputCollapsed = !inputFocused && inputTrim.length === 0 && selected.length > 0;
10649
10910
  const focusInput = () => {
@@ -10740,8 +11001,9 @@ function TagSelectRoot({
10740
11001
  const next = e.target.value;
10741
11002
  setInputValue(next);
10742
11003
  const nextTrim = next.trim();
10743
- const nextFiltered = optionsForList(options, next, selected);
10744
- const nextShowCreate = shouldShowCreate(creatable, nextTrim, selected, options);
11004
+ const nextMerged = mergeOptionsWithCreated(options, createdOptions);
11005
+ const nextFiltered = optionsForList(nextMerged, next, selected);
11006
+ const nextShowCreate = shouldShowCreate(creatable, nextTrim, selected, nextMerged);
10745
11007
  if (nextFiltered.length > 0 || nextShowCreate) {
10746
11008
  setOpen(true);
10747
11009
  } else {
@@ -10812,7 +11074,50 @@ function TagSelectRoot({
10812
11074
  },
10813
11075
  CREATE_VALUE
10814
11076
  ) : null,
10815
- filtered.map((o) => /* @__PURE__ */ jsx57(
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(
10816
11121
  "button",
10817
11122
  {
10818
11123
  type: "button",