@fanvue/ui 3.13.0 → 3.14.1
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/cjs/components/AudioPlayer/AudioPlayer.cjs +357 -0
- package/dist/cjs/components/AudioPlayer/AudioPlayer.cjs.map +1 -0
- package/dist/cjs/components/Dialog/Dialog.cjs +6 -4
- package/dist/cjs/components/Dialog/Dialog.cjs.map +1 -1
- package/dist/cjs/components/FloatingActionButton/FloatingActionButton.cjs +59 -0
- package/dist/cjs/components/FloatingActionButton/FloatingActionButton.cjs.map +1 -0
- package/dist/cjs/components/Icons/GridViewIcon.cjs +50 -0
- package/dist/cjs/components/Icons/GridViewIcon.cjs.map +1 -0
- package/dist/cjs/components/Icons/ListViewIcon.cjs +50 -0
- package/dist/cjs/components/Icons/ListViewIcon.cjs.map +1 -0
- package/dist/cjs/components/SegmentedControl/SegmentedControl.cjs +42 -11
- package/dist/cjs/components/SegmentedControl/SegmentedControl.cjs.map +1 -1
- package/dist/cjs/index.cjs +8 -0
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/components/AudioPlayer/AudioPlayer.mjs +340 -0
- package/dist/components/AudioPlayer/AudioPlayer.mjs.map +1 -0
- package/dist/components/Dialog/Dialog.mjs +6 -4
- package/dist/components/Dialog/Dialog.mjs.map +1 -1
- package/dist/components/FloatingActionButton/FloatingActionButton.mjs +42 -0
- package/dist/components/FloatingActionButton/FloatingActionButton.mjs.map +1 -0
- package/dist/components/Icons/GridViewIcon.mjs +33 -0
- package/dist/components/Icons/GridViewIcon.mjs.map +1 -0
- package/dist/components/Icons/ListViewIcon.mjs +33 -0
- package/dist/components/Icons/ListViewIcon.mjs.map +1 -0
- package/dist/components/SegmentedControl/SegmentedControl.mjs +42 -11
- package/dist/components/SegmentedControl/SegmentedControl.mjs.map +1 -1
- package/dist/index.d.ts +134 -1
- package/dist/index.mjs +8 -0
- package/dist/index.mjs.map +1 -1
- package/dist/styles/base.css +15 -0
- package/package.json +1 -1
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { BaseIcon } from "./BaseIcon.mjs";
|
|
5
|
+
const VARIANTS = {
|
|
6
|
+
16: {
|
|
7
|
+
outlined: [
|
|
8
|
+
{
|
|
9
|
+
d: "M13.267 9H2.733c-1 0-1.4.427-1.4 1.487v2.693c0 1.06.4 1.487 1.4 1.487h10.534c1 0 1.4-.427 1.4-1.487v-2.693c0-1.06-.4-1.487-1.4-1.487m0-7.667H2.733c-1 0-1.4.427-1.4 1.487v2.693c0 1.06.4 1.487 1.4 1.487h10.534c1 0 1.4-.427 1.4-1.487V2.82c0-1.06-.4-1.487-1.4-1.487"
|
|
10
|
+
}
|
|
11
|
+
]
|
|
12
|
+
},
|
|
13
|
+
24: {
|
|
14
|
+
outlined: [
|
|
15
|
+
{
|
|
16
|
+
d: "M19.9 13.5H4.1c-1.5 0-2.1.64-2.1 2.23v4.04C2 21.36 2.6 22 4.1 22h15.8c1.5 0 2.1-.64 2.1-2.23v-4.04c0-1.59-.6-2.23-2.1-2.23m0-11.5H4.1C2.6 2 2 2.64 2 4.23v4.04c0 1.59.6 2.23 2.1 2.23h15.8c1.5 0 2.1-.64 2.1-2.23V4.23C22 2.64 21.4 2 19.9 2"
|
|
17
|
+
}
|
|
18
|
+
]
|
|
19
|
+
},
|
|
20
|
+
32: {
|
|
21
|
+
outlined: [
|
|
22
|
+
{
|
|
23
|
+
d: "M26.533 18H5.467c-2 0-2.8.853-2.8 2.973v5.387c0 2.12.8 2.973 2.8 2.973h21.066c2 0 2.8-.853 2.8-2.973v-5.387c0-2.12-.8-2.973-2.8-2.973m0-15.333H5.467c-2 0-2.8.853-2.8 2.973v5.387c0 2.12.8 2.973 2.8 2.973h21.066c2 0 2.8-.854 2.8-2.973V5.64c0-2.12-.8-2.973-2.8-2.973"
|
|
24
|
+
}
|
|
25
|
+
]
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
const ListViewIcon = React.forwardRef((props, ref) => /* @__PURE__ */ jsx(BaseIcon, { ref, variants: VARIANTS, ...props }));
|
|
29
|
+
ListViewIcon.displayName = "ListViewIcon";
|
|
30
|
+
export {
|
|
31
|
+
ListViewIcon
|
|
32
|
+
};
|
|
33
|
+
//# sourceMappingURL=ListViewIcon.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ListViewIcon.mjs","sources":["../../../src/components/Icons/ListViewIcon.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { BaseIcon } from \"./BaseIcon\";\nimport type { BaseIconProps, IconVariants } from \"./types\";\n\nconst VARIANTS: IconVariants = {\n 16: {\n outlined: [\n {\n d: \"M13.267 9H2.733c-1 0-1.4.427-1.4 1.487v2.693c0 1.06.4 1.487 1.4 1.487h10.534c1 0 1.4-.427 1.4-1.487v-2.693c0-1.06-.4-1.487-1.4-1.487m0-7.667H2.733c-1 0-1.4.427-1.4 1.487v2.693c0 1.06.4 1.487 1.4 1.487h10.534c1 0 1.4-.427 1.4-1.487V2.82c0-1.06-.4-1.487-1.4-1.487\",\n },\n ],\n },\n 24: {\n outlined: [\n {\n d: \"M19.9 13.5H4.1c-1.5 0-2.1.64-2.1 2.23v4.04C2 21.36 2.6 22 4.1 22h15.8c1.5 0 2.1-.64 2.1-2.23v-4.04c0-1.59-.6-2.23-2.1-2.23m0-11.5H4.1C2.6 2 2 2.64 2 4.23v4.04c0 1.59.6 2.23 2.1 2.23h15.8c1.5 0 2.1-.64 2.1-2.23V4.23C22 2.64 21.4 2 19.9 2\",\n },\n ],\n },\n 32: {\n outlined: [\n {\n d: \"M26.533 18H5.467c-2 0-2.8.853-2.8 2.973v5.387c0 2.12.8 2.973 2.8 2.973h21.066c2 0 2.8-.853 2.8-2.973v-5.387c0-2.12-.8-2.973-2.8-2.973m0-15.333H5.467c-2 0-2.8.853-2.8 2.973v5.387c0 2.12.8 2.973 2.8 2.973h21.066c2 0 2.8-.854 2.8-2.973V5.64c0-2.12-.8-2.973-2.8-2.973\",\n },\n ],\n },\n};\n\n/** Props for {@link ListViewIcon}. See {@link BaseIconProps} for the shared shape. */\nexport type ListViewIconProps = BaseIconProps;\n\n/**\n * List view icon. Renders at sizes 16, 24, or 32 px.\n *\n * @example\n * ```tsx\n * <ListViewIcon size={24} />\n * ```\n */\nexport const ListViewIcon = React.forwardRef<SVGSVGElement, ListViewIconProps>((props, ref) => (\n <BaseIcon ref={ref} variants={VARIANTS} {...props} />\n));\n\nListViewIcon.displayName = \"ListViewIcon\";\n"],"names":[],"mappings":";;;;AAIA,MAAM,WAAyB;AAAA,EAC7B,IAAI;AAAA,IACF,UAAU;AAAA,MACR;AAAA,QACE,GAAG;AAAA,MAAA;AAAA,IACL;AAAA,EACF;AAAA,EAEF,IAAI;AAAA,IACF,UAAU;AAAA,MACR;AAAA,QACE,GAAG;AAAA,MAAA;AAAA,IACL;AAAA,EACF;AAAA,EAEF,IAAI;AAAA,IACF,UAAU;AAAA,MACR;AAAA,QACE,GAAG;AAAA,MAAA;AAAA,IACL;AAAA,EACF;AAEJ;AAaO,MAAM,eAAe,MAAM,WAA6C,CAAC,OAAO,QACrF,oBAAC,UAAA,EAAS,KAAU,UAAU,UAAW,GAAG,OAAO,CACpD;AAED,aAAa,cAAc;"}
|
|
@@ -16,11 +16,47 @@ function warnMissingAccessibleName(ariaLabel, ariaLabelledBy) {
|
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
|
+
function warnMissingOptionAccessibleName(options) {
|
|
20
|
+
if (process.env.NODE_ENV !== "production") {
|
|
21
|
+
for (const option of options) {
|
|
22
|
+
if (option.icon && !option.label?.trim()) {
|
|
23
|
+
console.warn(
|
|
24
|
+
`SegmentedControl: icon-only segment "${option.value}" is missing a non-empty \`label\` to use as its accessible name.`
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function getSegmentClassName({
|
|
31
|
+
appearance,
|
|
32
|
+
size,
|
|
33
|
+
variant,
|
|
34
|
+
isSelected,
|
|
35
|
+
disabled
|
|
36
|
+
}) {
|
|
37
|
+
return cn(
|
|
38
|
+
"relative inline-flex min-w-0 cursor-pointer items-center justify-center rounded-full",
|
|
39
|
+
"motion-safe:transition-colors motion-safe:duration-150 motion-safe:ease-in-out",
|
|
40
|
+
"focus-visible:shadow-focus-ring focus-visible:outline-none",
|
|
41
|
+
variant === "fill" ? "flex-1" : "shrink-0",
|
|
42
|
+
appearance === "plain" ? cn(
|
|
43
|
+
// Padding + negative margin enlarge the hit target without changing the
|
|
44
|
+
// visual footprint, which must stay glyph-only to match the design.
|
|
45
|
+
"-m-1 p-1",
|
|
46
|
+
isSelected ? "text-icons-primary" : "text-icons-tertiary hover:text-icons-primary"
|
|
47
|
+
) : cn(
|
|
48
|
+
sizeClasses[size],
|
|
49
|
+
isSelected ? "bg-buttons-primary-default text-content-primary-inverted shadow-sm" : "text-content-primary hover:bg-buttons-switch-hover"
|
|
50
|
+
),
|
|
51
|
+
disabled && "pointer-events-none"
|
|
52
|
+
);
|
|
53
|
+
}
|
|
19
54
|
const SegmentedControl = React.forwardRef(
|
|
20
55
|
({
|
|
21
56
|
className,
|
|
22
57
|
size = "32",
|
|
23
58
|
variant = "hug",
|
|
59
|
+
appearance = "pill",
|
|
24
60
|
options,
|
|
25
61
|
value: controlledValue,
|
|
26
62
|
defaultValue,
|
|
@@ -29,6 +65,7 @@ const SegmentedControl = React.forwardRef(
|
|
|
29
65
|
...props
|
|
30
66
|
}, ref) => {
|
|
31
67
|
warnMissingAccessibleName(props["aria-label"], props["aria-labelledby"]);
|
|
68
|
+
warnMissingOptionAccessibleName(options);
|
|
32
69
|
const [internalValue, setInternalValue] = React.useState(defaultValue ?? options[0]?.value);
|
|
33
70
|
const isControlled = controlledValue !== void 0;
|
|
34
71
|
const currentValue = isControlled ? controlledValue : internalValue;
|
|
@@ -55,8 +92,9 @@ const SegmentedControl = React.forwardRef(
|
|
|
55
92
|
ref,
|
|
56
93
|
role: "radiogroup",
|
|
57
94
|
className: cn(
|
|
58
|
-
"relative items-center rounded-full
|
|
95
|
+
"relative items-center rounded-full",
|
|
59
96
|
variant === "fill" ? "flex w-full" : "inline-flex",
|
|
97
|
+
appearance === "plain" ? "gap-2" : "bg-surface-tertiary p-1",
|
|
60
98
|
disabled && "cursor-not-allowed opacity-50",
|
|
61
99
|
className
|
|
62
100
|
),
|
|
@@ -76,18 +114,11 @@ const SegmentedControl = React.forwardRef(
|
|
|
76
114
|
"aria-checked": isSelected,
|
|
77
115
|
tabIndex: isSelected || !anySelected && index === 0 ? 0 : -1,
|
|
78
116
|
disabled,
|
|
117
|
+
"aria-label": option.icon ? option.label : void 0,
|
|
79
118
|
onClick: () => handleSelect(option.value),
|
|
80
119
|
onKeyDown: (e) => handleKeyDown(e, index),
|
|
81
|
-
className:
|
|
82
|
-
|
|
83
|
-
"motion-safe:transition-colors motion-safe:duration-150 motion-safe:ease-in-out",
|
|
84
|
-
"focus-visible:shadow-focus-ring focus-visible:outline-none",
|
|
85
|
-
variant === "fill" ? "flex-1" : "shrink-0",
|
|
86
|
-
sizeClasses[size],
|
|
87
|
-
isSelected ? "bg-buttons-primary-default text-content-primary-inverted shadow-sm" : "text-content-primary hover:bg-buttons-switch-hover",
|
|
88
|
-
disabled && "pointer-events-none"
|
|
89
|
-
),
|
|
90
|
-
children: /* @__PURE__ */ jsx("span", { className: "min-w-0 truncate", children: option.label })
|
|
120
|
+
className: getSegmentClassName({ appearance, size, variant, isSelected, disabled }),
|
|
121
|
+
children: option.icon ? /* @__PURE__ */ jsx("span", { className: "flex shrink-0 items-center justify-center", "aria-hidden": "true", children: option.icon }) : /* @__PURE__ */ jsx("span", { className: "min-w-0 truncate", children: option.label })
|
|
91
122
|
},
|
|
92
123
|
option.value
|
|
93
124
|
)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SegmentedControl.mjs","sources":["../../../src/components/SegmentedControl/SegmentedControl.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\n/** Height of the segmented control in pixels. */\nexport type SegmentedControlSize = \"32\" | \"40\" | \"48\";\n\n/**\n * Segment layout.\n * - `\"hug\"`: each segment is sized to its content.\n * - `\"fill\"`: the control spans the full width of its container and each segment grows equally.\n */\nexport type SegmentedControlVariant = \"hug\" | \"fill\";\n\n/** Describes one selectable segment. */\nexport interface SegmentedControlOption {\n /** Display label for the segment. */\n label: string;\n /** Value identifier returned via `onChange`. */\n value: string;\n}\n\nexport interface SegmentedControlProps\n extends Omit<React.HTMLAttributes<HTMLDivElement>, \"onChange\"> {\n /** Height of the control in pixels. @default \"32\" */\n size?: SegmentedControlSize;\n /** Segment layout. @default \"hug\" */\n variant?: SegmentedControlVariant;\n /** The selectable segments. Designed for two or three mutually exclusive options. */\n options: SegmentedControlOption[];\n /** Currently selected value (controlled). */\n value?: string;\n /** Initially selected value (uncontrolled). Defaults to the first option. */\n defaultValue?: string;\n /** Callback fired when the selected value changes. */\n onChange?: (value: string) => void;\n /** Whether the control is disabled. @default false */\n disabled?: boolean;\n}\n\n/** Padding and typography per size. Combined with the container's `p-1` these hit the 32/40/48px heights. */\nconst sizeClasses: Record<SegmentedControlSize, string> = {\n \"32\": \"px-2 py-1 typography-description-12px-semibold\",\n \"40\": \"px-3 py-1.75 typography-body-small-14px-semibold\",\n \"48\": \"px-4 py-2 typography-body-default-16px-semibold\",\n};\n\nfunction warnMissingAccessibleName(ariaLabel?: string, ariaLabelledBy?: string) {\n if (process.env.NODE_ENV !== \"production\") {\n if (!ariaLabel && !ariaLabelledBy) {\n console.warn(\n \"SegmentedControl: no accessible name provided. Pass an `aria-label` or `aria-labelledby` prop.\",\n );\n }\n }\n}\n\n/**\n * A compact selector for choosing between two or three mutually exclusive\n * options where the choice affects the content immediately below it. Use\n * instead of tabs when the options are more like settings or filters than\n * navigation, such as toggling a list/grid view or a monthly/annual price.\n *\n * Rendered as a `radiogroup` with roving-tabindex keyboard navigation. Supports\n * both controlled and uncontrolled usage.\n *\n * @example\n * ```tsx\n * <SegmentedControl\n * options={[\n * { label: \"Net\", value: \"net\" },\n * { label: \"Gross\", value: \"gross\" },\n * ]}\n * value={amount}\n * onChange={setAmount}\n * aria-label=\"Amount type\"\n * />\n * ```\n */\nexport const SegmentedControl = React.forwardRef<HTMLDivElement, SegmentedControlProps>(\n (\n {\n className,\n size = \"32\",\n variant = \"hug\",\n options,\n value: controlledValue,\n defaultValue,\n onChange,\n disabled = false,\n ...props\n },\n ref,\n ) => {\n warnMissingAccessibleName(props[\"aria-label\"], props[\"aria-labelledby\"]);\n\n // Tracks selection for uncontrolled usage; ignored when `value` prop is provided\n const [internalValue, setInternalValue] = React.useState(defaultValue ?? options[0]?.value);\n const isControlled = controlledValue !== undefined;\n const currentValue = isControlled ? controlledValue : internalValue;\n const anySelected = options.some((o) => o.value === currentValue);\n const buttonRefs = React.useRef<(HTMLButtonElement | null)[]>([]);\n\n const handleSelect = (optionValue: string) => {\n if (disabled || optionValue === currentValue) return;\n if (!isControlled) {\n setInternalValue(optionValue);\n }\n onChange?.(optionValue);\n };\n\n const handleKeyDown = (e: React.KeyboardEvent, index: number) => {\n const nextIndex =\n e.key === \"ArrowRight\" || e.key === \"ArrowDown\"\n ? (index + 1) % options.length\n : e.key === \"ArrowLeft\" || e.key === \"ArrowUp\"\n ? (index - 1 + options.length) % options.length\n : null;\n if (nextIndex === null) return;\n e.preventDefault();\n const nextOption = options[nextIndex] as SegmentedControlOption;\n handleSelect(nextOption.value);\n buttonRefs.current[nextIndex]?.focus();\n };\n\n return (\n <div\n ref={ref}\n role=\"radiogroup\"\n className={cn(\n \"relative items-center rounded-full bg-surface-tertiary p-1\",\n variant === \"fill\" ? \"flex w-full\" : \"inline-flex\",\n disabled && \"cursor-not-allowed opacity-50\",\n className,\n )}\n {...props}\n >\n {options.map((option, index) => {\n const isSelected = currentValue === option.value;\n return (\n // biome-ignore lint/a11y/useSemanticElements: native radio inputs only allow Tab-focus on the checked item; buttons with roving tabindex give full keyboard navigation\n <button\n key={option.value}\n ref={(el) => {\n buttonRefs.current[index] = el;\n }}\n type=\"button\"\n role=\"radio\"\n aria-checked={isSelected}\n tabIndex={isSelected || (!anySelected && index === 0) ? 0 : -1}\n disabled={disabled}\n onClick={() => handleSelect(option.value)}\n onKeyDown={(e) => handleKeyDown(e, index)}\n className={cn(\n \"relative inline-flex min-w-0 cursor-pointer items-center justify-center rounded-full\",\n \"motion-safe:transition-colors motion-safe:duration-150 motion-safe:ease-in-out\",\n \"focus-visible:shadow-focus-ring focus-visible:outline-none\",\n variant === \"fill\" ? \"flex-1\" : \"shrink-0\",\n sizeClasses[size],\n isSelected\n ? \"bg-buttons-primary-default text-content-primary-inverted shadow-sm\"\n : \"text-content-primary hover:bg-buttons-switch-hover\",\n disabled && \"pointer-events-none\",\n )}\n >\n <span className=\"min-w-0 truncate\">{option.label}</span>\n </button>\n );\n })}\n </div>\n );\n },\n);\n\nSegmentedControl.displayName = \"SegmentedControl\";\n"],"names":[],"mappings":";;;;AAwCA,MAAM,cAAoD;AAAA,EACxD,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AACR;AAEA,SAAS,0BAA0B,WAAoB,gBAAyB;AAC9E,MAAI,QAAQ,IAAI,aAAa,cAAc;AACzC,QAAI,CAAC,aAAa,CAAC,gBAAgB;AACjC,cAAQ;AAAA,QACN;AAAA,MAAA;AAAA,IAEJ;AAAA,EACF;AACF;AAwBO,MAAM,mBAAmB,MAAM;AAAA,EACpC,CACE;AAAA,IACE;AAAA,IACA,OAAO;AAAA,IACP,UAAU;AAAA,IACV;AAAA,IACA,OAAO;AAAA,IACP;AAAA,IACA;AAAA,IACA,WAAW;AAAA,IACX,GAAG;AAAA,EAAA,GAEL,QACG;AACH,8BAA0B,MAAM,YAAY,GAAG,MAAM,iBAAiB,CAAC;AAGvE,UAAM,CAAC,eAAe,gBAAgB,IAAI,MAAM,SAAS,gBAAgB,QAAQ,CAAC,GAAG,KAAK;AAC1F,UAAM,eAAe,oBAAoB;AACzC,UAAM,eAAe,eAAe,kBAAkB;AACtD,UAAM,cAAc,QAAQ,KAAK,CAAC,MAAM,EAAE,UAAU,YAAY;AAChE,UAAM,aAAa,MAAM,OAAqC,EAAE;AAEhE,UAAM,eAAe,CAAC,gBAAwB;AAC5C,UAAI,YAAY,gBAAgB,aAAc;AAC9C,UAAI,CAAC,cAAc;AACjB,yBAAiB,WAAW;AAAA,MAC9B;AACA,iBAAW,WAAW;AAAA,IACxB;AAEA,UAAM,gBAAgB,CAAC,GAAwB,UAAkB;AAC/D,YAAM,YACJ,EAAE,QAAQ,gBAAgB,EAAE,QAAQ,eAC/B,QAAQ,KAAK,QAAQ,SACtB,EAAE,QAAQ,eAAe,EAAE,QAAQ,aAChC,QAAQ,IAAI,QAAQ,UAAU,QAAQ,SACvC;AACR,UAAI,cAAc,KAAM;AACxB,QAAE,eAAA;AACF,YAAM,aAAa,QAAQ,SAAS;AACpC,mBAAa,WAAW,KAAK;AAC7B,iBAAW,QAAQ,SAAS,GAAG,MAAA;AAAA,IACjC;AAEA,WACE;AAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA,MAAK;AAAA,QACL,WAAW;AAAA,UACT;AAAA,UACA,YAAY,SAAS,gBAAgB;AAAA,UACrC,YAAY;AAAA,UACZ;AAAA,QAAA;AAAA,QAED,GAAG;AAAA,QAEH,UAAA,QAAQ,IAAI,CAAC,QAAQ,UAAU;AAC9B,gBAAM,aAAa,iBAAiB,OAAO;AAC3C;AAAA;AAAA,YAEE;AAAA,cAAC;AAAA,cAAA;AAAA,gBAEC,KAAK,CAAC,OAAO;AACX,6BAAW,QAAQ,KAAK,IAAI;AAAA,gBAC9B;AAAA,gBACA,MAAK;AAAA,gBACL,MAAK;AAAA,gBACL,gBAAc;AAAA,gBACd,UAAU,cAAe,CAAC,eAAe,UAAU,IAAK,IAAI;AAAA,gBAC5D;AAAA,gBACA,SAAS,MAAM,aAAa,OAAO,KAAK;AAAA,gBACxC,WAAW,CAAC,MAAM,cAAc,GAAG,KAAK;AAAA,gBACxC,WAAW;AAAA,kBACT;AAAA,kBACA;AAAA,kBACA;AAAA,kBACA,YAAY,SAAS,WAAW;AAAA,kBAChC,YAAY,IAAI;AAAA,kBAChB,aACI,uEACA;AAAA,kBACJ,YAAY;AAAA,gBAAA;AAAA,gBAGd,UAAA,oBAAC,QAAA,EAAK,WAAU,oBAAoB,iBAAO,MAAA,CAAM;AAAA,cAAA;AAAA,cAvB5C,OAAO;AAAA,YAAA;AAAA;AAAA,QA0BlB,CAAC;AAAA,MAAA;AAAA,IAAA;AAAA,EAGP;AACF;AAEA,iBAAiB,cAAc;"}
|
|
1
|
+
{"version":3,"file":"SegmentedControl.mjs","sources":["../../../src/components/SegmentedControl/SegmentedControl.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\n/** Height of the segmented control in pixels. */\nexport type SegmentedControlSize = \"32\" | \"40\" | \"48\";\n\n/**\n * Segment layout.\n * - `\"hug\"`: each segment is sized to its content.\n * - `\"fill\"`: the control spans the full width of its container and each segment grows equally.\n */\nexport type SegmentedControlVariant = \"hug\" | \"fill\";\n\n/**\n * Visual style of the control.\n * - `\"pill\"`: the container has a muted background and the selected segment shows a filled pill (default).\n * - `\"plain\"`: no container or selected-pill background; segments are bare content and selection is\n * communicated by color alone. Designed for icon-only toggles (e.g. a list/grid view switch).\n */\nexport type SegmentedControlAppearance = \"pill\" | \"plain\";\n\n/** Describes one selectable segment. */\nexport interface SegmentedControlOption {\n /**\n * Display label for the segment. When `icon` is provided, the segment renders icon-only and\n * this value is used as its accessible name (applied as `aria-label`) instead of visible text.\n */\n label: string;\n /** Value identifier returned via `onChange`. */\n value: string;\n /**\n * Icon to render instead of the visible label. When set, the segment renders icon-only and\n * `label` becomes required as its accessible name — it is applied as `aria-label` and no\n * visible text is rendered.\n */\n icon?: React.ReactNode;\n}\n\nexport interface SegmentedControlProps\n extends Omit<React.HTMLAttributes<HTMLDivElement>, \"onChange\"> {\n /** Height of the control in pixels. @default \"32\" */\n size?: SegmentedControlSize;\n /** Segment layout. @default \"hug\" */\n variant?: SegmentedControlVariant;\n /** Visual style of the control. @default \"pill\" */\n appearance?: SegmentedControlAppearance;\n /** The selectable segments. Designed for two or three mutually exclusive options. */\n options: SegmentedControlOption[];\n /** Currently selected value (controlled). */\n value?: string;\n /** Initially selected value (uncontrolled). Defaults to the first option. */\n defaultValue?: string;\n /** Callback fired when the selected value changes. */\n onChange?: (value: string) => void;\n /** Whether the control is disabled. @default false */\n disabled?: boolean;\n}\n\n/** Padding and typography per size. Combined with the container's `p-1` these hit the 32/40/48px heights. */\nconst sizeClasses: Record<SegmentedControlSize, string> = {\n \"32\": \"px-2 py-1 typography-description-12px-semibold\",\n \"40\": \"px-3 py-1.75 typography-body-small-14px-semibold\",\n \"48\": \"px-4 py-2 typography-body-default-16px-semibold\",\n};\n\nfunction warnMissingAccessibleName(ariaLabel?: string, ariaLabelledBy?: string) {\n if (process.env.NODE_ENV !== \"production\") {\n if (!ariaLabel && !ariaLabelledBy) {\n console.warn(\n \"SegmentedControl: no accessible name provided. Pass an `aria-label` or `aria-labelledby` prop.\",\n );\n }\n }\n}\n\nfunction warnMissingOptionAccessibleName(options: SegmentedControlOption[]) {\n if (process.env.NODE_ENV !== \"production\") {\n for (const option of options) {\n if (option.icon && !option.label?.trim()) {\n console.warn(\n `SegmentedControl: icon-only segment \"${option.value}\" is missing a non-empty \\`label\\` to use as its accessible name.`,\n );\n }\n }\n }\n}\n\nfunction getSegmentClassName({\n appearance,\n size,\n variant,\n isSelected,\n disabled,\n}: {\n appearance: SegmentedControlAppearance;\n size: SegmentedControlSize;\n variant: SegmentedControlVariant;\n isSelected: boolean;\n disabled: boolean;\n}) {\n return cn(\n \"relative inline-flex min-w-0 cursor-pointer items-center justify-center rounded-full\",\n \"motion-safe:transition-colors motion-safe:duration-150 motion-safe:ease-in-out\",\n \"focus-visible:shadow-focus-ring focus-visible:outline-none\",\n variant === \"fill\" ? \"flex-1\" : \"shrink-0\",\n appearance === \"plain\"\n ? cn(\n // Padding + negative margin enlarge the hit target without changing the\n // visual footprint, which must stay glyph-only to match the design.\n \"-m-1 p-1\",\n isSelected ? \"text-icons-primary\" : \"text-icons-tertiary hover:text-icons-primary\",\n )\n : cn(\n sizeClasses[size],\n isSelected\n ? \"bg-buttons-primary-default text-content-primary-inverted shadow-sm\"\n : \"text-content-primary hover:bg-buttons-switch-hover\",\n ),\n disabled && \"pointer-events-none\",\n );\n}\n\n/**\n * A compact selector for choosing between two or three mutually exclusive\n * options where the choice affects the content immediately below it. Use\n * instead of tabs when the options are more like settings or filters than\n * navigation, such as toggling a list/grid view or a monthly/annual price.\n *\n * Rendered as a `radiogroup` with roving-tabindex keyboard navigation. Supports\n * both controlled and uncontrolled usage.\n *\n * @example\n * ```tsx\n * <SegmentedControl\n * options={[\n * { label: \"Net\", value: \"net\" },\n * { label: \"Gross\", value: \"gross\" },\n * ]}\n * value={amount}\n * onChange={setAmount}\n * aria-label=\"Amount type\"\n * />\n * ```\n *\n * @example Icon-only segments (e.g. a list/grid view toggle)\n * ```tsx\n * <SegmentedControl\n * appearance=\"plain\"\n * options={[\n * { label: \"List view\", value: \"list\", icon: <ListViewIcon size={16} /> },\n * { label: \"Grid view\", value: \"grid\", icon: <GridViewIcon size={16} /> },\n * ]}\n * value={view}\n * onChange={setView}\n * aria-label=\"View\"\n * />\n * ```\n */\nexport const SegmentedControl = React.forwardRef<HTMLDivElement, SegmentedControlProps>(\n (\n {\n className,\n size = \"32\",\n variant = \"hug\",\n appearance = \"pill\",\n options,\n value: controlledValue,\n defaultValue,\n onChange,\n disabled = false,\n ...props\n },\n ref,\n ) => {\n warnMissingAccessibleName(props[\"aria-label\"], props[\"aria-labelledby\"]);\n warnMissingOptionAccessibleName(options);\n\n // Tracks selection for uncontrolled usage; ignored when `value` prop is provided\n const [internalValue, setInternalValue] = React.useState(defaultValue ?? options[0]?.value);\n const isControlled = controlledValue !== undefined;\n const currentValue = isControlled ? controlledValue : internalValue;\n const anySelected = options.some((o) => o.value === currentValue);\n const buttonRefs = React.useRef<(HTMLButtonElement | null)[]>([]);\n\n const handleSelect = (optionValue: string) => {\n if (disabled || optionValue === currentValue) return;\n if (!isControlled) {\n setInternalValue(optionValue);\n }\n onChange?.(optionValue);\n };\n\n const handleKeyDown = (e: React.KeyboardEvent, index: number) => {\n const nextIndex =\n e.key === \"ArrowRight\" || e.key === \"ArrowDown\"\n ? (index + 1) % options.length\n : e.key === \"ArrowLeft\" || e.key === \"ArrowUp\"\n ? (index - 1 + options.length) % options.length\n : null;\n if (nextIndex === null) return;\n e.preventDefault();\n const nextOption = options[nextIndex] as SegmentedControlOption;\n handleSelect(nextOption.value);\n buttonRefs.current[nextIndex]?.focus();\n };\n\n return (\n <div\n ref={ref}\n role=\"radiogroup\"\n className={cn(\n \"relative items-center rounded-full\",\n variant === \"fill\" ? \"flex w-full\" : \"inline-flex\",\n appearance === \"plain\" ? \"gap-2\" : \"bg-surface-tertiary p-1\",\n disabled && \"cursor-not-allowed opacity-50\",\n className,\n )}\n {...props}\n >\n {options.map((option, index) => {\n const isSelected = currentValue === option.value;\n return (\n // biome-ignore lint/a11y/useSemanticElements: native radio inputs only allow Tab-focus on the checked item; buttons with roving tabindex give full keyboard navigation\n <button\n key={option.value}\n ref={(el) => {\n buttonRefs.current[index] = el;\n }}\n type=\"button\"\n role=\"radio\"\n aria-checked={isSelected}\n tabIndex={isSelected || (!anySelected && index === 0) ? 0 : -1}\n disabled={disabled}\n aria-label={option.icon ? option.label : undefined}\n onClick={() => handleSelect(option.value)}\n onKeyDown={(e) => handleKeyDown(e, index)}\n className={getSegmentClassName({ appearance, size, variant, isSelected, disabled })}\n >\n {option.icon ? (\n <span className=\"flex shrink-0 items-center justify-center\" aria-hidden=\"true\">\n {option.icon}\n </span>\n ) : (\n <span className=\"min-w-0 truncate\">{option.label}</span>\n )}\n </button>\n );\n })}\n </div>\n );\n },\n);\n\nSegmentedControl.displayName = \"SegmentedControl\";\n"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -332,6 +332,54 @@ export declare interface AudioFileRejection {
|
|
|
332
332
|
errors: AudioValidationError[];
|
|
333
333
|
}
|
|
334
334
|
|
|
335
|
+
/**
|
|
336
|
+
* A compact playback control for a single audio clip: a play/pause toggle, a
|
|
337
|
+
* static amplitude waveform that doubles as a seek scrubber, and elapsed/total
|
|
338
|
+
* timestamps. Designed to sit as an overlay on media thumbnails (Vault cards)
|
|
339
|
+
* as well as inline rows (the AI Voice Message "Generated Audio" row).
|
|
340
|
+
*
|
|
341
|
+
* The waveform is seeded from the decoded audio when possible, falling back
|
|
342
|
+
* to a deterministic (not random) placeholder derived from `src` so the
|
|
343
|
+
* result is stable across renders, SSR, and Chromatic snapshots.
|
|
344
|
+
*
|
|
345
|
+
* @example
|
|
346
|
+
* ```tsx
|
|
347
|
+
* <AudioPlayer src="https://example.com/clip.mp3" duration={5} />
|
|
348
|
+
* ```
|
|
349
|
+
*/
|
|
350
|
+
export declare const AudioPlayer: React_2.ForwardRefExoticComponent<AudioPlayerProps & React_2.RefAttributes<HTMLDivElement>>;
|
|
351
|
+
|
|
352
|
+
export declare interface AudioPlayerProps extends Omit<React_2.HTMLAttributes<HTMLDivElement>, "onPlay" | "onPause" | "onEnded"> {
|
|
353
|
+
/** URL of the audio file to play. */
|
|
354
|
+
src: string;
|
|
355
|
+
/**
|
|
356
|
+
* Total duration in seconds, used to render the timestamp before the
|
|
357
|
+
* media's own metadata has loaded (and as a fallback if it never does).
|
|
358
|
+
*/
|
|
359
|
+
duration?: number;
|
|
360
|
+
/**
|
|
361
|
+
* Whether playback is active (controlled). Note: once `onEnded` fires, the
|
|
362
|
+
* browser has already stopped native playback. To loop or replay under
|
|
363
|
+
* controlled usage, toggle this prop `false` then `true` (rather than
|
|
364
|
+
* leaving it `true`) — the sync effect only calls `play()` again when this
|
|
365
|
+
* value actually changes.
|
|
366
|
+
*/
|
|
367
|
+
playing?: boolean;
|
|
368
|
+
/** Initial playback state (uncontrolled). @default false */
|
|
369
|
+
defaultPlaying?: boolean;
|
|
370
|
+
/** Called when playback starts. */
|
|
371
|
+
onPlay?: () => void;
|
|
372
|
+
/** Called when playback is paused. */
|
|
373
|
+
onPause?: () => void;
|
|
374
|
+
/** Called when playback reaches the end of the media. */
|
|
375
|
+
onEnded?: () => void;
|
|
376
|
+
/** Height of the player row, in pixels. @default "40" */
|
|
377
|
+
size?: AudioPlayerSize;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/** Height of the audio player row, in pixels. Matches both the Vault card overlay and the "Generated Audio" modal row in Figma — both use an identical 32px play button and 40px row. */
|
|
381
|
+
export declare type AudioPlayerSize = "40";
|
|
382
|
+
|
|
335
383
|
/**
|
|
336
384
|
* Audio file upload with drag-and-drop and optional in-browser recording.
|
|
337
385
|
* Supports file validation, multiple files, and real-time waveform visualization during recording.
|
|
@@ -1812,6 +1860,8 @@ export declare interface DialogContentProps extends React_2.ComponentPropsWithou
|
|
|
1812
1860
|
* @default "sheet"
|
|
1813
1861
|
*/
|
|
1814
1862
|
mobilePresentation?: "sheet" | "card";
|
|
1863
|
+
/** Props forwarded to the default {@link DialogOverlay} when `overlay` is `true`. */
|
|
1864
|
+
overlayProps?: DialogOverlayProps;
|
|
1815
1865
|
}
|
|
1816
1866
|
|
|
1817
1867
|
/** Accessible description for the dialog. Rendered as secondary text. */
|
|
@@ -2603,6 +2653,30 @@ export declare const FlashIcon: React_2.ForwardRefExoticComponent<BaseIconProps
|
|
|
2603
2653
|
/** Props for {@link FlashIcon}. See {@link BaseIconProps} for the shared shape. */
|
|
2604
2654
|
export declare type FlashIconProps = BaseIconProps;
|
|
2605
2655
|
|
|
2656
|
+
/**
|
|
2657
|
+
* A circular, elevated button for the single most important action on a
|
|
2658
|
+
* screen (e.g. creating new content). Unlike {@link IconButton}, it always
|
|
2659
|
+
* floats above page content with a drop shadow and is meant to be
|
|
2660
|
+
* positioned by the consumer (e.g. `fixed bottom-6 right-6`) rather than
|
|
2661
|
+
* inline in a toolbar or action row.
|
|
2662
|
+
*
|
|
2663
|
+
* Requires an accessible name — pass `aria-label` or `aria-labelledby`, since
|
|
2664
|
+
* the icon alone doesn't convey the action to screen readers.
|
|
2665
|
+
*
|
|
2666
|
+
* @example
|
|
2667
|
+
* ```tsx
|
|
2668
|
+
* <FloatingActionButton aria-label="Add content" className="fixed right-6 bottom-6">
|
|
2669
|
+
* <AddIcon />
|
|
2670
|
+
* </FloatingActionButton>
|
|
2671
|
+
* ```
|
|
2672
|
+
*/
|
|
2673
|
+
export declare const FloatingActionButton: React_2.ForwardRefExoticComponent<FloatingActionButtonProps & React_2.RefAttributes<HTMLButtonElement>>;
|
|
2674
|
+
|
|
2675
|
+
export declare interface FloatingActionButtonProps extends React_2.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
2676
|
+
/** Icon to render inside the button. Pass an icon component (e.g. `<AddIcon />`). */
|
|
2677
|
+
children: React_2.ReactNode;
|
|
2678
|
+
}
|
|
2679
|
+
|
|
2606
2680
|
/**
|
|
2607
2681
|
* Folder icon. Renders at sizes 16, 24, or 32 px with outlined and filled variants.
|
|
2608
2682
|
*
|
|
@@ -2705,6 +2779,19 @@ export declare const GoogleIcon: React_2.ForwardRefExoticComponent<React_2.SVGAt
|
|
|
2705
2779
|
className?: string;
|
|
2706
2780
|
} & React_2.RefAttributes<SVGSVGElement>>;
|
|
2707
2781
|
|
|
2782
|
+
/**
|
|
2783
|
+
* Grid view icon. Renders at sizes 16, 24, or 32 px.
|
|
2784
|
+
*
|
|
2785
|
+
* @example
|
|
2786
|
+
* ```tsx
|
|
2787
|
+
* <GridViewIcon size={24} />
|
|
2788
|
+
* ```
|
|
2789
|
+
*/
|
|
2790
|
+
export declare const GridViewIcon: React_2.ForwardRefExoticComponent<BaseIconProps & React_2.RefAttributes<SVGSVGElement>>;
|
|
2791
|
+
|
|
2792
|
+
/** Props for {@link GridViewIcon}. See {@link BaseIconProps} for the shared shape. */
|
|
2793
|
+
export declare type GridViewIconProps = BaseIconProps;
|
|
2794
|
+
|
|
2708
2795
|
/**
|
|
2709
2796
|
* Health icon. Renders at sizes 16, 24, or 32 px with outlined and filled variants.
|
|
2710
2797
|
*
|
|
@@ -3054,6 +3141,19 @@ export declare type LinkSize = "16" | "14";
|
|
|
3054
3141
|
/** Visual style variant of the link. */
|
|
3055
3142
|
export declare type LinkVariant = "primary" | "brand";
|
|
3056
3143
|
|
|
3144
|
+
/**
|
|
3145
|
+
* List view icon. Renders at sizes 16, 24, or 32 px.
|
|
3146
|
+
*
|
|
3147
|
+
* @example
|
|
3148
|
+
* ```tsx
|
|
3149
|
+
* <ListViewIcon size={24} />
|
|
3150
|
+
* ```
|
|
3151
|
+
*/
|
|
3152
|
+
export declare const ListViewIcon: React_2.ForwardRefExoticComponent<BaseIconProps & React_2.RefAttributes<SVGSVGElement>>;
|
|
3153
|
+
|
|
3154
|
+
/** Props for {@link ListViewIcon}. See {@link BaseIconProps} for the shared shape. */
|
|
3155
|
+
export declare type ListViewIconProps = BaseIconProps;
|
|
3156
|
+
|
|
3057
3157
|
/**
|
|
3058
3158
|
* A layout-aware loading indicator that renders a centered `SpinnerIcon` inside
|
|
3059
3159
|
* a relatively-positioned container. Drop-in replacement for the Olympus `Loader`.
|
|
@@ -4071,15 +4171,46 @@ export declare type SearchIconProps = BaseIconProps;
|
|
|
4071
4171
|
* aria-label="Amount type"
|
|
4072
4172
|
* />
|
|
4073
4173
|
* ```
|
|
4174
|
+
*
|
|
4175
|
+
* @example Icon-only segments (e.g. a list/grid view toggle)
|
|
4176
|
+
* ```tsx
|
|
4177
|
+
* <SegmentedControl
|
|
4178
|
+
* appearance="plain"
|
|
4179
|
+
* options={[
|
|
4180
|
+
* { label: "List view", value: "list", icon: <ListViewIcon size={16} /> },
|
|
4181
|
+
* { label: "Grid view", value: "grid", icon: <GridViewIcon size={16} /> },
|
|
4182
|
+
* ]}
|
|
4183
|
+
* value={view}
|
|
4184
|
+
* onChange={setView}
|
|
4185
|
+
* aria-label="View"
|
|
4186
|
+
* />
|
|
4187
|
+
* ```
|
|
4074
4188
|
*/
|
|
4075
4189
|
export declare const SegmentedControl: React_2.ForwardRefExoticComponent<SegmentedControlProps & React_2.RefAttributes<HTMLDivElement>>;
|
|
4076
4190
|
|
|
4191
|
+
/**
|
|
4192
|
+
* Visual style of the control.
|
|
4193
|
+
* - `"pill"`: the container has a muted background and the selected segment shows a filled pill (default).
|
|
4194
|
+
* - `"plain"`: no container or selected-pill background; segments are bare content and selection is
|
|
4195
|
+
* communicated by color alone. Designed for icon-only toggles (e.g. a list/grid view switch).
|
|
4196
|
+
*/
|
|
4197
|
+
export declare type SegmentedControlAppearance = "pill" | "plain";
|
|
4198
|
+
|
|
4077
4199
|
/** Describes one selectable segment. */
|
|
4078
4200
|
export declare interface SegmentedControlOption {
|
|
4079
|
-
/**
|
|
4201
|
+
/**
|
|
4202
|
+
* Display label for the segment. When `icon` is provided, the segment renders icon-only and
|
|
4203
|
+
* this value is used as its accessible name (applied as `aria-label`) instead of visible text.
|
|
4204
|
+
*/
|
|
4080
4205
|
label: string;
|
|
4081
4206
|
/** Value identifier returned via `onChange`. */
|
|
4082
4207
|
value: string;
|
|
4208
|
+
/**
|
|
4209
|
+
* Icon to render instead of the visible label. When set, the segment renders icon-only and
|
|
4210
|
+
* `label` becomes required as its accessible name — it is applied as `aria-label` and no
|
|
4211
|
+
* visible text is rendered.
|
|
4212
|
+
*/
|
|
4213
|
+
icon?: React_2.ReactNode;
|
|
4083
4214
|
}
|
|
4084
4215
|
|
|
4085
4216
|
export declare interface SegmentedControlProps extends Omit<React_2.HTMLAttributes<HTMLDivElement>, "onChange"> {
|
|
@@ -4087,6 +4218,8 @@ export declare interface SegmentedControlProps extends Omit<React_2.HTMLAttribut
|
|
|
4087
4218
|
size?: SegmentedControlSize;
|
|
4088
4219
|
/** Segment layout. @default "hug" */
|
|
4089
4220
|
variant?: SegmentedControlVariant;
|
|
4221
|
+
/** Visual style of the control. @default "pill" */
|
|
4222
|
+
appearance?: SegmentedControlAppearance;
|
|
4090
4223
|
/** The selectable segments. Designed for two or three mutually exclusive options. */
|
|
4091
4224
|
options: SegmentedControlOption[];
|
|
4092
4225
|
/** Currently selected value (controlled). */
|
package/dist/index.mjs
CHANGED
|
@@ -5,6 +5,7 @@ import { AccordionContent } from "./components/Accordion/AccordionContent.mjs";
|
|
|
5
5
|
import { AccordionItem } from "./components/Accordion/AccordionItem.mjs";
|
|
6
6
|
import { AccordionTrigger } from "./components/Accordion/AccordionTrigger.mjs";
|
|
7
7
|
import { Alert } from "./components/Alert/Alert.mjs";
|
|
8
|
+
import { AudioPlayer } from "./components/AudioPlayer/AudioPlayer.mjs";
|
|
8
9
|
import { AudioUpload } from "./components/AudioUpload/AudioUpload.mjs";
|
|
9
10
|
import { useAudioRecorder } from "./components/AudioUpload/useAudioRecorder.mjs";
|
|
10
11
|
import { Autocomplete } from "./components/Autocomplete/Autocomplete.mjs";
|
|
@@ -30,6 +31,7 @@ import { Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, Dr
|
|
|
30
31
|
import { DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuHeader, DropdownMenuItem, DropdownMenuLabel, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuTrigger } from "./components/DropdownMenu/DropdownMenu.mjs";
|
|
31
32
|
import { EmptyState } from "./components/EmptyState/EmptyState.mjs";
|
|
32
33
|
import { FanFollowerCount } from "./components/FanFollowerCount/FanFollowerCount.mjs";
|
|
34
|
+
import { FloatingActionButton } from "./components/FloatingActionButton/FloatingActionButton.mjs";
|
|
33
35
|
import { IconButton } from "./components/IconButton/IconButton.mjs";
|
|
34
36
|
import { AddIcon } from "./components/Icons/AddIcon.mjs";
|
|
35
37
|
import { AffiliatesIcon } from "./components/Icons/AffiliatesIcon.mjs";
|
|
@@ -103,6 +105,7 @@ import { GenderIcon } from "./components/Icons/GenderIcon.mjs";
|
|
|
103
105
|
import { GifIcon } from "./components/Icons/GifIcon.mjs";
|
|
104
106
|
import { GiftIcon } from "./components/Icons/GiftIcon.mjs";
|
|
105
107
|
import { GoogleIcon } from "./components/Icons/GoogleIcon.mjs";
|
|
108
|
+
import { GridViewIcon } from "./components/Icons/GridViewIcon.mjs";
|
|
106
109
|
import { HealthIcon } from "./components/Icons/HealthIcon.mjs";
|
|
107
110
|
import { HeartIcon } from "./components/Icons/HeartIcon.mjs";
|
|
108
111
|
import { HelpIcon } from "./components/Icons/HelpIcon.mjs";
|
|
@@ -115,6 +118,7 @@ import { InfoCircleIcon } from "./components/Icons/InfoCircleIcon.mjs";
|
|
|
115
118
|
import { InfoIcon } from "./components/Icons/InfoIcon.mjs";
|
|
116
119
|
import { LanguageIcon } from "./components/Icons/LanguageIcon.mjs";
|
|
117
120
|
import { LinkIcon } from "./components/Icons/LinkIcon.mjs";
|
|
121
|
+
import { ListViewIcon } from "./components/Icons/ListViewIcon.mjs";
|
|
118
122
|
import { LocationIcon } from "./components/Icons/LocationIcon.mjs";
|
|
119
123
|
import { LockerIcon } from "./components/Icons/LockerIcon.mjs";
|
|
120
124
|
import { LockerOffIcon } from "./components/Icons/LockerOffIcon.mjs";
|
|
@@ -262,6 +266,7 @@ export {
|
|
|
262
266
|
ArrowUpIcon,
|
|
263
267
|
ArrowUpRightIcon,
|
|
264
268
|
AtSignIcon,
|
|
269
|
+
AudioPlayer,
|
|
265
270
|
AudioUpload,
|
|
266
271
|
AutoMessageIcon,
|
|
267
272
|
Autocomplete,
|
|
@@ -375,6 +380,7 @@ export {
|
|
|
375
380
|
FlagIcon,
|
|
376
381
|
FlameIcon,
|
|
377
382
|
FlashIcon,
|
|
383
|
+
FloatingActionButton,
|
|
378
384
|
FolderIcon,
|
|
379
385
|
ForwardIcon,
|
|
380
386
|
GalleryIcon,
|
|
@@ -383,6 +389,7 @@ export {
|
|
|
383
389
|
GifIcon,
|
|
384
390
|
GiftIcon,
|
|
385
391
|
GoogleIcon,
|
|
392
|
+
GridViewIcon,
|
|
386
393
|
HealthIcon,
|
|
387
394
|
HeartIcon,
|
|
388
395
|
HelpIcon,
|
|
@@ -401,6 +408,7 @@ export {
|
|
|
401
408
|
LanguageIcon,
|
|
402
409
|
Link,
|
|
403
410
|
LinkIcon,
|
|
411
|
+
ListViewIcon,
|
|
404
412
|
Loader,
|
|
405
413
|
LocationIcon,
|
|
406
414
|
LockerIcon,
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
package/dist/styles/base.css
CHANGED
|
@@ -136,6 +136,21 @@
|
|
|
136
136
|
animation: accordion-collapse 200ms ease-out;
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
+
/*
|
|
140
|
+
* `dvh` reflects the browser chrome actually on screen (e.g. iOS in-app browsers
|
|
141
|
+
* like Instagram, which report a `vh` taller than what's visible once chrome +
|
|
142
|
+
* home-indicator inset are subtracted). `dvh` support is what's missing on older
|
|
143
|
+
* engines, not `vh`, so the fallback must be declared second: an unsupported unit
|
|
144
|
+
* makes only that declaration invalid, and the browser keeps the prior valid one.
|
|
145
|
+
* This has to stay a single CSS rule with two declarations — two separate Tailwind
|
|
146
|
+
* utility classes (`max-h-[85vh] max-h-[85dvh]`) share one `tailwind-merge` conflict
|
|
147
|
+
* group, so `cn()`/`twMerge` would drop the `vh` one and destroy the fallback.
|
|
148
|
+
*/
|
|
149
|
+
@utility dialog-max-h-dynamic {
|
|
150
|
+
max-height: 85vh;
|
|
151
|
+
max-height: 85dvh;
|
|
152
|
+
}
|
|
153
|
+
|
|
139
154
|
/* Static tokens not in Figma — manually maintained for now */
|
|
140
155
|
@utility typography-medium-caption-xs {
|
|
141
156
|
font-size: 0.625rem;
|