@giddaa-housing/ui 3.6.0 → 3.8.0
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/css/generated/shared.css +1 -1
- package/css/theme.css +11 -8
- package/dist/button.js +4 -2
- package/dist/calendar.js +1 -1
- package/dist/{combobox-CACVdp1R.js → combobox-D7QdQzN8.js} +76 -10
- package/dist/combobox.d.ts +2 -2
- package/dist/combobox.js +1 -1
- package/dist/data-table.d.ts +1 -1
- package/dist/date-picker.d.ts +1 -1
- package/dist/dialog.d.ts +4 -2
- package/dist/dialog.js +10 -11
- package/dist/field.d.ts +3 -2
- package/dist/field.js +9 -2
- package/dist/infotip.js +0 -1
- package/dist/input-group.js +5 -5
- package/dist/label.d.ts +2 -1
- package/dist/label.js +12 -24
- package/dist/match.d.ts +11 -2
- package/dist/match.js +49 -32
- package/dist/media-player.js +3 -3
- package/dist/mobile-sidebar.js +7 -7
- package/dist/phone-input.d.ts +18 -1
- package/dist/phone-input.js +133 -64
- package/dist/{picker-j3txxA5t.d.ts → picker-qxWi9wZ1.d.ts} +1 -1
- package/dist/{popover-CNymD33m.d.ts → popover-BMTZp3AQ.d.ts} +2 -2
- package/dist/popover.d.ts +1 -1
- package/dist/popover.js +2 -1
- package/dist/rating.d.ts +60 -0
- package/dist/rating.js +187 -0
- package/dist/select.js +38 -3
- package/dist/sheet.d.ts +3 -1
- package/dist/sheet.js +10 -11
- package/dist/sonar.d.ts +3 -1
- package/dist/sonar.js +116 -28
- package/dist/styles.css +180 -30
- package/dist/time-picker.d.ts +1 -1
- package/dist/toast.js +3 -3
- package/dist/video-player-dialog.js +1 -1
- package/package.json +5 -1
- package/dist/dialog-nesting.d.ts +0 -9
- package/dist/dialog-nesting.js +0 -27
package/dist/match.js
CHANGED
|
@@ -3,7 +3,7 @@ import { ChevronDown } from "./icons.js";
|
|
|
3
3
|
import { cn } from "./utils/cn.js";
|
|
4
4
|
import { resolveResponsiveComponentSize, useComponentSizeValue } from "./size-context.js";
|
|
5
5
|
import { CircularProgress } from "./progress.js";
|
|
6
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
7
7
|
import { cva } from "class-variance-authority";
|
|
8
8
|
//#region src/match.tsx
|
|
9
9
|
const matchVariants = cva("inline-flex w-fit shrink-0 items-center rounded-full border border-line-subtle bg-surface text-fg-primary shadow-none transition-[background-color,border-color,box-shadow] outline-none focus-visible:border-line-focus focus-visible:shadow-[0px_0px_0px_2px_var(--color-line-focus)] disabled:pointer-events-none disabled:opacity-60", {
|
|
@@ -83,54 +83,71 @@ function normalizeMatchValue(value) {
|
|
|
83
83
|
if (!Number.isFinite(value)) return 0;
|
|
84
84
|
return Math.min(100, Math.max(0, Math.round(value)));
|
|
85
85
|
}
|
|
86
|
-
function Match({ className, value, variant = "bar", size, label = "Match", showChevron
|
|
86
|
+
function Match({ className, value, variant = "bar", size, label = "Match", showChevron, readOnly, type = "button", disabled, onClick, ref, "aria-label": ariaLabel, ...props }) {
|
|
87
87
|
const sizeValue = useComponentSizeValue(size);
|
|
88
88
|
const resolvedSizes = resolveResponsiveComponentSize(sizeValue);
|
|
89
89
|
const responsive = typeof sizeValue !== "string";
|
|
90
90
|
const resolvedVariant = variant ?? "bar";
|
|
91
91
|
const progress = normalizeMatchValue(value);
|
|
92
92
|
const text = `${progress}% ${label}`;
|
|
93
|
+
const interactive = readOnly === void 0 ? onClick !== void 0 : !readOnly;
|
|
94
|
+
const chevron = showChevron ?? interactive;
|
|
93
95
|
const rootSizeClasses = cn(matchVariants({
|
|
94
96
|
variant: resolvedVariant,
|
|
95
97
|
size: resolvedSizes.base
|
|
96
98
|
}), responsive && matchBreakpointClasses(resolvedVariant, "md", resolvedSizes.md), responsive && matchBreakpointClasses(resolvedVariant, "lg", resolvedSizes.lg));
|
|
97
|
-
|
|
98
|
-
type,
|
|
99
|
+
const rootProps = {
|
|
99
100
|
"aria-label": ariaLabel ?? text,
|
|
100
101
|
"data-slot": "match",
|
|
101
102
|
"data-variant": resolvedVariant,
|
|
102
103
|
"data-size": resolvedSizes.base,
|
|
103
104
|
"data-size-md": responsive ? resolvedSizes.md : void 0,
|
|
104
105
|
"data-size-lg": responsive ? resolvedSizes.lg : void 0,
|
|
105
|
-
className: cn(rootSizeClasses, className)
|
|
106
|
+
className: cn(rootSizeClasses, className)
|
|
107
|
+
};
|
|
108
|
+
const content = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
109
|
+
resolvedVariant === "donut" ? /* @__PURE__ */ jsx(MatchDonut, {
|
|
110
|
+
progress,
|
|
111
|
+
sizes: resolvedSizes,
|
|
112
|
+
responsive
|
|
113
|
+
}) : null,
|
|
114
|
+
/* @__PURE__ */ jsx("span", {
|
|
115
|
+
"data-slot": "match-label",
|
|
116
|
+
className: "shrink-0 font-bold",
|
|
117
|
+
children: text
|
|
118
|
+
}),
|
|
119
|
+
resolvedVariant === "bar" ? /* @__PURE__ */ jsx("span", {
|
|
120
|
+
"aria-hidden": "true",
|
|
121
|
+
"data-slot": "match-bar-track",
|
|
122
|
+
className: cn("relative shrink-0 overflow-hidden rounded-full bg-surface-raised", matchBarSizes[resolvedSizes.base], responsive && responsiveClass("md", matchBarSizes[resolvedSizes.md]), responsive && responsiveClass("lg", matchBarSizes[resolvedSizes.lg])),
|
|
123
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
124
|
+
"data-slot": "match-bar-fill",
|
|
125
|
+
className: "absolute inset-y-0 left-0 rounded-full bg-action-primary",
|
|
126
|
+
style: { width: `${progress}%` }
|
|
127
|
+
})
|
|
128
|
+
}) : null,
|
|
129
|
+
chevron ? /* @__PURE__ */ jsx(ChevronDown, {
|
|
130
|
+
"aria-hidden": "true",
|
|
131
|
+
"data-slot": "match-chevron",
|
|
132
|
+
className: cn("shrink-0 text-fg-secondary", matchChevronSizes[resolvedSizes.base], responsive && responsiveClass("md", matchChevronSizes[resolvedSizes.md]), responsive && responsiveClass("lg", matchChevronSizes[resolvedSizes.lg]))
|
|
133
|
+
}) : null
|
|
134
|
+
] });
|
|
135
|
+
if (!interactive) return /* @__PURE__ */ jsx("span", {
|
|
136
|
+
...rootProps,
|
|
137
|
+
"data-readonly": "true",
|
|
138
|
+
role: "img",
|
|
139
|
+
ref,
|
|
140
|
+
...props,
|
|
141
|
+
children: content
|
|
142
|
+
});
|
|
143
|
+
return /* @__PURE__ */ jsx("button", {
|
|
144
|
+
...rootProps,
|
|
145
|
+
type,
|
|
146
|
+
disabled,
|
|
147
|
+
onClick,
|
|
148
|
+
ref,
|
|
106
149
|
...props,
|
|
107
|
-
children:
|
|
108
|
-
resolvedVariant === "donut" ? /* @__PURE__ */ jsx(MatchDonut, {
|
|
109
|
-
progress,
|
|
110
|
-
sizes: resolvedSizes,
|
|
111
|
-
responsive
|
|
112
|
-
}) : null,
|
|
113
|
-
/* @__PURE__ */ jsx("span", {
|
|
114
|
-
"data-slot": "match-label",
|
|
115
|
-
className: "shrink-0 font-bold",
|
|
116
|
-
children: text
|
|
117
|
-
}),
|
|
118
|
-
resolvedVariant === "bar" ? /* @__PURE__ */ jsx("span", {
|
|
119
|
-
"aria-hidden": "true",
|
|
120
|
-
"data-slot": "match-bar-track",
|
|
121
|
-
className: cn("relative shrink-0 overflow-hidden rounded-full bg-surface-raised", matchBarSizes[resolvedSizes.base], responsive && responsiveClass("md", matchBarSizes[resolvedSizes.md]), responsive && responsiveClass("lg", matchBarSizes[resolvedSizes.lg])),
|
|
122
|
-
children: /* @__PURE__ */ jsx("span", {
|
|
123
|
-
"data-slot": "match-bar-fill",
|
|
124
|
-
className: "absolute inset-y-0 left-0 rounded-full bg-action-primary",
|
|
125
|
-
style: { width: `${progress}%` }
|
|
126
|
-
})
|
|
127
|
-
}) : null,
|
|
128
|
-
showChevron ? /* @__PURE__ */ jsx(ChevronDown, {
|
|
129
|
-
"aria-hidden": "true",
|
|
130
|
-
"data-slot": "match-chevron",
|
|
131
|
-
className: cn("shrink-0 text-fg-secondary", matchChevronSizes[resolvedSizes.base], responsive && responsiveClass("md", matchChevronSizes[resolvedSizes.md]), responsive && responsiveClass("lg", matchChevronSizes[resolvedSizes.lg]))
|
|
132
|
-
}) : null
|
|
133
|
-
]
|
|
150
|
+
children: content
|
|
134
151
|
});
|
|
135
152
|
}
|
|
136
153
|
const responsiveSizeClasses = {
|
package/dist/media-player.js
CHANGED
|
@@ -171,7 +171,7 @@ function MediaPlayerRoot({ audio, children, className, onBlurCapture, onFocusCap
|
|
|
171
171
|
"data-slot": "media-player",
|
|
172
172
|
role: "region",
|
|
173
173
|
tabIndex: 0,
|
|
174
|
-
className: cn("group/media-player relative isolate block w-full overflow-hidden rounded-2xl bg-fg-primary text-fg-on-brand shadow-2 outline-none", "data-[controls=hidden]:cursor-none", "data-[audio]:bg-[image:var(--gradient-accent-forest-to-brass)]", "data-[fullscreen]:rounded-none data-[fullscreen]:shadow-none", className),
|
|
174
|
+
className: cn("group/media-player relative isolate block w-full overflow-hidden rounded-2xl bg-fg-primary text-fg-on-brand shadow-2 outline-none [transform:translateZ(0)]", "data-[controls=hidden]:cursor-none", "data-[audio]:bg-[image:var(--gradient-accent-forest-to-brass)]", "data-[fullscreen]:rounded-none data-[fullscreen]:shadow-none", className),
|
|
175
175
|
...props,
|
|
176
176
|
onBlurCapture: (event) => {
|
|
177
177
|
onBlurCapture?.(event);
|
|
@@ -264,7 +264,7 @@ function MediaPlayerVideo({ className, ref, ...props }) {
|
|
|
264
264
|
setRef(ref, node);
|
|
265
265
|
}, [ref]),
|
|
266
266
|
"data-slot": "media-player-video",
|
|
267
|
-
className: cn("block aspect-video size-full
|
|
267
|
+
className: cn("block aspect-video size-full object-cover", "group-data-[fullscreen]/media-player:aspect-auto group-data-[fullscreen]/media-player:object-contain", className),
|
|
268
268
|
playsInline: true,
|
|
269
269
|
...props
|
|
270
270
|
});
|
|
@@ -891,7 +891,7 @@ function MediaPlayerError({ children, className, ...props }) {
|
|
|
891
891
|
return /* @__PURE__ */ jsx("div", {
|
|
892
892
|
"data-slot": "media-player-error",
|
|
893
893
|
role: "alert",
|
|
894
|
-
className: cn("absolute inset-0 z-20 flex flex-col items-center justify-center gap-1
|
|
894
|
+
className: cn("absolute inset-0 z-20 flex flex-col items-center justify-center gap-1 bg-canvas px-6 text-center text-fg-primary", className),
|
|
895
895
|
...props,
|
|
896
896
|
children: children ?? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
897
897
|
/* @__PURE__ */ jsx(TriangleAlert, {
|
package/dist/mobile-sidebar.js
CHANGED
|
@@ -27,8 +27,8 @@ function MobileSidebarContent({ className, showCloseButton = false, ...props })
|
|
|
27
27
|
side: "right",
|
|
28
28
|
size: "sm",
|
|
29
29
|
showCloseButton,
|
|
30
|
-
popupClassName: "h-dvh w-[
|
|
31
|
-
className: cn("h-dvh w-
|
|
30
|
+
popupClassName: "h-dvh w-full max-w-[24.375rem]",
|
|
31
|
+
className: cn("h-dvh w-full rounded-none border-y-0 border-r-0 bg-canvas [--sheet-padding-x:1.25rem] [--sheet-padding-top:0.8125rem]", className),
|
|
32
32
|
...props
|
|
33
33
|
});
|
|
34
34
|
}
|
|
@@ -81,7 +81,7 @@ function MobileSidebarNav({ className, ...props }) {
|
|
|
81
81
|
function MobileSidebarList({ className, ...props }) {
|
|
82
82
|
return /* @__PURE__ */ jsx("ul", {
|
|
83
83
|
"data-slot": "mobile-sidebar-list",
|
|
84
|
-
className: cn("flex flex-col", className),
|
|
84
|
+
className: cn("-mx-4 flex flex-col gap-1", className),
|
|
85
85
|
...props
|
|
86
86
|
});
|
|
87
87
|
}
|
|
@@ -97,7 +97,7 @@ function MobileSidebarLink({ active = false, icon, className, children, render,
|
|
|
97
97
|
defaultTagName: "a",
|
|
98
98
|
props: mergeProps({
|
|
99
99
|
"aria-current": active ? "page" : void 0,
|
|
100
|
-
className: cn("group/mobile-sidebar-link grid min-h-11 w-full grid-cols-1 items-center rounded-xl px-
|
|
100
|
+
className: cn("group/mobile-sidebar-link grid min-h-11 w-full grid-cols-1 items-center rounded-xl px-4 py-2.5 text-left text-gdt-md font-semibold text-fg-primary outline-none transition-colors hover:bg-surface focus-visible:shadow-[0_0_0_2px_var(--color-line-focus)]", active && "bg-surface-brand-subtle text-fg-brand hover:bg-surface-brand-subtle", className),
|
|
101
101
|
children: /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs("span", {
|
|
102
102
|
className: cn("grid min-w-0 items-center", icon ? "grid-cols-[1.25rem_minmax(0,1fr)] gap-3" : "grid-cols-1"),
|
|
103
103
|
children: [icon ? /* @__PURE__ */ jsx("span", {
|
|
@@ -127,7 +127,7 @@ function MobileSidebarGroupTrigger({ active = false, icon, className, children,
|
|
|
127
127
|
return /* @__PURE__ */ jsxs(CollapsibleTrigger, {
|
|
128
128
|
"data-slot": "mobile-sidebar-group-trigger",
|
|
129
129
|
"data-active": active || void 0,
|
|
130
|
-
className: cn("grid min-h-11 w-full grid-cols-[minmax(0,1fr)_auto] items-center gap-3 rounded-xl px-
|
|
130
|
+
className: cn("grid min-h-11 w-full grid-cols-[minmax(0,1fr)_auto] items-center gap-3 rounded-xl px-4 py-2.5 text-left text-gdt-md font-semibold text-fg-primary outline-none transition-colors hover:bg-surface focus-visible:shadow-[0_0_0_2px_var(--color-line-focus)]", active && "bg-surface-brand-subtle text-fg-brand hover:bg-surface-brand-subtle", className),
|
|
131
131
|
...props,
|
|
132
132
|
children: [/* @__PURE__ */ jsxs("span", {
|
|
133
133
|
className: cn("grid min-w-0 items-center", icon ? "grid-cols-[1.25rem_minmax(0,1fr)] gap-3" : "grid-cols-1"),
|
|
@@ -154,7 +154,7 @@ function MobileSidebarGroupPanel({ className, ...props }) {
|
|
|
154
154
|
function MobileSidebarSubList({ className, ...props }) {
|
|
155
155
|
return /* @__PURE__ */ jsx("ul", {
|
|
156
156
|
"data-slot": "mobile-sidebar-sub-list",
|
|
157
|
-
className: cn("ml-8 flex flex-col pb-1", className),
|
|
157
|
+
className: cn("ml-8 flex flex-col gap-0.5 pb-2 pt-1", className),
|
|
158
158
|
...props
|
|
159
159
|
});
|
|
160
160
|
}
|
|
@@ -168,7 +168,7 @@ function MobileSidebarSubheading({ className, ...props }) {
|
|
|
168
168
|
function MobileSidebarSubLink({ className, render, ...props }) {
|
|
169
169
|
return useRender({
|
|
170
170
|
defaultTagName: "a",
|
|
171
|
-
props: mergeProps({ className: cn("block rounded-lg px-
|
|
171
|
+
props: mergeProps({ className: cn("block rounded-lg px-4 py-1.5 text-gdt-sm font-medium text-fg-secondary outline-none transition-colors hover:bg-surface hover:text-fg-primary focus-visible:shadow-[0_0_0_2px_var(--color-line-focus)]", className) }, props),
|
|
172
172
|
render,
|
|
173
173
|
state: { slot: "mobile-sidebar-sub-link" }
|
|
174
174
|
});
|
package/dist/phone-input.d.ts
CHANGED
|
@@ -3,19 +3,36 @@ import * as React from "react";
|
|
|
3
3
|
import { CountryCode } from "libphonenumber-js";
|
|
4
4
|
//#region src/phone-input.d.ts
|
|
5
5
|
interface PhoneInputProps {
|
|
6
|
+
/** Controlled E.164 value, e.g. `"+2348012345678"`. */
|
|
6
7
|
value?: string;
|
|
7
8
|
onChange?: (value: string | undefined) => void;
|
|
9
|
+
/** Country the field starts on, before anything is typed. */
|
|
8
10
|
defaultCountry?: CountryCode;
|
|
9
11
|
size?: ComponentSizeValue;
|
|
10
12
|
disabled?: boolean;
|
|
11
13
|
readOnly?: boolean;
|
|
12
14
|
placeholder?: string;
|
|
15
|
+
/**
|
|
16
|
+
* Content pinned to the trailing edge of the field — a validity tick, a
|
|
17
|
+
* "Verify" button, an extension. It sits outside the number, so it never
|
|
18
|
+
* shifts as the number is formatted.
|
|
19
|
+
*/
|
|
20
|
+
rightAddon?: React.ReactNode;
|
|
13
21
|
className?: string;
|
|
14
22
|
id?: string;
|
|
15
23
|
name?: string;
|
|
16
24
|
ref?: React.Ref<HTMLInputElement>;
|
|
17
25
|
"aria-invalid"?: boolean;
|
|
18
26
|
}
|
|
19
|
-
|
|
27
|
+
/**
|
|
28
|
+
* A phone field that takes a number written either way round.
|
|
29
|
+
*
|
|
30
|
+
* `0801 234 5678` and `+234 801 234 5678` are the same number, and both are
|
|
31
|
+
* accepted here: typing or pasting a leading `+` switches the field to
|
|
32
|
+
* international entry and moves the country selector to match, while a plain
|
|
33
|
+
* national number is read against the selected country, trunk prefix and all.
|
|
34
|
+
* `onChange` receives E.164 in both cases.
|
|
35
|
+
*/
|
|
36
|
+
declare function PhoneInput({ value, onChange, defaultCountry, size, disabled, readOnly, placeholder, rightAddon, className, id, name, ref, "aria-invalid": ariaInvalid }: PhoneInputProps): React.JSX.Element;
|
|
20
37
|
//#endregion
|
|
21
38
|
export { type CountryCode, PhoneInput, type PhoneInputProps };
|
package/dist/phone-input.js
CHANGED
|
@@ -10,28 +10,87 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
10
10
|
import * as React from "react";
|
|
11
11
|
import { AsYouType, getCountryCallingCode, parsePhoneNumberFromString } from "libphonenumber-js";
|
|
12
12
|
import flags from "react-phone-number-input/flags";
|
|
13
|
-
//#region src/phone-input.
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
//#region src/lib/phone-input-value.ts
|
|
14
|
+
const EMPTY = {
|
|
15
|
+
text: "",
|
|
16
|
+
national: "",
|
|
17
|
+
international: false,
|
|
18
|
+
value: void 0
|
|
19
|
+
};
|
|
20
|
+
/** Drops everything a phone number cannot contain, keeping a leading `+`. */
|
|
21
|
+
function sanitizePhoneText(input) {
|
|
22
|
+
const digits = input.replace(/\D/g, "");
|
|
23
|
+
return input.trimStart().startsWith("+") ? `+${digits}` : digits;
|
|
17
24
|
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
25
|
+
/**
|
|
26
|
+
* Groups national digits that `AsYouType` handed back untouched.
|
|
27
|
+
*
|
|
28
|
+
* Most countries only publish a national format that assumes the trunk prefix,
|
|
29
|
+
* so `AsYouType("NG")` formats `08161827754` as `0816 182 7754` but hands
|
|
30
|
+
* `8161827754` straight back in one run — and it formats neither until enough
|
|
31
|
+
* digits have arrived. The same digits always group under the international
|
|
32
|
+
* pattern, so that grouping is borrowed and the calling code taken back off.
|
|
33
|
+
*
|
|
34
|
+
* Whatever the reader typed in front of the national digits is theirs, and goes
|
|
35
|
+
* back where they left it. This only ever inserts separators: typing `081`
|
|
36
|
+
* shows `081`, never `81`.
|
|
37
|
+
*/
|
|
38
|
+
function groupNationalDigits(sanitized, national, country) {
|
|
39
|
+
if (!national || !sanitized.endsWith(national)) return "";
|
|
40
|
+
const callingCode = `+${getCountryCallingCode(country)}`;
|
|
41
|
+
const international = new AsYouType().input(`${callingCode}${national}`);
|
|
42
|
+
if (!international.startsWith(callingCode)) return "";
|
|
43
|
+
return sanitized.slice(0, sanitized.length - national.length) + international.slice(callingCode.length).trimStart();
|
|
21
44
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
45
|
+
/**
|
|
46
|
+
* Reads what is in the field after a keystroke.
|
|
47
|
+
*
|
|
48
|
+
* `country` is the currently selected country; it is only overridden when the
|
|
49
|
+
* text is international *and* the digits so far name exactly one country —
|
|
50
|
+
* `+1` is still both the US and Canada, so the selector holds still until it
|
|
51
|
+
* is not.
|
|
52
|
+
*/
|
|
53
|
+
function derivePhoneValue(input, country) {
|
|
54
|
+
const sanitized = sanitizePhoneText(input);
|
|
55
|
+
const formatter = new AsYouType(country);
|
|
56
|
+
const formatted = formatter.input(sanitized);
|
|
57
|
+
const international = sanitized.startsWith("+");
|
|
58
|
+
const national = formatter.getNationalNumber();
|
|
59
|
+
return {
|
|
60
|
+
text: !international && formatted === sanitized && groupNationalDigits(sanitized, national, country) || formatted,
|
|
61
|
+
country: (international ? formatter.getCountry() : void 0) ?? country,
|
|
62
|
+
national,
|
|
63
|
+
international,
|
|
64
|
+
value: national ? formatter.getNumberValue() : void 0
|
|
65
|
+
};
|
|
25
66
|
}
|
|
26
|
-
|
|
27
|
-
|
|
67
|
+
/** Reads an externally supplied value — a form's default, a fetched record. */
|
|
68
|
+
function readPhoneValue(value, fallback) {
|
|
69
|
+
if (!value) return {
|
|
70
|
+
...EMPTY,
|
|
71
|
+
country: fallback
|
|
72
|
+
};
|
|
28
73
|
const parsed = parsePhoneNumberFromString(value);
|
|
29
|
-
if (parsed?.nationalNumber) return
|
|
30
|
-
|
|
31
|
-
|
|
74
|
+
if (!parsed?.nationalNumber) return derivePhoneValue(value, fallback);
|
|
75
|
+
if (!parsed.country) return derivePhoneValue(parsed.number, fallback);
|
|
76
|
+
return {
|
|
77
|
+
...derivePhoneValue(parsed.formatNational(), parsed.country),
|
|
78
|
+
country: parsed.country
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Rewrites the field for a country picked from the selector. The digits the
|
|
83
|
+
* reader typed are kept; only the code in front of them changes.
|
|
84
|
+
*/
|
|
85
|
+
function selectPhoneCountry(current, next) {
|
|
86
|
+
return {
|
|
87
|
+
...derivePhoneValue(current.international ? `+${getCountryCallingCode(next)}${current.national}` : current.national, next),
|
|
88
|
+
country: next
|
|
32
89
|
};
|
|
33
|
-
return { digits: "" };
|
|
34
90
|
}
|
|
91
|
+
//#endregion
|
|
92
|
+
//#region src/phone-input.tsx
|
|
93
|
+
const COUNTRIES = getPhoneCountries();
|
|
35
94
|
const flagSizeVariants = {
|
|
36
95
|
sm: "h-3 w-[18px]",
|
|
37
96
|
md: "h-3.5 w-5",
|
|
@@ -110,7 +169,7 @@ function CountryList({ items, country, onSelect }) {
|
|
|
110
169
|
})
|
|
111
170
|
});
|
|
112
171
|
}
|
|
113
|
-
function CountrySelect({ country, size, disabled, onSelect, finalFocusRef }) {
|
|
172
|
+
function CountrySelect({ country, size, disabled, showCallingCode, onSelect, finalFocusRef }) {
|
|
114
173
|
const [open, setOpen] = React.useState(false);
|
|
115
174
|
const [query, setQuery] = React.useState("");
|
|
116
175
|
const searchRef = React.useRef(null);
|
|
@@ -140,7 +199,7 @@ function CountrySelect({ country, size, disabled, onSelect, finalFocusRef }) {
|
|
|
140
199
|
country,
|
|
141
200
|
size
|
|
142
201
|
}),
|
|
143
|
-
/* @__PURE__ */ jsxs("span", { children: ["+", getCountryCallingCode(country)] }),
|
|
202
|
+
showCallingCode ? /* @__PURE__ */ jsxs("span", { children: ["+", getCountryCallingCode(country)] }) : null,
|
|
144
203
|
/* @__PURE__ */ jsx(ChevronDown, { className: cn("text-fg-secondary transition-transform", open && "rotate-180", size === "lg" ? "size-4" : "size-3.5") })
|
|
145
204
|
]
|
|
146
205
|
}), /* @__PURE__ */ jsxs(PopoverContent, {
|
|
@@ -167,7 +226,16 @@ function CountrySelect({ country, size, disabled, onSelect, finalFocusRef }) {
|
|
|
167
226
|
})]
|
|
168
227
|
});
|
|
169
228
|
}
|
|
170
|
-
|
|
229
|
+
/**
|
|
230
|
+
* A phone field that takes a number written either way round.
|
|
231
|
+
*
|
|
232
|
+
* `0801 234 5678` and `+234 801 234 5678` are the same number, and both are
|
|
233
|
+
* accepted here: typing or pasting a leading `+` switches the field to
|
|
234
|
+
* international entry and moves the country selector to match, while a plain
|
|
235
|
+
* national number is read against the selected country, trunk prefix and all.
|
|
236
|
+
* `onChange` receives E.164 in both cases.
|
|
237
|
+
*/
|
|
238
|
+
function PhoneInput({ value, onChange, defaultCountry = "NG", size, disabled = false, readOnly = false, placeholder = "(0000) 000-0000", rightAddon, className, id, name, ref, "aria-invalid": ariaInvalid }) {
|
|
171
239
|
const sizeValue = useComponentSizeValue(size);
|
|
172
240
|
const resolvedSize = resolveResponsiveComponentSize(sizeValue);
|
|
173
241
|
const inputRef = React.useRef(null);
|
|
@@ -176,63 +244,64 @@ function PhoneInput({ value, onChange, defaultCountry = "NG", size, disabled = f
|
|
|
176
244
|
if (typeof ref === "function") ref(node);
|
|
177
245
|
else if (ref) ref.current = node;
|
|
178
246
|
}, [ref]);
|
|
179
|
-
const
|
|
180
|
-
const [country, setCountry] = React.useState(initial.country ?? defaultCountry);
|
|
181
|
-
const [national, setNational] = React.useState(() => formatNational(initial.digits, initial.country ?? defaultCountry));
|
|
247
|
+
const [phone, setPhone] = React.useState(() => readPhoneValue(value, defaultCountry));
|
|
182
248
|
React.useEffect(() => {
|
|
183
|
-
if (
|
|
184
|
-
|
|
185
|
-
const nextCountry = next.country ?? country;
|
|
186
|
-
setCountry(nextCountry);
|
|
187
|
-
setNational(formatNational(next.digits, nextCountry));
|
|
249
|
+
if (phone.value === value) return;
|
|
250
|
+
setPhone(readPhoneValue(value, phone.country));
|
|
188
251
|
}, [value]);
|
|
189
|
-
function
|
|
190
|
-
|
|
252
|
+
function commit(next) {
|
|
253
|
+
setPhone(next);
|
|
254
|
+
if (next.value !== phone.value) onChange?.(next.value);
|
|
191
255
|
}
|
|
192
256
|
function handleInputChange(event) {
|
|
193
|
-
|
|
194
|
-
setNational(formatNational(digits, country));
|
|
195
|
-
emit(digits, country);
|
|
257
|
+
commit(derivePhoneValue(event.target.value, phone.country));
|
|
196
258
|
}
|
|
197
259
|
function handleCountrySelect(nextCountry) {
|
|
198
|
-
|
|
199
|
-
setCountry(nextCountry);
|
|
200
|
-
setNational(formatNational(digits, nextCountry));
|
|
201
|
-
emit(digits, nextCountry);
|
|
260
|
+
commit(selectPhoneCountry(phone, nextCountry));
|
|
202
261
|
}
|
|
203
262
|
const dividerStateClasses = cn("group-hover/input-group:bg-line-strong", "group-focus-within/input-group:bg-line-focus", "group-has-[[aria-invalid=true]]/input-group:bg-line-danger", "group-has-disabled/input-group:bg-line-subtle");
|
|
204
263
|
return /* @__PURE__ */ jsxs(InputGroup, {
|
|
205
264
|
size: sizeValue,
|
|
206
265
|
className,
|
|
207
|
-
children: [
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
266
|
+
children: [
|
|
267
|
+
/* @__PURE__ */ jsxs(InputGroupAddon, {
|
|
268
|
+
align: "inline-start",
|
|
269
|
+
className: "gap-2 self-stretch py-0",
|
|
270
|
+
children: [/* @__PURE__ */ jsx(CountrySelect, {
|
|
271
|
+
country: phone.country,
|
|
272
|
+
size: resolvedSize.base,
|
|
273
|
+
disabled,
|
|
274
|
+
showCallingCode: !phone.international,
|
|
275
|
+
onSelect: handleCountrySelect,
|
|
276
|
+
finalFocusRef: inputRef
|
|
277
|
+
}), /* @__PURE__ */ jsx(Separator, {
|
|
278
|
+
orientation: "vertical",
|
|
279
|
+
shade: "deep",
|
|
280
|
+
className: cn("self-stretch transition-colors", dividerStateClasses)
|
|
281
|
+
})]
|
|
282
|
+
}),
|
|
283
|
+
/* @__PURE__ */ jsx(InputGroupInput, {
|
|
284
|
+
ref: setInputRef,
|
|
285
|
+
id,
|
|
286
|
+
name,
|
|
287
|
+
type: "tel",
|
|
288
|
+
inputMode: "tel",
|
|
289
|
+
autoComplete: "tel",
|
|
290
|
+
value: phone.text,
|
|
291
|
+
onChange: handleInputChange,
|
|
292
|
+
placeholder,
|
|
213
293
|
disabled,
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
type: "tel",
|
|
226
|
-
inputMode: "tel",
|
|
227
|
-
autoComplete: "tel",
|
|
228
|
-
value: national,
|
|
229
|
-
onChange: handleInputChange,
|
|
230
|
-
placeholder,
|
|
231
|
-
disabled,
|
|
232
|
-
readOnly,
|
|
233
|
-
"aria-invalid": ariaInvalid || void 0,
|
|
234
|
-
className: "pl-0"
|
|
235
|
-
})]
|
|
294
|
+
readOnly,
|
|
295
|
+
"aria-invalid": ariaInvalid || void 0,
|
|
296
|
+
className: "pl-0"
|
|
297
|
+
}),
|
|
298
|
+
rightAddon ? /* @__PURE__ */ jsx(InputGroupAddon, {
|
|
299
|
+
align: "inline-end",
|
|
300
|
+
"data-slot": "phone-right-addon",
|
|
301
|
+
className: "gap-2 self-stretch py-0",
|
|
302
|
+
children: rightAddon
|
|
303
|
+
}) : null
|
|
304
|
+
]
|
|
236
305
|
});
|
|
237
306
|
}
|
|
238
307
|
//#endregion
|
|
@@ -4,11 +4,11 @@ import { Popover } from "@base-ui/react/popover";
|
|
|
4
4
|
//#region src/popover.d.ts
|
|
5
5
|
declare function Popover$1({ ...props }: Popover.Root.Props): React.JSX.Element;
|
|
6
6
|
declare function PopoverTrigger({ ...props }: Popover.Trigger.Props): React.JSX.Element;
|
|
7
|
-
type PopoverContentProps = Popover.Popup.Props & Pick<Popover.Positioner.Props, "align" | "alignOffset" | "collisionAvoidance" | "side" | "sideOffset"> & {
|
|
7
|
+
type PopoverContentProps = Popover.Popup.Props & Pick<Popover.Positioner.Props, "align" | "alignOffset" | "collisionAvoidance" | "positionMethod" | "side" | "sideOffset"> & {
|
|
8
8
|
showArrow?: boolean;
|
|
9
9
|
size?: ComponentSize;
|
|
10
10
|
};
|
|
11
|
-
declare function PopoverContent({ className, align, alignOffset, collisionAvoidance, side, sideOffset, showArrow, size, children, ...props }: PopoverContentProps): React.JSX.Element;
|
|
11
|
+
declare function PopoverContent({ className, align, alignOffset, collisionAvoidance, positionMethod, side, sideOffset, showArrow, size, children, ...props }: PopoverContentProps): React.JSX.Element;
|
|
12
12
|
declare function PopoverHeader({ className, border, ...props }: React.ComponentProps<"div"> & {
|
|
13
13
|
border?: boolean;
|
|
14
14
|
}): React.JSX.Element;
|
package/dist/popover.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as PopoverFooter, c as PopoverTrigger, i as PopoverDescription, n as PopoverBody, o as PopoverHeader, r as PopoverContent, s as PopoverTitle, t as Popover } from "./popover-
|
|
1
|
+
import { a as PopoverFooter, c as PopoverTrigger, i as PopoverDescription, n as PopoverBody, o as PopoverHeader, r as PopoverContent, s as PopoverTitle, t as Popover } from "./popover-BMTZp3AQ.js";
|
|
2
2
|
export { Popover, PopoverBody, PopoverContent, PopoverDescription, PopoverFooter, PopoverHeader, PopoverTitle, PopoverTrigger };
|
package/dist/popover.js
CHANGED
|
@@ -16,11 +16,12 @@ function PopoverTrigger({ ...props }) {
|
|
|
16
16
|
...props
|
|
17
17
|
});
|
|
18
18
|
}
|
|
19
|
-
function PopoverContent({ className, align = "center", alignOffset = 0, collisionAvoidance, side = "bottom", sideOffset = 6, showArrow = true, size = "md", children, ...props }) {
|
|
19
|
+
function PopoverContent({ className, align = "center", alignOffset = 0, collisionAvoidance, positionMethod = "fixed", side = "bottom", sideOffset = 6, showArrow = true, size = "md", children, ...props }) {
|
|
20
20
|
return /* @__PURE__ */ jsx(Popover$1.Portal, { children: /* @__PURE__ */ jsx(Popover$1.Positioner, {
|
|
21
21
|
align,
|
|
22
22
|
alignOffset,
|
|
23
23
|
collisionAvoidance,
|
|
24
|
+
positionMethod,
|
|
24
25
|
side,
|
|
25
26
|
sideOffset,
|
|
26
27
|
className: "isolate z-50",
|
package/dist/rating.d.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { n as ComponentSizeValue } from "./size-context-BVhHduLi.js";
|
|
2
|
+
import { j as IconProps } from "./icons-C9JXR5Xv.js";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
//#region src/rating.d.ts
|
|
5
|
+
/** How finely a rating can be *set*. Display always honours the exact value. */
|
|
6
|
+
type RatingPrecision = 1 | 0.5;
|
|
7
|
+
type RatingIcon = React.ComponentType<IconProps>;
|
|
8
|
+
interface RatingProps {
|
|
9
|
+
/** Controlled rating. Fractions render as partially filled icons. */
|
|
10
|
+
value?: number;
|
|
11
|
+
/** Starting rating when the component owns its own state. */
|
|
12
|
+
defaultValue?: number;
|
|
13
|
+
onValueChange?: (value: number) => void;
|
|
14
|
+
/** How many icons to render. */
|
|
15
|
+
max?: number;
|
|
16
|
+
/** Smallest increment a reader can pick. Display is not rounded to it. */
|
|
17
|
+
precision?: RatingPrecision;
|
|
18
|
+
size?: ComponentSizeValue;
|
|
19
|
+
/** Renders the score as an image instead of a control. */
|
|
20
|
+
readOnly?: boolean;
|
|
21
|
+
disabled?: boolean;
|
|
22
|
+
required?: boolean;
|
|
23
|
+
/** Groups the radios and names the value in submitted form data. */
|
|
24
|
+
name?: string;
|
|
25
|
+
id?: string;
|
|
26
|
+
/** Accessible name for each option, e.g. `(value) => \`${value} stars\``. */
|
|
27
|
+
itemLabel?: (value: number, max: number) => string;
|
|
28
|
+
/** Swaps the glyph. Anything that takes SVG props and paints `currentColor`. */
|
|
29
|
+
icon?: RatingIcon;
|
|
30
|
+
/**
|
|
31
|
+
* Overrides the icon box for a size the `sm`/`md`/`lg` scale does not cover
|
|
32
|
+
* — `iconClassName="size-9"`. Merged after the scale, so it wins outright.
|
|
33
|
+
*/
|
|
34
|
+
iconClassName?: string;
|
|
35
|
+
className?: string;
|
|
36
|
+
onBlur?: React.FocusEventHandler<HTMLElement>;
|
|
37
|
+
/** The `div` that groups the radios, or the `span` when `readOnly`. */
|
|
38
|
+
ref?: React.Ref<HTMLElement>;
|
|
39
|
+
"aria-label"?: string;
|
|
40
|
+
"aria-labelledby"?: string;
|
|
41
|
+
"aria-describedby"?: string;
|
|
42
|
+
"aria-invalid"?: boolean;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* A star rating that reads and writes.
|
|
46
|
+
*
|
|
47
|
+
* Interactive ratings are a group of real radio inputs — one per selectable
|
|
48
|
+
* value — visually replaced by the icons. That is deliberate: it buys native
|
|
49
|
+
* arrow-key navigation, native form submission under `name`, and the
|
|
50
|
+
* "3 of 5, radio button 3 of 5" announcement, none of which a div with click
|
|
51
|
+
* handlers gets for free.
|
|
52
|
+
*
|
|
53
|
+
* ```tsx
|
|
54
|
+
* <Rating defaultValue={4} onValueChange={setRating} />
|
|
55
|
+
* <Rating value={4.3} readOnly />
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
declare function Rating({ value, defaultValue, onValueChange, max, precision, size, readOnly, disabled, required, name, id, itemLabel, icon, iconClassName, className, onBlur, ref, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, "aria-invalid": ariaInvalid }: RatingProps): React.JSX.Element;
|
|
59
|
+
//#endregion
|
|
60
|
+
export { Rating, type RatingIcon, type RatingPrecision, type RatingProps };
|