@bonprix-ds/react-select 1.0.9 → 1.0.11

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.
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,7 @@
1
+ /* Radix prepends focus guards to `body` while a dialog is open. Chromium saves the scroll anchor of a page that is
2
+ left as an `nth-child` path, which the guards shift, so going back would restore the scroll position of another
3
+ element. Without an anchor, Chromium restores the plain scroll offset. Keyed to the guards rather than to the
4
+ open state, so it holds through the closing animation and across every copy of the package on the page. */
5
+ :root:has(> body > [data-radix-focus-guard]) {
6
+ overflow-anchor: none;
7
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"Select.js","names":[],"sources":["../../src/Select.tsx"],"sourcesContent":["import React from \"react\";\n\nimport SelectItem from \"./SelectItem\";\nimport SelectRoot, { type Props } from \"./SelectRoot\";\n\nexport type { Props };\n\nconst Select = (props: Props) => <SelectRoot {...props} />;\n\nSelect.displayName = \"Select\";\n\nSelect.Item = SelectItem;\n\nexport default Select;\n"],"mappings":";;;;;AAOA,IAAM,KAAU,MAAiB,kBAAC,GAAD,EAAY,GAAI,EAAQ,CAAA;AAEzD,EAAO,cAAc,UAErB,EAAO,OAAO"}
1
+ {"version":3,"file":"Select.js","names":["SelectItem","SelectRoot","Select","props"],"sources":["../../src/Select.tsx"],"sourcesContent":["import React from \"react\";\n\nimport SelectItem from \"./SelectItem\";\nimport SelectRoot, { type Props } from \"./SelectRoot\";\n\nexport type { Props };\n\nconst Select = (props: Props) => <SelectRoot {...props} />;\n\nSelect.displayName = \"Select\";\n\nSelect.Item = SelectItem;\n\nexport default Select;\n"],"mappings":";;;;;AAOA,IAAME,KAAU,MAAiB,kBAACD,GAAD,EAAY,GAAIE,EAAQ,CAAA;AAEzD,EAAO,cAAc,UAErB,EAAO,OAAOH"}
@@ -5,13 +5,13 @@ import { jsx as n } from "react/jsx-runtime";
5
5
  //#region src/SelectItem.tsx
6
6
  var r = t({
7
7
  base: [
8
- "px-1 py-2 typography-input-text outline-none select-none text-wrap",
9
- "underline-offset-[30%] hover:underline data-[highlighted]:outline-none data-[highlighted]:underline data-[disabled]:no-underline",
8
+ "typography-input-text select-none text-wrap px-1 py-2 outline-none",
9
+ "underline-offset-[30%] hover:underline data-[highlighted]:underline data-[disabled]:no-underline data-[highlighted]:outline-none",
10
10
  "data-[state=checked]:underline data-[state=checked]:decoration-[15%]",
11
- "data-[state=checked]:hover:decoration-[25%] data-[state=checked]:data-[highlighted]:decoration-[25%]"
11
+ "data-[state=checked]:data-[highlighted]:decoration-[25%] data-[state=checked]:hover:decoration-[25%]"
12
12
  ],
13
13
  variants: { disabled: {
14
- false: "text-clr-primary cursor-pointer",
14
+ false: "cursor-pointer text-clr-primary",
15
15
  true: "text-clr-disabled"
16
16
  } }
17
17
  }), i = ({ children: t, value: i, disabled: a = !1, hidden: o = !1, ref: s }) => {
@@ -1 +1 @@
1
- {"version":3,"file":"SelectItem.js","names":[],"sources":["../../src/SelectItem.tsx"],"sourcesContent":["import * as SelectPrimitive from \"@radix-ui/react-select\";\nimport React, { ReactNode, RefObject } from \"react\";\nimport { tv } from \"tailwind-variants\";\n\nconst generateClasses = tv({\n base: [\n \"px-1 py-2 typography-input-text outline-none select-none text-wrap\",\n \"underline-offset-[30%] hover:underline data-[highlighted]:outline-none data-[highlighted]:underline data-[disabled]:no-underline\", // focus/hover\n \"data-[state=checked]:underline data-[state=checked]:decoration-[15%]\", // selected\n \"data-[state=checked]:hover:decoration-[25%] data-[state=checked]:data-[highlighted]:decoration-[25%]\", // selected + focus/hover\n ],\n variants: {\n disabled: {\n false: \"text-clr-primary cursor-pointer\",\n true: \"text-clr-disabled\",\n },\n },\n});\n\nexport interface SelectItemProps {\n /** The value of the select item. This will be used as the value when the select is submitted. */\n value: string;\n /** The content text of the select item. */\n children?: ReactNode;\n /** If `true`, the select item will be disabled and not selectable. */\n disabled?: boolean;\n /** If `true`, the select item will be hidden from the dropdown menu. */\n hidden?: boolean;\n /** A reference to the root element of the select item. */\n ref?: RefObject<HTMLDivElement>;\n}\n\nconst SelectItem = ({ children, value, disabled = false, hidden = false, ref }: SelectItemProps) => {\n const base = generateClasses({ disabled });\n return (\n <SelectPrimitive.Item value={value} disabled={disabled} hidden={hidden} ref={ref} className={base}>\n <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>\n </SelectPrimitive.Item>\n );\n};\n\nSelectItem.displayName = \"Select.Item\";\n\nexport default SelectItem;\n"],"mappings":";;;;;AAIA,IAAM,IAAkB,EAAG;CACzB,MAAM;EACJ;EACA;EACA;EACA;CACF;CACA,UAAU,EACR,UAAU;EACR,OAAO;EACP,MAAM;CACR,EACF;AACF,CAAC,GAeK,KAAc,EAAE,aAAU,UAAO,cAAW,IAAO,YAAS,IAAO,aAA2B;CAClG,IAAM,IAAO,EAAgB,EAAE,YAAS,CAAC;CACzC,OACE,kBAAC,EAAgB,MAAjB;EAA6B;EAAiB;EAAkB;EAAa;EAAK,WAAW;EAC3F,UAAA,kBAAC,EAAgB,UAAjB,EAA2B,YAAmC,CAAA;CAC1C,CAAA;AAE1B;AAEA,EAAW,cAAc"}
1
+ {"version":3,"file":"SelectItem.js","names":["SelectPrimitive","generateClasses","tv","SelectItem","base"],"sources":["../../src/SelectItem.tsx"],"sourcesContent":["import * as SelectPrimitive from \"@radix-ui/react-select\";\nimport React, { ReactNode, RefObject } from \"react\";\nimport { tv } from \"tailwind-variants\";\n\nconst generateClasses = tv({\n base: [\n \"typography-input-text select-none text-wrap px-1 py-2 outline-none\",\n \"underline-offset-[30%] hover:underline data-[highlighted]:underline data-[disabled]:no-underline data-[highlighted]:outline-none\", // focus/hover\n \"data-[state=checked]:underline data-[state=checked]:decoration-[15%]\", // selected\n \"data-[state=checked]:data-[highlighted]:decoration-[25%] data-[state=checked]:hover:decoration-[25%]\", // selected + focus/hover\n ],\n variants: {\n disabled: {\n false: \"cursor-pointer text-clr-primary\",\n true: \"text-clr-disabled\",\n },\n },\n});\n\nexport interface SelectItemProps {\n /** The value of the select item. This will be used as the value when the select is submitted. */\n value: string;\n /** The content text of the select item. */\n children?: ReactNode;\n /** If `true`, the select item will be disabled and not selectable. */\n disabled?: boolean;\n /** If `true`, the select item will be hidden from the dropdown menu. */\n hidden?: boolean;\n /** A reference to the root element of the select item. */\n ref?: RefObject<HTMLDivElement>;\n}\n\nconst SelectItem = ({ children, value, disabled = false, hidden = false, ref }: SelectItemProps) => {\n const base = generateClasses({ disabled });\n return (\n <SelectPrimitive.Item value={value} disabled={disabled} hidden={hidden} ref={ref} className={base}>\n <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>\n </SelectPrimitive.Item>\n );\n};\n\nSelectItem.displayName = \"Select.Item\";\n\nexport default SelectItem;\n"],"mappings":";;;;;AAIA,IAAMC,IAAkBC,EAAG;CACzB,MAAM;EACJ;EACA;EACA;EACA;CACF;CACA,UAAU,EACR,UAAU;EACR,OAAO;EACP,MAAM;CACR,EACF;AACF,CAAC,GAeKC,KAAc,EAAE,aAAU,UAAO,cAAW,IAAO,YAAS,IAAO,aAA2B;CAClG,IAAMC,IAAOH,EAAgB,EAAE,YAAS,CAAC;CACzC,OACE,kBAACD,EAAgB,MAAjB;EAA6B;EAAiB;EAAkB;EAAa;EAAK,WAAWI;EAC3F,UAAA,kBAACJ,EAAgB,UAAjB,EAA2B,YAAmC,CAAA;CAC1C,CAAA;AAE1B;AAEA,EAAW,cAAc"}
@@ -1,4 +1,5 @@
1
1
  import React, { RefObject } from "react";
2
+ import "./Select.css";
2
3
  interface BaseProps {
3
4
  /** The id of the input field. */
4
5
  id?: string;
@@ -1,93 +1,99 @@
1
1
  "use client";
2
- import { useId as e, useState as t } from "react";
3
- import * as n from "@radix-ui/react-select";
4
- import { tv as r } from "tailwind-variants";
5
- import { jsx as i, jsxs as a } from "react/jsx-runtime";
6
- import { InputWrapper as o } from "@bonprix-ds/internal-components";
7
- import s from "@bonprix-ds/react-icon";
8
- import { useSafeAreaInsetBottom as c, useZIndex as l } from "@bonprix-ds/utils";
2
+ import { useId as e, useRef as t, useState as n } from "react";
3
+ import * as r from "@radix-ui/react-select";
4
+ import { tv as i } from "tailwind-variants";
5
+ import { jsx as a, jsxs as o } from "react/jsx-runtime";
6
+ import { InputWrapper as s } from "@bonprix-ds/internal-components";
7
+ import c from "@bonprix-ds/react-icon";
8
+ import { useSafeAreaInsetBottom as l, useZIndex as u } from "@bonprix-ds/utils";
9
+ import "#styles";
9
10
  //#region src/SelectRoot.tsx
10
- var u = r({
11
+ var d = i({
11
12
  slots: {
12
- trigger: "flex w-full align-center items-center gap-3 p-3 outline-none",
13
- icon: "w-5 h-5 fill-inherit shrink-0 [button[data-state='open']_span>&]:rotate-180",
13
+ trigger: "align-center flex w-full items-center gap-3 p-3 outline-none",
14
+ icon: "h-5 w-5 shrink-0 fill-inherit [button[data-state='open']_span>&]:rotate-180",
14
15
  menu: [
15
- "group/menu preflight bg-clr-bg-main overflow-hidden whitespace-nowrap",
16
- "border border-clr-primary rounded data-[side=top]:rounded-b-none data-[side=bottom]:rounded-t-none",
16
+ "group/menu preflight overflow-hidden whitespace-nowrap bg-clr-bg-main",
17
+ "rounded border border-clr-primary data-[side=bottom]:rounded-t-none data-[side=top]:rounded-b-none",
17
18
  "w-[calc(var(--radix-select-trigger-width)+0.125rem)]"
18
19
  ],
19
- viewport: "max-h-[calc(283/16*1rem)] group-data-[side=top]/menu:max-h-[calc(302/16*1rem)] overscroll-none",
20
+ viewport: "max-h-[calc(283/16*1rem)] overscroll-none group-data-[side=top]/menu:max-h-[calc(302/16*1rem)]",
20
21
  list: "flex flex-col gap-1 p-2",
21
- text: ["h-[1lh] typography-input-text grow text-left truncate", "text-clr-primary"],
22
- labelClone: "hidden sticky bottom-0 bg-clr-bg-main typography-input-label pb-[0.0625rem] pl-[0.6875rem] pe-2"
22
+ text: ["typography-input-text h-[1lh] grow truncate text-left", "text-clr-primary"],
23
+ labelClone: "typography-input-label sticky bottom-0 hidden bg-clr-bg-main pb-[0.0625rem] pe-2 pl-[0.6875rem]"
23
24
  },
24
25
  variants: { labelFloated: { true: { labelClone: "group-data-[side=top]/menu:block" } } }
25
- }), d = ({ id: r, name: d, label: f, onValueChange: p, children: m, disabled: h = !1, error: g = !1, required: _ = !1, helperText: v = "", errorText: y = "", ref: b, ...x }) => {
26
- let S = e(), C = r ?? S, w = e(), T = e(), E = x.value !== void 0, D = E ? x.value : void 0, O = E ? void 0 : x.defaultValue, k = E ? !!x.value : !!O, [A, j] = t(k), { trigger: M, icon: N, menu: P, viewport: F, list: I, text: L, labelClone: R } = u({
27
- error: g,
28
- labelFloated: A
29
- }), z = l(), B;
30
- v && (B = w), g && y && (B = B ? B + " " + T : T);
31
- let V = c();
32
- return /* @__PURE__ */ i(o, {
33
- label: f,
34
- id: C,
35
- required: _,
36
- labelFloated: A,
37
- disabled: h,
38
- error: g,
39
- errorText: y,
40
- errorTextId: T,
41
- helperText: v,
42
- helperTextId: w,
26
+ }), f = ({ id: i, name: f, label: p, onValueChange: m, children: h, disabled: g = !1, error: _ = !1, required: v = !1, helperText: y = "", errorText: b = "", ref: x, ...S }) => {
27
+ let C = e(), w = i ?? C, T = e(), E = e(), D = S.value !== void 0, O = D ? S.value : void 0, k = D ? void 0 : S.defaultValue, A = D ? !!S.value : !!k, [j, M] = n(A), { trigger: N, icon: P, menu: F, viewport: I, list: L, text: R, labelClone: z } = d({
28
+ error: _,
29
+ labelFloated: j
30
+ }), B = u(), V;
31
+ y && (V = T), _ && b && (V = V ? V + " " + E : E);
32
+ let H = l(), U = t(!1);
33
+ return /* @__PURE__ */ a(s, {
34
+ label: p,
35
+ id: w,
36
+ required: v,
37
+ labelFloated: j,
38
+ disabled: g,
39
+ error: _,
40
+ errorText: b,
41
+ errorTextId: E,
42
+ helperText: y,
43
+ helperTextId: T,
43
44
  componentName: "select",
44
- children: /* @__PURE__ */ a(n.Root, {
45
- name: d,
46
- disabled: h,
47
- value: D,
45
+ children: /* @__PURE__ */ o(r.Root, {
46
+ name: f,
47
+ disabled: g,
48
+ value: O,
48
49
  onValueChange: (e) => {
49
- j(!0), p && p(e);
50
+ M(!0), m && m(e);
50
51
  },
51
- defaultValue: O,
52
- children: [/* @__PURE__ */ a(n.Trigger, {
53
- id: C,
54
- className: M(),
55
- ref: b,
56
- "aria-describedby": B,
57
- "aria-invalid": g ? "true" : void 0,
52
+ defaultValue: k,
53
+ children: [/* @__PURE__ */ o(r.Trigger, {
54
+ id: w,
55
+ className: N(),
56
+ ref: x,
57
+ "aria-describedby": V,
58
+ "aria-invalid": _ ? "true" : void 0,
58
59
  "data-bpds": "select",
59
- children: [/* @__PURE__ */ i("span", {
60
- className: L(),
61
- children: /* @__PURE__ */ i(n.Value, {})
62
- }), /* @__PURE__ */ i(n.Icon, { children: /* @__PURE__ */ i(s, {
60
+ onPointerDown: () => U.current = !1,
61
+ onClick: (e) => {
62
+ U.current && (U.current = !1, e.preventDefault());
63
+ },
64
+ children: [/* @__PURE__ */ a("span", {
65
+ className: R(),
66
+ children: /* @__PURE__ */ a(r.Value, {})
67
+ }), /* @__PURE__ */ a(r.Icon, { children: /* @__PURE__ */ a(c, {
63
68
  variant: "arrow-down",
64
- className: N()
69
+ className: P()
65
70
  }) })]
66
- }), /* @__PURE__ */ i(n.Portal, { children: /* @__PURE__ */ i(n.Content, {
71
+ }), /* @__PURE__ */ a(r.Portal, { children: /* @__PURE__ */ a(r.Content, {
67
72
  position: "popper",
68
73
  side: "bottom",
69
74
  align: "start",
70
75
  alignOffset: -1,
71
- className: P(),
72
- style: { zIndex: z },
73
- collisionPadding: V ? { bottom: V + 10 } : void 0,
74
- children: /* @__PURE__ */ a(n.Viewport, {
75
- className: F(),
76
- children: [/* @__PURE__ */ i("div", {
77
- className: I(),
78
- children: m
79
- }), /* @__PURE__ */ a("div", {
80
- className: R(),
76
+ className: F(),
77
+ style: { zIndex: B },
78
+ collisionPadding: H ? { bottom: H + 10 } : void 0,
79
+ onPointerDownOutside: (e) => U.current = e.detail.originalEvent.pointerType !== "mouse",
80
+ children: /* @__PURE__ */ o(r.Viewport, {
81
+ className: I(),
82
+ children: [/* @__PURE__ */ a("div", {
83
+ className: L(),
84
+ children: h
85
+ }), /* @__PURE__ */ o("div", {
86
+ className: z(),
81
87
  "aria-hidden": "true",
82
- children: [f, _ && /* @__PURE__ */ i("span", { children: "*" })]
88
+ children: [p, v && /* @__PURE__ */ a("span", { children: "*" })]
83
89
  })]
84
90
  })
85
91
  }) })]
86
92
  })
87
93
  });
88
94
  };
89
- d.displayName = "Select";
95
+ f.displayName = "Select";
90
96
  //#endregion
91
- export { d as default };
97
+ export { f as default };
92
98
 
93
99
  //# sourceMappingURL=SelectRoot.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"SelectRoot.js","names":[],"sources":["../../src/SelectRoot.tsx"],"sourcesContent":["\"use client\";\n\nimport * as SelectPrimitive from \"@radix-ui/react-select\";\nimport React, { RefObject, useId, useState } from \"react\";\nimport { tv } from \"tailwind-variants\";\n\nimport { InputWrapper } from \"@bonprix-ds/internal-components\";\nimport Icon from \"@bonprix-ds/react-icon\";\nimport { useSafeAreaInsetBottom, useZIndex } from \"@bonprix-ds/utils\";\n\nconst generateClasses = tv({\n slots: {\n trigger: \"flex w-full align-center items-center gap-3 p-3 outline-none\",\n icon: \"w-5 h-5 fill-inherit shrink-0 [button[data-state='open']_span>&]:rotate-180\",\n menu: [\n \"group/menu preflight bg-clr-bg-main overflow-hidden whitespace-nowrap\",\n \"border border-clr-primary rounded data-[side=top]:rounded-b-none data-[side=bottom]:rounded-t-none\",\n \"w-[calc(var(--radix-select-trigger-width)+0.125rem)]\", // border is managed on InputWrapper, we need to add 0.125rem here to stay flush visually\n ],\n // max-height when opened to bottom : 6.5 x 38px (item height) + 5 x 4px (gap) + 8px x 2 (padding top or bottom) = 283px\n // max-height when opened to top: 283px + 19px label clone = 302px\n viewport: \"max-h-[calc(283/16*1rem)] group-data-[side=top]/menu:max-h-[calc(302/16*1rem)] overscroll-none\",\n list: \"flex flex-col gap-1 p-2\",\n text: [\n \"h-[1lh] typography-input-text grow text-left truncate\",\n // override Safari iOS UA bluish style coming from the button tag\n \"text-clr-primary\",\n ],\n labelClone: \"hidden sticky bottom-0 bg-clr-bg-main typography-input-label pb-[0.0625rem] pl-[0.6875rem] pe-2\", // mimick label\n },\n variants: {\n labelFloated: {\n // We show a label clone inside the dropdown whenever the actual label is floated and hidden behind the dropdown.\n // Whehn not floated, the label is still visible inside the Select box as kind of a placeholder, so no need to duplicate it then.\n true: { labelClone: \"group-data-[side=top]/menu:block\" },\n },\n },\n});\n\ninterface BaseProps {\n /** The id of the input field. */\n id?: string;\n /** The visible label for the input field. */\n label: string;\n /** This is where Select.Item subcomponents should be placed. */\n children: React.ReactNode;\n /** The name of the input field. */\n name?: string;\n /** If `true`, disables the input. */\n disabled?: boolean;\n /** If `true`, the input field is in an error state. Required for `errorText` to be shown. */\n error?: boolean;\n /** If `true`, marks the input as required. */\n required?: boolean;\n /** A text that further describes the field and helps with filling out the form correctly. */\n helperText?: string;\n /** A text message that appears when the field is invalid. Only visible when `error` is set to `true`. */\n errorText?: string;\n /** <a href=\"https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Attributes/aria-describedby\" target=\"_blank\">ARIA: aria-describedby attribute.</a> */\n describedBy?: string;\n /** Reference to the root element of the select. */\n ref?: RefObject<HTMLButtonElement>;\n}\n\ninterface ControlledSelectProps extends BaseProps {\n /** The current value of the select. Should be used in conjunction with `onValueChange`. */\n value: string;\n /** The default value of the select. Should be used when select is uncontrolled. */\n defaultValue?: never;\n /** Callback that is triggered when the value changes. */\n onValueChange: (value: string) => void;\n}\n\ninterface UncontrolledSelectProps extends BaseProps {\n value?: never;\n defaultValue: string;\n onValueChange?: (value: string) => void;\n}\n\nexport type Props = ControlledSelectProps | UncontrolledSelectProps;\n\nconst SelectRoot = ({\n id,\n name,\n label,\n onValueChange,\n children,\n disabled = false,\n error = false,\n required = false,\n helperText = \"\",\n errorText = \"\",\n ref,\n ...unionProps\n}: Props) => {\n const generatedId = useId();\n const calculatedId = id ?? generatedId;\n const helperTextId = useId();\n const errorTextId = useId();\n\n const isControlled = unionProps.value !== undefined;\n const value = isControlled ? unionProps.value : undefined;\n const defaultValue = isControlled ? undefined : unionProps.defaultValue;\n const initialLabelFloated = isControlled ? !!unionProps.value : !!defaultValue;\n const [labelFloated, setLabelFloated] = useState(initialLabelFloated);\n\n const variations = { error, labelFloated };\n const { trigger, icon, menu, viewport, list, text, labelClone } = generateClasses(variations);\n const zIndex = useZIndex();\n\n let ariaDescribedBy;\n if (helperText) {\n ariaDescribedBy = helperTextId;\n }\n if (error && errorText) {\n ariaDescribedBy = ariaDescribedBy ? ariaDescribedBy + \" \" + errorTextId : errorTextId;\n }\n\n const safeAreaInsetBottom = useSafeAreaInsetBottom();\n\n const handleValueChange = (value: string) => {\n setLabelFloated(true);\n if (onValueChange) {\n onValueChange(value);\n }\n };\n\n return (\n <InputWrapper\n label={label}\n id={calculatedId}\n required={required}\n labelFloated={labelFloated}\n disabled={disabled}\n error={error}\n errorText={errorText}\n errorTextId={errorTextId}\n helperText={helperText}\n helperTextId={helperTextId}\n componentName=\"select\"\n >\n <SelectPrimitive.Root name={name} disabled={disabled} value={value} onValueChange={handleValueChange} defaultValue={defaultValue}>\n <SelectPrimitive.Trigger\n id={calculatedId}\n className={trigger()}\n ref={ref}\n aria-describedby={ariaDescribedBy}\n aria-invalid={error ? \"true\" : undefined}\n data-bpds=\"select\"\n >\n <span className={text()}>\n <SelectPrimitive.Value />\n </span>\n <SelectPrimitive.Icon>\n <Icon variant=\"arrow-down\" className={icon()} />\n </SelectPrimitive.Icon>\n </SelectPrimitive.Trigger>\n <SelectPrimitive.Portal>\n <SelectPrimitive.Content\n position=\"popper\"\n side=\"bottom\"\n align=\"start\"\n alignOffset={-1} // no idea why we suddently need to adjust it\n className={menu()}\n style={{ zIndex }}\n collisionPadding={safeAreaInsetBottom ? { bottom: safeAreaInsetBottom + 10 } : undefined}\n >\n <SelectPrimitive.Viewport className={viewport()}>\n <div className={list()}>{children}</div>\n {/* Scrollbar should go until the end. So we need to use a pseudo element instad of padding */}\n <div className={labelClone()} aria-hidden=\"true\">\n {label}\n {required && <span>*</span>}\n </div>\n </SelectPrimitive.Viewport>\n </SelectPrimitive.Content>\n </SelectPrimitive.Portal>\n </SelectPrimitive.Root>\n </InputWrapper>\n );\n};\n\nSelectRoot.displayName = \"Select\";\n\nexport default SelectRoot;\n"],"mappings":";;;;;;;;;AAUA,IAAM,IAAkB,EAAG;CACzB,OAAO;EACL,SAAS;EACT,MAAM;EACN,MAAM;GACJ;GACA;GACA;EACF;EAGA,UAAU;EACV,MAAM;EACN,MAAM,CACJ,yDAEA,kBACF;EACA,YAAY;CACd;CACA,UAAU,EACR,cAAc,EAGZ,MAAM,EAAE,YAAY,mCAAmC,EACzD,EACF;AACF,CAAC,GA4CK,KAAc,EAClB,OACA,SACA,UACA,kBACA,aACA,cAAW,IACX,WAAQ,IACR,cAAW,IACX,gBAAa,IACb,eAAY,IACZ,QACA,GAAG,QACQ;CACX,IAAM,IAAc,EAAM,GACpB,IAAe,KAAM,GACrB,IAAe,EAAM,GACrB,IAAc,EAAM,GAEpB,IAAe,EAAW,UAAU,KAAA,GACpC,IAAQ,IAAe,EAAW,QAAQ,KAAA,GAC1C,IAAe,IAAe,KAAA,IAAY,EAAW,cACrD,IAAsB,IAAe,CAAC,CAAC,EAAW,QAAQ,CAAC,CAAC,GAC5D,CAAC,GAAc,KAAmB,EAAS,CAAmB,GAG9D,EAAE,YAAS,SAAM,SAAM,aAAU,SAAM,SAAM,kBAAe,EAAgB;EAD7D;EAAO;CACsD,CAAU,GACtF,IAAS,EAAU,GAErB;CAIJ,AAHI,MACF,IAAkB,IAEhB,KAAS,MACX,IAAkB,IAAkB,IAAkB,MAAM,IAAc;CAG5E,IAAM,IAAsB,EAAuB;CASnD,OACE,kBAAC,GAAD;EACS;EACP,IAAI;EACM;EACI;EACJ;EACH;EACI;EACE;EACD;EACE;EACd,eAAc;EAEd,UAAA,kBAAC,EAAgB,MAAjB;GAA4B;GAAgB;GAAiB;GAAO,gBArB7C,MAAkB;IAE3C,AADA,EAAgB,EAAI,GAChB,KACF,EAAc,CAAK;GAEvB;GAgBwH;GAApH,UAAA,CACE,kBAAC,EAAgB,SAAjB;IACE,IAAI;IACJ,WAAW,EAAQ;IACd;IACL,oBAAkB;IAClB,gBAAc,IAAQ,SAAS,KAAA;IAC/B,aAAU;IANZ,UAAA,CAQE,kBAAC,QAAD;KAAM,WAAW,EAAK;KACpB,UAAA,kBAAC,EAAgB,OAAjB,CAAwB,CAAA;IACpB,CAAA,GACN,kBAAC,EAAgB,MAAjB,EAAA,UACE,kBAAC,GAAD;KAAM,SAAQ;KAAa,WAAW,EAAK;IAAI,CAAA,EAC3B,CAAA,CACC;GACzB,CAAA,GAAA,kBAAC,EAAgB,QAAjB,EAAA,UACE,kBAAC,EAAgB,SAAjB;IACE,UAAS;IACT,MAAK;IACL,OAAM;IACN,aAAa;IACb,WAAW,EAAK;IAChB,OAAO,EAAE,UAAO;IAChB,kBAAkB,IAAsB,EAAE,QAAQ,IAAsB,GAAG,IAAI,KAAA;IAE/E,UAAA,kBAAC,EAAgB,UAAjB;KAA0B,WAAW,EAAS;KAA9C,UAAA,CACE,kBAAC,OAAD;MAAK,WAAW,EAAK;MAAI;KAAc,CAAA,GAEvC,kBAAC,OAAD;MAAK,WAAW,EAAW;MAAG,eAAY;MAA1C,UAAA,CACG,GACA,KAAY,kBAAC,QAAD,EAAA,UAAM,IAAO,CAAA,CACvB;KACmB,CAAA,CAAA;;GACH,CAAA,EACH,CAAA,CACJ;;CACV,CAAA;AAElB;AAEA,EAAW,cAAc"}
1
+ {"version":3,"file":"SelectRoot.js","names":["SelectPrimitive","Icon","generateClasses","tv","SelectRoot","unionProps","generatedId","useId","calculatedId","id","helperTextId","errorTextId","isControlled","value","defaultValue","initialLabelFloated","labelFloated","setLabelFloated","useState","zIndex","useZIndex","ariaDescribedBy","helperText","error","errorText","safeAreaInsetBottom","useSafeAreaInsetBottom","ignoreTriggerClickRef","useRef","InputWrapper","onValueChange","trigger","event","text","icon","menu","viewport","list","labelClone","label","required"],"sources":["../../src/SelectRoot.tsx"],"sourcesContent":["\"use client\";\n\nimport * as SelectPrimitive from \"@radix-ui/react-select\";\nimport React, { RefObject, useId, useRef, useState } from \"react\";\nimport { tv } from \"tailwind-variants\";\n\nimport { InputWrapper } from \"@bonprix-ds/internal-components\";\nimport Icon from \"@bonprix-ds/react-icon\";\nimport { useSafeAreaInsetBottom, useZIndex } from \"@bonprix-ds/utils\";\n\nimport \"./Select.css\";\n\nconst generateClasses = tv({\n slots: {\n trigger: \"align-center flex w-full items-center gap-3 p-3 outline-none\",\n icon: \"h-5 w-5 shrink-0 fill-inherit [button[data-state='open']_span>&]:rotate-180\",\n menu: [\n \"group/menu preflight overflow-hidden whitespace-nowrap bg-clr-bg-main\",\n \"rounded border border-clr-primary data-[side=bottom]:rounded-t-none data-[side=top]:rounded-b-none\",\n \"w-[calc(var(--radix-select-trigger-width)+0.125rem)]\", // border is managed on InputWrapper, we need to add 0.125rem here to stay flush visually\n ],\n // max-height when opened to bottom : 6.5 x 38px (item height) + 5 x 4px (gap) + 8px x 2 (padding top or bottom) = 283px\n // max-height when opened to top: 283px + 19px label clone = 302px\n viewport: \"max-h-[calc(283/16*1rem)] overscroll-none group-data-[side=top]/menu:max-h-[calc(302/16*1rem)]\",\n list: \"flex flex-col gap-1 p-2\",\n text: [\n \"typography-input-text h-[1lh] grow truncate text-left\",\n // override Safari iOS UA bluish style coming from the button tag\n \"text-clr-primary\",\n ],\n labelClone: \"typography-input-label sticky bottom-0 hidden bg-clr-bg-main pb-[0.0625rem] pe-2 pl-[0.6875rem]\", // mimick label\n },\n variants: {\n labelFloated: {\n // We show a label clone inside the dropdown whenever the actual label is floated and hidden behind the dropdown.\n // Whehn not floated, the label is still visible inside the Select box as kind of a placeholder, so no need to duplicate it then.\n true: { labelClone: \"group-data-[side=top]/menu:block\" },\n },\n },\n});\n\ninterface BaseProps {\n /** The id of the input field. */\n id?: string;\n /** The visible label for the input field. */\n label: string;\n /** This is where Select.Item subcomponents should be placed. */\n children: React.ReactNode;\n /** The name of the input field. */\n name?: string;\n /** If `true`, disables the input. */\n disabled?: boolean;\n /** If `true`, the input field is in an error state. Required for `errorText` to be shown. */\n error?: boolean;\n /** If `true`, marks the input as required. */\n required?: boolean;\n /** A text that further describes the field and helps with filling out the form correctly. */\n helperText?: string;\n /** A text message that appears when the field is invalid. Only visible when `error` is set to `true`. */\n errorText?: string;\n /** <a href=\"https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Attributes/aria-describedby\" target=\"_blank\">ARIA: aria-describedby attribute.</a> */\n describedBy?: string;\n /** Reference to the root element of the select. */\n ref?: RefObject<HTMLButtonElement>;\n}\n\ninterface ControlledSelectProps extends BaseProps {\n /** The current value of the select. Should be used in conjunction with `onValueChange`. */\n value: string;\n /** The default value of the select. Should be used when select is uncontrolled. */\n defaultValue?: never;\n /** Callback that is triggered when the value changes. */\n onValueChange: (value: string) => void;\n}\n\ninterface UncontrolledSelectProps extends BaseProps {\n value?: never;\n defaultValue: string;\n onValueChange?: (value: string) => void;\n}\n\nexport type Props = ControlledSelectProps | UncontrolledSelectProps;\n\nconst SelectRoot = ({\n id,\n name,\n label,\n onValueChange,\n children,\n disabled = false,\n error = false,\n required = false,\n helperText = \"\",\n errorText = \"\",\n ref,\n ...unionProps\n}: Props) => {\n const generatedId = useId();\n const calculatedId = id ?? generatedId;\n const helperTextId = useId();\n const errorTextId = useId();\n\n const isControlled = unionProps.value !== undefined;\n const value = isControlled ? unionProps.value : undefined;\n const defaultValue = isControlled ? undefined : unionProps.defaultValue;\n const initialLabelFloated = isControlled ? !!unionProps.value : !!defaultValue;\n const [labelFloated, setLabelFloated] = useState(initialLabelFloated);\n\n const variations = { error, labelFloated };\n const { trigger, icon, menu, viewport, list, text, labelClone } = generateClasses(variations);\n const zIndex = useZIndex();\n\n let ariaDescribedBy;\n if (helperText) {\n ariaDescribedBy = helperTextId;\n }\n if (error && errorText) {\n ariaDescribedBy = ariaDescribedBy ? ariaDescribedBy + \" \" + errorTextId : errorTextId;\n }\n\n const safeAreaInsetBottom = useSafeAreaInsetBottom();\n\n // Radix closes the Select on the pointerdown of a tap outside of it, but opens it on the click of a tap on the trigger.\n // On an open Select, a tap on the trigger is both, so its click would open the Select again.\n const ignoreTriggerClickRef = useRef(false);\n\n const handleValueChange = (value: string) => {\n setLabelFloated(true);\n if (onValueChange) {\n onValueChange(value);\n }\n };\n\n return (\n <InputWrapper\n label={label}\n id={calculatedId}\n required={required}\n labelFloated={labelFloated}\n disabled={disabled}\n error={error}\n errorText={errorText}\n errorTextId={errorTextId}\n helperText={helperText}\n helperTextId={helperTextId}\n componentName=\"select\"\n >\n <SelectPrimitive.Root name={name} disabled={disabled} value={value} onValueChange={handleValueChange} defaultValue={defaultValue}>\n <SelectPrimitive.Trigger\n id={calculatedId}\n className={trigger()}\n ref={ref}\n aria-describedby={ariaDescribedBy}\n aria-invalid={error ? \"true\" : undefined}\n data-bpds=\"select\"\n onPointerDown={() => (ignoreTriggerClickRef.current = false)}\n onClick={(event) => {\n if (ignoreTriggerClickRef.current) {\n ignoreTriggerClickRef.current = false;\n event.preventDefault();\n }\n }}\n >\n <span className={text()}>\n <SelectPrimitive.Value />\n </span>\n <SelectPrimitive.Icon>\n <Icon variant=\"arrow-down\" className={icon()} />\n </SelectPrimitive.Icon>\n </SelectPrimitive.Trigger>\n <SelectPrimitive.Portal>\n <SelectPrimitive.Content\n position=\"popper\"\n side=\"bottom\"\n align=\"start\"\n alignOffset={-1} // no idea why we suddently need to adjust it\n className={menu()}\n style={{ zIndex }}\n collisionPadding={safeAreaInsetBottom ? { bottom: safeAreaInsetBottom + 10 } : undefined}\n onPointerDownOutside={(event) => (ignoreTriggerClickRef.current = event.detail.originalEvent.pointerType !== \"mouse\")}\n >\n <SelectPrimitive.Viewport className={viewport()}>\n <div className={list()}>{children}</div>\n {/* Scrollbar should go until the end. So we need to use a pseudo element instad of padding */}\n <div className={labelClone()} aria-hidden=\"true\">\n {label}\n {required && <span>*</span>}\n </div>\n </SelectPrimitive.Viewport>\n </SelectPrimitive.Content>\n </SelectPrimitive.Portal>\n </SelectPrimitive.Root>\n </InputWrapper>\n );\n};\n\nSelectRoot.displayName = \"Select\";\n\nexport default SelectRoot;\n"],"mappings":";;;;;;;;;;AAYA,IAAME,IAAkBC,EAAG;CACzB,OAAO;EACL,SAAS;EACT,MAAM;EACN,MAAM;GACJ;GACA;GACA;EACF;EAGA,UAAU;EACV,MAAM;EACN,MAAM,CACJ,yDAEA,kBACF;EACA,YAAY;CACd;CACA,UAAU,EACR,cAAc,EAGZ,MAAM,EAAE,YAAY,mCAAmC,EACzD,EACF;AACF,CAAC,GA4CKC,KAAc,EAClB,OACA,SACA,UACA,kBACA,aACA,cAAW,IACX,WAAQ,IACR,cAAW,IACX,gBAAa,IACb,eAAY,IACZ,QACA,GAAGC,QACQ;CACX,IAAMC,IAAcC,EAAM,GACpBC,IAAeC,KAAMH,GACrBI,IAAeH,EAAM,GACrBI,IAAcJ,EAAM,GAEpBK,IAAeP,EAAW,UAAU,KAAA,GACpCQ,IAAQD,IAAeP,EAAW,QAAQ,KAAA,GAC1CS,IAAeF,IAAe,KAAA,IAAYP,EAAW,cACrDU,IAAsBH,IAAe,CAAC,CAACP,EAAW,QAAQ,CAAC,CAACS,GAC5D,CAACE,GAAcC,KAAmBC,EAASH,CAAmB,GAG9D,EAAE,YAAS,SAAM,SAAM,aAAU,SAAM,SAAM,kBAAeb,EAAgB;EAD7D;EAAO;CACsD,CAAU,GACtFiB,IAASC,EAAU,GAErBC;CAIJ,AAHIC,MACF,IAAkBZ,IAEhBa,KAASC,MACX,IAAkBH,IAAkBA,IAAkB,MAAMV,IAAcA;CAG5E,IAAMc,IAAsBC,EAAuB,GAI7CC,IAAwBC,EAAO,EAAK;CAS1C,OACE,kBAACC,GAAD;EACS;EACP,IAAIrB;EACM;EACI;EACJ;EACH;EACI;EACE;EACD;EACE;EACd,eAAc;EAEd,UAAA,kBAACR,EAAgB,MAAjB;GAA4B;GAAgB;GAAiB;GAAO,gBArB7C,MAAkB;IAE3C,AADAiB,EAAgB,EAAI,GAChBa,KACFA,EAAcjB,CAAK;GAEvB;GAgBwH;GAApH,UAAA,CACE,kBAACb,EAAgB,SAAjB;IACE,IAAIQ;IACJ,WAAWuB,EAAQ;IACd;IACL,oBAAkBV;IAClB,gBAAcE,IAAQ,SAAS,KAAA;IAC/B,aAAU;IACV,qBAAsB,EAAsB,UAAU;IACtD,UAAU,MAAU;KAClB,AAAII,EAAsB,YACxB,EAAsB,UAAU,IAChCK,EAAM,eAAe;IAEzB;IAbF,UAAA,CAeE,kBAAC,QAAD;KAAM,WAAWC,EAAK;KACpB,UAAA,kBAACjC,EAAgB,OAAjB,CAAwB,CAAA;IACpB,CAAA,GACN,kBAACA,EAAgB,MAAjB,EAAA,UACE,kBAACC,GAAD;KAAM,SAAQ;KAAa,WAAWiC,EAAK;IAAI,CAAA,EAC3B,CAAA,CACC;GACzB,CAAA,GAAA,kBAAClC,EAAgB,QAAjB,EAAA,UACE,kBAACA,EAAgB,SAAjB;IACE,UAAS;IACT,MAAK;IACL,OAAM;IACN,aAAa;IACb,WAAWmC,EAAK;IAChB,OAAO,EAAE,UAAO;IAChB,kBAAkBV,IAAsB,EAAE,QAAQA,IAAsB,GAAG,IAAI,KAAA;IAC/E,uBAAuB,MAAW,EAAsB,UAAUO,EAAM,OAAO,cAAc,gBAAgB;IAE7G,UAAA,kBAAChC,EAAgB,UAAjB;KAA0B,WAAWoC,EAAS;KAA9C,UAAA,CACE,kBAAC,OAAD;MAAK,WAAWC,EAAK;MAAI;KAAc,CAAA,GAEvC,kBAAC,OAAD;MAAK,WAAWC,EAAW;MAAG,eAAY;MAA1C,UAAA,CACGC,GACAC,KAAY,kBAAC,QAAD,EAAA,UAAM,IAAO,CAAA,CACvB;KACmB,CAAA,CAAA;;GACH,CAAA,EACH,CAAA,CACJ;;CACV,CAAA;AAElB;AAEA,EAAW,cAAc"}
package/dist/styles.js ADDED
@@ -0,0 +1 @@
1
+ import "./src/Select.css";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bonprix-ds/react-select",
3
- "version": "1.0.9",
3
+ "version": "1.0.11",
4
4
  "description": "Select component, implemented in React, for the Bonprix Design System.",
5
5
  "keywords": [],
6
6
  "license": "UNLICENSED",
@@ -17,9 +17,18 @@
17
17
  "!dist/**/*.test.d.ts"
18
18
  ],
19
19
  "type": "module",
20
- "sideEffects": false,
20
+ "sideEffects": [
21
+ "**/*.css",
22
+ "./dist/styles.js"
23
+ ],
21
24
  "module": "./dist/index.js",
22
25
  "types": "./dist/index.d.ts",
26
+ "imports": {
27
+ "#styles": {
28
+ "browser": "./dist/styles.js",
29
+ "default": "./dist/no-styles.js"
30
+ }
31
+ },
23
32
  "exports": {
24
33
  ".": {
25
34
  "import": "./dist/index.js",
@@ -33,19 +42,22 @@
33
42
  },
34
43
  "dependencies": {
35
44
  "@radix-ui/react-select": "2.3.7",
36
- "@bonprix-ds/internal-components": "0.2.1",
37
- "@bonprix-ds/react-icon": "1.2.1",
38
- "@bonprix-ds/utils": "0.2.2"
45
+ "@bonprix-ds/internal-components": "0.2.2",
46
+ "@bonprix-ds/react-icon": "1.2.2",
47
+ "@bonprix-ds/utils": "0.3.0"
39
48
  },
40
49
  "devDependencies": {
50
+ "@testing-library/dom": "10.4.2",
51
+ "@testing-library/react": "16.3.3",
41
52
  "@types/react": "19.3.0",
42
- "@vitejs/plugin-react": "6.1.1",
43
- "rollup-preserve-directives": "1.1.3",
44
- "vite": "8.3.0"
53
+ "@vitest/browser-playwright": "4.1.11",
54
+ "vite": "8.3.2",
55
+ "vitest": "4.1.11",
56
+ "@bonprix-ds/vite-config": "0.0.0"
45
57
  },
46
58
  "peerDependencies": {
47
- "react": "^19.3.0",
48
- "react-dom": "^19.3.0",
59
+ "react": "^19",
60
+ "react-dom": "^19",
49
61
  "tailwind-variants": "0.3.1"
50
62
  },
51
63
  "scripts": {