@seekalfred/primitives 0.6.0 → 0.7.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/README.md +19 -11
- package/dist/activity-heatmap/activity-heatmap.d.ts +92 -0
- package/dist/activity-heatmap/activity-heatmap.js +699 -0
- package/dist/activity-heatmap/activity-heatmap.js.map +1 -0
- package/dist/activity-heatmap/index.d.ts +2 -0
- package/dist/activity-heatmap/index.js +2 -0
- package/dist/alert/alert.d.ts +5 -1
- package/dist/alert/alert.js +68 -7
- package/dist/alert/alert.js.map +1 -1
- package/dist/alert-dialog/alert-dialog.d.ts +45 -18
- package/dist/alert-dialog/alert-dialog.js +128 -9
- package/dist/alert-dialog/alert-dialog.js.map +1 -1
- package/dist/alert-dialog/index.d.ts +2 -2
- package/dist/alert-dialog/index.js +2 -2
- package/dist/animated-counter/animated-counter.d.ts +38 -0
- package/dist/animated-counter/animated-counter.js +230 -0
- package/dist/animated-counter/animated-counter.js.map +1 -0
- package/dist/animated-counter/index.d.ts +2 -0
- package/dist/animated-counter/index.js +2 -0
- package/dist/announcement-bar/announcement-bar.d.ts +67 -0
- package/dist/announcement-bar/announcement-bar.js +364 -0
- package/dist/announcement-bar/announcement-bar.js.map +1 -0
- package/dist/announcement-bar/index.d.ts +2 -0
- package/dist/announcement-bar/index.js +2 -0
- package/dist/badge/badge.d.ts +1 -1
- package/dist/bar-chart/bar-chart.d.ts +51 -0
- package/dist/bar-chart/bar-chart.js +565 -0
- package/dist/bar-chart/bar-chart.js.map +1 -0
- package/dist/bar-chart/index.d.ts +2 -0
- package/dist/bar-chart/index.js +2 -0
- package/dist/breadcrumb/breadcrumb.d.ts +2 -2
- package/dist/breadcrumb/breadcrumb.js +12 -3
- package/dist/breadcrumb/breadcrumb.js.map +1 -1
- package/dist/brush-chart/brush-chart.d.ts +64 -0
- package/dist/brush-chart/brush-chart.js +1056 -0
- package/dist/brush-chart/brush-chart.js.map +1 -0
- package/dist/brush-chart/index.d.ts +2 -0
- package/dist/brush-chart/index.js +2 -0
- package/dist/button/button.d.ts +2 -2
- package/dist/button/button.js +31 -21
- package/dist/button/button.js.map +1 -1
- package/dist/button-group/button-group.js +3 -4
- package/dist/button-group/button-group.js.map +1 -1
- package/dist/card/card.js +1 -1
- package/dist/card/card.js.map +1 -1
- package/dist/card-stack/card-stack.d.ts +54 -0
- package/dist/card-stack/card-stack.js +492 -0
- package/dist/card-stack/card-stack.js.map +1 -0
- package/dist/card-stack/index.d.ts +2 -0
- package/dist/card-stack/index.js +2 -0
- package/dist/carousel/carousel.d.ts +56 -0
- package/dist/carousel/carousel.js +389 -0
- package/dist/carousel/carousel.js.map +1 -0
- package/dist/carousel/index.d.ts +2 -0
- package/dist/carousel/index.js +2 -0
- package/dist/checkbox/checkbox.js +4 -4
- package/dist/checkbox/checkbox.js.map +1 -1
- package/dist/chip-group/chip-group.d.ts +37 -0
- package/dist/chip-group/chip-group.js +148 -0
- package/dist/chip-group/chip-group.js.map +1 -0
- package/dist/chip-group/index.d.ts +2 -0
- package/dist/chip-group/index.js +2 -0
- package/dist/countdown/countdown.d.ts +45 -0
- package/dist/countdown/countdown.js +298 -0
- package/dist/countdown/countdown.js.map +1 -0
- package/dist/countdown/index.d.ts +2 -0
- package/dist/countdown/index.js +2 -0
- package/dist/date-picker/picker-parts.js +1 -1
- package/dist/date-picker/picker-parts.js.map +1 -1
- package/dist/dialog/dialog.js +1 -1
- package/dist/dialog/dialog.js.map +1 -1
- package/dist/donut-chart/donut-chart.d.ts +64 -0
- package/dist/donut-chart/donut-chart.js +576 -0
- package/dist/donut-chart/donut-chart.js.map +1 -0
- package/dist/donut-chart/index.d.ts +2 -0
- package/dist/donut-chart/index.js +2 -0
- package/dist/empty-state/empty-state.d.ts +21 -10
- package/dist/empty-state/empty-state.js +32 -26
- package/dist/empty-state/empty-state.js.map +1 -1
- package/dist/empty-state/index.d.ts +2 -2
- package/dist/empty-state/index.js +2 -2
- package/dist/file-drop-zone/file-drop-zone.js +1 -1
- package/dist/file-drop-zone/file-drop-zone.js.map +1 -1
- package/dist/filter-bar/filter-bar.d.ts +47 -26
- package/dist/filter-bar/filter-bar.js +272 -156
- package/dist/filter-bar/filter-bar.js.map +1 -1
- package/dist/gauge/gauge.d.ts +45 -0
- package/dist/gauge/gauge.js +219 -0
- package/dist/gauge/gauge.js.map +1 -0
- package/dist/gauge/index.d.ts +2 -0
- package/dist/gauge/index.js +2 -0
- package/dist/in-view-title/in-view-title.d.ts +33 -0
- package/dist/in-view-title/in-view-title.js +119 -0
- package/dist/in-view-title/in-view-title.js.map +1 -0
- package/dist/in-view-title/index.d.ts +2 -0
- package/dist/in-view-title/index.js +2 -0
- package/dist/index.d.ts +49 -6
- package/dist/index.js +48 -8
- package/dist/lib/cn.js +11 -1
- package/dist/lib/cn.js.map +1 -1
- package/dist/lib/field-message.js +28 -10
- package/dist/lib/field-message.js.map +1 -1
- package/dist/lib/field-surface.js +9 -8
- package/dist/lib/field-surface.js.map +1 -1
- package/dist/lib/flag-emoji.js +50 -0
- package/dist/lib/flag-emoji.js.map +1 -0
- package/dist/lib/flip.js +11 -3
- package/dist/lib/flip.js.map +1 -1
- package/dist/lib/menu-highlight.js +64 -0
- package/dist/lib/menu-highlight.js.map +1 -0
- package/dist/lib/motion.js +9 -2
- package/dist/lib/motion.js.map +1 -1
- package/dist/lib/rolling-value.js +1 -1
- package/dist/lib/rolling-value.js.map +1 -1
- package/dist/lib/spring-tween.js +500 -0
- package/dist/lib/spring-tween.js.map +1 -0
- package/dist/lib/swap-text.js +3 -1
- package/dist/lib/swap-text.js.map +1 -1
- package/dist/line-chart/index.d.ts +2 -0
- package/dist/line-chart/index.js +2 -0
- package/dist/line-chart/line-chart.d.ts +76 -0
- package/dist/line-chart/line-chart.js +611 -0
- package/dist/line-chart/line-chart.js.map +1 -0
- package/dist/mention-input/index.d.ts +2 -0
- package/dist/mention-input/index.js +2 -0
- package/dist/mention-input/mention-input.d.ts +98 -0
- package/dist/mention-input/mention-input.js +576 -0
- package/dist/mention-input/mention-input.js.map +1 -0
- package/dist/menu/menu.d.ts +2 -0
- package/dist/menu/menu.js +23 -12
- package/dist/menu/menu.js.map +1 -1
- package/dist/money-input/index.d.ts +2 -0
- package/dist/money-input/index.js +2 -0
- package/dist/money-input/money-input.d.ts +65 -0
- package/dist/money-input/money-input.js +607 -0
- package/dist/money-input/money-input.js.map +1 -0
- package/dist/morph-select/index.d.ts +2 -0
- package/dist/morph-select/index.js +2 -0
- package/dist/morph-select/morph-select.d.ts +71 -0
- package/dist/morph-select/morph-select.js +693 -0
- package/dist/morph-select/morph-select.js.map +1 -0
- package/dist/multi-select/index.d.ts +2 -0
- package/dist/multi-select/index.js +2 -0
- package/dist/multi-select/multi-select.d.ts +36 -0
- package/dist/multi-select/multi-select.js +152 -0
- package/dist/multi-select/multi-select.js.map +1 -0
- package/dist/number-field/index.d.ts +2 -0
- package/dist/number-field/index.js +2 -0
- package/dist/number-field/number-field.d.ts +42 -0
- package/dist/number-field/number-field.js +199 -0
- package/dist/number-field/number-field.js.map +1 -0
- package/dist/onboarding-checklist/index.d.ts +2 -0
- package/dist/onboarding-checklist/index.js +2 -0
- package/dist/onboarding-checklist/onboarding-checklist.d.ts +55 -0
- package/dist/onboarding-checklist/onboarding-checklist.js +577 -0
- package/dist/onboarding-checklist/onboarding-checklist.js.map +1 -0
- package/dist/password-field/password-field.js +2 -2
- package/dist/password-field/password-field.js.map +1 -1
- package/dist/phone-input/index.d.ts +3 -0
- package/dist/phone-input/index.js +3 -0
- package/dist/phone-input/phone-countries.d.ts +29 -0
- package/dist/phone-input/phone-countries.js +170 -0
- package/dist/phone-input/phone-countries.js.map +1 -0
- package/dist/phone-input/phone-input.d.ts +66 -0
- package/dist/phone-input/phone-input.js +440 -0
- package/dist/phone-input/phone-input.js.map +1 -0
- package/dist/popover/popover.d.ts +12 -0
- package/dist/popover/popover.js +31 -7
- package/dist/popover/popover.js.map +1 -1
- package/dist/progress/progress.js +1 -1
- package/dist/radio-group/radio-group.js +1 -1
- package/dist/radio-group/radio-group.js.map +1 -1
- package/dist/reactions/index.d.ts +2 -0
- package/dist/reactions/index.js +2 -0
- package/dist/reactions/reactions.d.ts +50 -0
- package/dist/reactions/reactions.js +315 -0
- package/dist/reactions/reactions.js.map +1 -0
- package/dist/rich-text-editor/index.d.ts +3 -0
- package/dist/rich-text-editor/index.js +2 -0
- package/dist/rich-text-editor/rich-text-editor.d.ts +70 -0
- package/dist/rich-text-editor/rich-text-editor.js +909 -0
- package/dist/rich-text-editor/rich-text-editor.js.map +1 -0
- package/dist/rich-text-editor/rich-text-engine.d.ts +16 -0
- package/dist/rich-text-editor/rich-text-engine.js +366 -0
- package/dist/rich-text-editor/rich-text-engine.js.map +1 -0
- package/dist/rich-text-editor/rich-text-html.js +174 -0
- package/dist/rich-text-editor/rich-text-html.js.map +1 -0
- package/dist/rich-text-editor/rich-text-icons.js +43 -0
- package/dist/rich-text-editor/rich-text-icons.js.map +1 -0
- package/dist/ridgeline/index.d.ts +2 -0
- package/dist/ridgeline/index.js +2 -0
- package/dist/ridgeline/ridgeline.d.ts +55 -0
- package/dist/ridgeline/ridgeline.js +701 -0
- package/dist/ridgeline/ridgeline.js.map +1 -0
- package/dist/scroll-area/index.d.ts +2 -2
- package/dist/scroll-area/index.js +2 -2
- package/dist/scroll-area/scroll-area.d.ts +68 -31
- package/dist/scroll-area/scroll-area.js +208 -59
- package/dist/scroll-area/scroll-area.js.map +1 -1
- package/dist/scrub-input/evaluate.d.ts +14 -0
- package/dist/scrub-input/evaluate.js +72 -0
- package/dist/scrub-input/evaluate.js.map +1 -0
- package/dist/scrub-input/index.d.ts +3 -0
- package/dist/scrub-input/index.js +3 -0
- package/dist/scrub-input/scrub-input.d.ts +74 -0
- package/dist/scrub-input/scrub-input.js +486 -0
- package/dist/scrub-input/scrub-input.js.map +1 -0
- package/dist/search-field/search-field.d.ts +2 -1
- package/dist/search-field/search-field.js +4 -4
- package/dist/search-field/search-field.js.map +1 -1
- package/dist/select/select.d.ts +3 -1
- package/dist/select/select.js +20 -8
- package/dist/select/select.js.map +1 -1
- package/dist/separator/separator.d.ts +1 -1
- package/dist/sheet/sheet.js +2 -1
- package/dist/sheet/sheet.js.map +1 -1
- package/dist/shortcut-recorder/index.d.ts +3 -0
- package/dist/shortcut-recorder/index.js +3 -0
- package/dist/shortcut-recorder/shortcut-model.d.ts +27 -0
- package/dist/shortcut-recorder/shortcut-model.js +206 -0
- package/dist/shortcut-recorder/shortcut-model.js.map +1 -0
- package/dist/shortcut-recorder/shortcut-recorder.d.ts +93 -0
- package/dist/shortcut-recorder/shortcut-recorder.js +489 -0
- package/dist/shortcut-recorder/shortcut-recorder.js.map +1 -0
- package/dist/slider/index.d.ts +2 -2
- package/dist/slider/slider.d.ts +46 -14
- package/dist/slider/slider.js +151 -67
- package/dist/slider/slider.js.map +1 -1
- package/dist/slope-chart/index.d.ts +2 -0
- package/dist/slope-chart/index.js +2 -0
- package/dist/slope-chart/slope-chart.d.ts +54 -0
- package/dist/slope-chart/slope-chart.js +568 -0
- package/dist/slope-chart/slope-chart.js.map +1 -0
- package/dist/sparkline/index.d.ts +2 -0
- package/dist/sparkline/index.js +2 -0
- package/dist/sparkline/sparkline.d.ts +46 -0
- package/dist/sparkline/sparkline.js +390 -0
- package/dist/sparkline/sparkline.js.map +1 -0
- package/dist/split-bar/index.d.ts +2 -0
- package/dist/split-bar/index.js +2 -0
- package/dist/split-bar/split-bar.d.ts +62 -0
- package/dist/split-bar/split-bar.js +513 -0
- package/dist/split-bar/split-bar.js.map +1 -0
- package/dist/split-button/split-button.d.ts +1 -1
- package/dist/split-button/split-button.js +12 -11
- package/dist/split-button/split-button.js.map +1 -1
- package/dist/stat-card/index.d.ts +2 -2
- package/dist/stat-card/index.js +2 -2
- package/dist/stat-card/stat-card.d.ts +24 -4
- package/dist/stat-card/stat-card.js +114 -17
- package/dist/stat-card/stat-card.js.map +1 -1
- package/dist/stepper/index.d.ts +2 -0
- package/dist/stepper/index.js +2 -0
- package/dist/stepper/stepper.d.ts +51 -0
- package/dist/stepper/stepper.js +215 -0
- package/dist/stepper/stepper.js.map +1 -0
- package/dist/streamgraph/index.d.ts +2 -0
- package/dist/streamgraph/index.js +2 -0
- package/dist/streamgraph/streamgraph.d.ts +70 -0
- package/dist/streamgraph/streamgraph.js +613 -0
- package/dist/streamgraph/streamgraph.js.map +1 -0
- package/dist/styles.css +1 -1
- package/dist/swipe-actions/index.d.ts +2 -0
- package/dist/swipe-actions/index.js +2 -0
- package/dist/swipe-actions/swipe-actions.d.ts +67 -0
- package/dist/swipe-actions/swipe-actions.js +552 -0
- package/dist/swipe-actions/swipe-actions.js.map +1 -0
- package/dist/switch/switch.js +2 -2
- package/dist/switch/switch.js.map +1 -1
- package/dist/table/index.d.ts +2 -2
- package/dist/table/index.js +2 -2
- package/dist/table/table.d.ts +56 -4
- package/dist/table/table.js +340 -165
- package/dist/table/table.js.map +1 -1
- package/dist/tabs/tabs.js +2 -2
- package/dist/tabs/tabs.js.map +1 -1
- package/dist/tag-input/index.d.ts +2 -0
- package/dist/tag-input/index.js +2 -0
- package/dist/tag-input/tag-input.d.ts +40 -0
- package/dist/tag-input/tag-input.js +269 -0
- package/dist/tag-input/tag-input.js.map +1 -0
- package/dist/text-morph/index.d.ts +2 -0
- package/dist/text-morph/index.js +2 -0
- package/dist/text-morph/text-morph.d.ts +23 -0
- package/dist/text-morph/text-morph.js +21 -0
- package/dist/text-morph/text-morph.js.map +1 -0
- package/dist/text-reveal/index.d.ts +2 -0
- package/dist/text-reveal/index.js +2 -0
- package/dist/text-reveal/text-reveal.d.ts +27 -0
- package/dist/text-reveal/text-reveal.js +51 -0
- package/dist/text-reveal/text-reveal.js.map +1 -0
- package/dist/text-shimmer/index.d.ts +2 -0
- package/dist/text-shimmer/index.js +2 -0
- package/dist/text-shimmer/text-shimmer.d.ts +29 -0
- package/dist/text-shimmer/text-shimmer.js +29 -0
- package/dist/text-shimmer/text-shimmer.js.map +1 -0
- package/dist/time-picker/time-picker.js +2 -2
- package/dist/time-picker/time-picker.js.map +1 -1
- package/dist/toast/index.d.ts +2 -2
- package/dist/toast/index.js +2 -2
- package/dist/toast/toast.d.ts +6 -0
- package/dist/toast/toast.js +82 -40
- package/dist/toast/toast.js.map +1 -1
- package/dist/tooltip/tooltip.d.ts +9 -2
- package/dist/tooltip/tooltip.js +7 -7
- package/dist/tooltip/tooltip.js.map +1 -1
- package/dist/treemap/index.d.ts +2 -0
- package/dist/treemap/index.js +2 -0
- package/dist/treemap/treemap.d.ts +53 -0
- package/dist/treemap/treemap.js +699 -0
- package/dist/treemap/treemap.js.map +1 -0
- package/dist/usage-meter/index.d.ts +2 -0
- package/dist/usage-meter/index.js +2 -0
- package/dist/usage-meter/usage-meter.d.ts +50 -0
- package/dist/usage-meter/usage-meter.js +440 -0
- package/dist/usage-meter/usage-meter.js.map +1 -0
- package/dist/waffle-chart/index.d.ts +2 -0
- package/dist/waffle-chart/index.js +2 -0
- package/dist/waffle-chart/waffle-chart.d.ts +57 -0
- package/dist/waffle-chart/waffle-chart.js +426 -0
- package/dist/waffle-chart/waffle-chart.js.map +1 -0
- package/package.json +165 -3
|
@@ -0,0 +1,440 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn, cnAll } from "../lib/cn.js";
|
|
3
|
+
import { useIsomorphicLayoutEffect } from "../lib/motion.js";
|
|
4
|
+
import { SwapText } from "../lib/swap-text.js";
|
|
5
|
+
import { fieldSurfaceVariants } from "../lib/field-surface.js";
|
|
6
|
+
import { FieldShell, useFieldWiring } from "../lib/field-shell.js";
|
|
7
|
+
import { SelectItem, selectPopoverVariants } from "../select/select.js";
|
|
8
|
+
import { useFlagEmoji } from "../lib/flag-emoji.js";
|
|
9
|
+
import { ScrollArea } from "../scroll-area/scroll-area.js";
|
|
10
|
+
import { PHONE_COUNTRIES, capDigits, caretAfterDigits, flagOf, formatNational, lengthsOf, matchesCountry, onlyDigits, parsePhoneNumber, phoneCountryByIso, phoneStatus, splitTrunk, toE164 } from "./phone-countries.js";
|
|
11
|
+
import { forwardRef, useCallback, useImperativeHandle, useMemo, useReducer, useRef, useState } from "react";
|
|
12
|
+
import { Autocomplete, Button, Dialog, DialogTrigger, Group, Input, ListBox, Popover, SearchField } from "react-aria-components";
|
|
13
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
14
|
+
//#region src/phone-input/phone-input.tsx
|
|
15
|
+
const focusRing = "data-[focus-visible]:not-data-[invalid]:border-foreground forced-colors:data-[focus-visible]:outline-solid forced-colors:data-[focus-visible]:outline-(length:--focus-ring-width) forced-colors:data-[focus-visible]:outline-offset-(--focus-ring-offset)";
|
|
16
|
+
const shellClass = cnAll(fieldSurfaceVariants(), "flex h-(--density-control-h-md) items-center", "has-[input:focus]:not-data-[invalid]:border-foreground", "forced-colors:has-[input:focus]:outline-solid forced-colors:has-[input:focus]:outline-(length:--focus-ring-width) forced-colors:has-[input:focus]:outline-offset-(--focus-ring-offset)", focusRing);
|
|
17
|
+
const triggerClass = cnAll("group/trigger m-1 inline-flex h-[calc(100%-0.5rem)] flex-none cursor-pointer items-center gap-1.5 rounded-control border-0 bg-transparent ps-2.5 pe-2 font-sans text-base font-medium tabular-nums text-foreground outline-none", "motion-safe:transition-[background-color] motion-safe:duration-(--duration-quick) motion-safe:ease-settle", "data-[hovered]:bg-muted data-[pressed]:bg-muted data-[disabled]:cursor-not-allowed", focusRing);
|
|
18
|
+
/** A native `<input>` styled flat: the shell draws the surface. */
|
|
19
|
+
const numberClass = "relative z-10 m-0 h-full w-full min-w-0 appearance-none border-0 bg-transparent px-3 pe-10 font-sans text-base tabular-nums text-foreground outline-none selection:bg-primary selection:text-primary-foreground data-[disabled]:cursor-not-allowed";
|
|
20
|
+
const searchClass = "relative flex h-(--density-control-h-sm) items-center gap-2 border-b-(length:--border-width) border-border px-2.5 after:pointer-events-none after:absolute after:inset-x-0 after:bottom-0 after:h-(--focus-ring-width) after:bg-transparent has-[input:focus]:after:bg-(--color-control-edge)";
|
|
21
|
+
const rowClass = "group/row rounded-control data-[hovered]:bg-muted data-[focus-visible]:outline-(--color-control-edge)";
|
|
22
|
+
const nameClass = "min-w-0 flex-1 truncate font-medium text-text-secondary group-data-[selected]/row:text-foreground group-data-[focused]/row:text-foreground group-data-[hovered]/row:text-foreground";
|
|
23
|
+
const flagClass = "w-6 flex-none text-center text-base leading-none";
|
|
24
|
+
const codeClass = "w-6 flex-none text-center text-xs font-semibold leading-none tracking-wide text-muted-foreground";
|
|
25
|
+
const emptyClass = "px-3 py-3 text-sm text-muted-foreground";
|
|
26
|
+
const SORTED = [...PHONE_COUNTRIES].sort((a, b) => a.name.localeCompare(b.name));
|
|
27
|
+
const textValueOf = (entry) => `${entry.name} ${entry.iso} +${entry.dial}`;
|
|
28
|
+
const PhoneInput = forwardRef(function PhoneInput({ label, labelHidden = false, value, defaultValue, onValueChange, country: countryProp, defaultCountry = "US", onCountryChange, countries, description, error, validate = true, flags = "auto", disabled = false, required, name, id, className, onBlur }, forwardedRef) {
|
|
29
|
+
const pool = useMemo(() => countries?.length ? PHONE_COUNTRIES.filter((entry) => countries.includes(entry.iso)) : PHONE_COUNTRIES, [countries]);
|
|
30
|
+
const list = useMemo(() => SORTED.filter((entry) => pool.includes(entry)), [pool]);
|
|
31
|
+
const items = useMemo(() => list.map((entry) => ({
|
|
32
|
+
...entry,
|
|
33
|
+
id: entry.iso
|
|
34
|
+
})), [list]);
|
|
35
|
+
const canDrawFlags = useFlagEmoji();
|
|
36
|
+
const flagEmoji = flags === "auto" ? canDrawFlags : flags === "emoji";
|
|
37
|
+
const fallback = phoneCountryByIso(defaultCountry) ?? PHONE_COUNTRIES[0];
|
|
38
|
+
const [state, setState] = useState(() => {
|
|
39
|
+
const parsed = parsePhoneNumber(value ?? defaultValue ?? "", pool);
|
|
40
|
+
return parsed ? {
|
|
41
|
+
iso: parsed.country.iso,
|
|
42
|
+
digits: parsed.national
|
|
43
|
+
} : {
|
|
44
|
+
iso: fallback.iso,
|
|
45
|
+
digits: ""
|
|
46
|
+
};
|
|
47
|
+
});
|
|
48
|
+
const iso = countryProp ?? state.iso;
|
|
49
|
+
const current = phoneCountryByIso(iso) ?? fallback;
|
|
50
|
+
const digits = state.digits;
|
|
51
|
+
const e164 = toE164(current, digits);
|
|
52
|
+
const [seenValue, setSeenValue] = useState(value);
|
|
53
|
+
if (value !== seenValue) {
|
|
54
|
+
setSeenValue(value);
|
|
55
|
+
if (value !== void 0 && value !== e164) {
|
|
56
|
+
const parsed = parsePhoneNumber(value, pool);
|
|
57
|
+
setState(parsed ? {
|
|
58
|
+
iso: parsed.country.iso,
|
|
59
|
+
digits: parsed.national
|
|
60
|
+
} : {
|
|
61
|
+
iso,
|
|
62
|
+
digits: ""
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
const formatted = formatNational(current, digits);
|
|
67
|
+
const status = phoneStatus(current, digits);
|
|
68
|
+
const [touched, setTouched] = useState(false);
|
|
69
|
+
const [edited, setEdited] = useState(false);
|
|
70
|
+
const lengths = lengthsOf(current);
|
|
71
|
+
const lengthCopy = lengths.length === 1 ? `${lengths[0]}` : `${lengths.slice(0, -1).join(", ")} or ${lengths[lengths.length - 1]}`;
|
|
72
|
+
const builtIn = validate && touched && (status === "incomplete" || status === "too-long") ? `Numbers in ${current.name} have ${lengthCopy} digits` : void 0;
|
|
73
|
+
const message = error ?? builtIn;
|
|
74
|
+
const guide = useMemo(() => {
|
|
75
|
+
const [trunk, rest] = splitTrunk(current, digits);
|
|
76
|
+
if (rest.length >= current.example.length) return "";
|
|
77
|
+
const full = formatNational(current, trunk + rest + current.example.slice(rest.length));
|
|
78
|
+
return full.startsWith(formatted) ? full.slice(formatted.length) : "";
|
|
79
|
+
}, [
|
|
80
|
+
current,
|
|
81
|
+
digits,
|
|
82
|
+
formatted
|
|
83
|
+
]);
|
|
84
|
+
const inputRef = useRef(null);
|
|
85
|
+
useImperativeHandle(forwardedRef, () => inputRef.current);
|
|
86
|
+
const shellRef = useRef(null);
|
|
87
|
+
const pendingCaret = useRef(null);
|
|
88
|
+
const [, rerender] = useReducer((count) => count + 1, 0);
|
|
89
|
+
const [announcement, setAnnouncement] = useState("");
|
|
90
|
+
const emit = useCallback((entry, nextDigits) => {
|
|
91
|
+
const nextStatus = phoneStatus(entry, nextDigits);
|
|
92
|
+
onValueChange?.(toE164(entry, nextDigits), {
|
|
93
|
+
country: entry,
|
|
94
|
+
formatted: formatNational(entry, nextDigits),
|
|
95
|
+
status: nextStatus,
|
|
96
|
+
valid: nextStatus === "valid"
|
|
97
|
+
});
|
|
98
|
+
}, [onValueChange]);
|
|
99
|
+
function setNumber(nextDigits, caretDigits, entry = current) {
|
|
100
|
+
const capped = capDigits(entry, nextDigits);
|
|
101
|
+
pendingCaret.current = caretDigits === null ? null : Math.min(caretDigits, capped.length);
|
|
102
|
+
if (entry.iso !== current.iso) onCountryChange?.(entry.iso);
|
|
103
|
+
if (capped === digits && entry.iso === current.iso) {
|
|
104
|
+
rerender();
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
setEdited(true);
|
|
108
|
+
setState({
|
|
109
|
+
iso: entry.iso,
|
|
110
|
+
digits: capped
|
|
111
|
+
});
|
|
112
|
+
emit(entry, capped);
|
|
113
|
+
}
|
|
114
|
+
useIsomorphicLayoutEffect(() => {
|
|
115
|
+
const input = inputRef.current;
|
|
116
|
+
const count = pendingCaret.current;
|
|
117
|
+
pendingCaret.current = null;
|
|
118
|
+
if (!input || count === null || document.activeElement !== input) return;
|
|
119
|
+
const position = caretAfterDigits(input.value, count);
|
|
120
|
+
input.setSelectionRange(position, position);
|
|
121
|
+
});
|
|
122
|
+
function onNumberChange(input) {
|
|
123
|
+
const raw = input.value;
|
|
124
|
+
if (raw.includes("+") || /^\s*00/.test(raw) && onlyDigits(raw).length > 6) {
|
|
125
|
+
const parsed = parsePhoneNumber(raw.slice(Math.max(0, raw.indexOf("+"))), pool);
|
|
126
|
+
if (parsed?.national) {
|
|
127
|
+
if (parsed.country.iso !== current.iso) setAnnouncement(`Country set to ${parsed.country.name}`);
|
|
128
|
+
setNumber(parsed.national, parsed.national.length, parsed.country);
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
const caret = input.selectionStart ?? raw.length;
|
|
133
|
+
setNumber(onlyDigits(raw), onlyDigits(raw.slice(0, caret)).length);
|
|
134
|
+
}
|
|
135
|
+
function onNumberKeyDown(event) {
|
|
136
|
+
const input = event.currentTarget;
|
|
137
|
+
if (event.key === "+" && !event.metaKey && !event.ctrlKey) {
|
|
138
|
+
event.preventDefault();
|
|
139
|
+
setQuery("+");
|
|
140
|
+
setOpen(true);
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
const start = input.selectionStart ?? 0;
|
|
144
|
+
if (start !== (input.selectionEnd ?? 0) || event.metaKey || event.ctrlKey || event.altKey) return;
|
|
145
|
+
if (event.key === "Backspace" && start > 0 && !/\d/.test(input.value[start - 1])) {
|
|
146
|
+
event.preventDefault();
|
|
147
|
+
const index = onlyDigits(input.value.slice(0, start)).length;
|
|
148
|
+
if (index > 0) setNumber(digits.slice(0, index - 1) + digits.slice(index), index - 1);
|
|
149
|
+
}
|
|
150
|
+
if (event.key === "Delete" && start < input.value.length && !/\d/.test(input.value[start])) {
|
|
151
|
+
event.preventDefault();
|
|
152
|
+
const index = onlyDigits(input.value.slice(0, start)).length;
|
|
153
|
+
setNumber(digits.slice(0, index) + digits.slice(index + 1), index);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
function onPaste(event) {
|
|
157
|
+
const text = event.clipboardData.getData("text");
|
|
158
|
+
if (!text) return;
|
|
159
|
+
event.preventDefault();
|
|
160
|
+
const input = event.currentTarget;
|
|
161
|
+
const parsed = /^\s*(\+|00)/.test(text) ? parsePhoneNumber(text, pool) : null;
|
|
162
|
+
if (parsed) {
|
|
163
|
+
if (parsed.country.iso !== current.iso) setAnnouncement(`Country set to ${parsed.country.name}`);
|
|
164
|
+
setNumber(parsed.national, parsed.national.length, parsed.country);
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
let pasted = onlyDigits(text);
|
|
168
|
+
if (pasted.startsWith(current.dial) && pasted.length > capDigits(current, pasted).length) pasted = pasted.slice(current.dial.length);
|
|
169
|
+
if (pasted.length >= Math.min(...lengthsOf(current))) {
|
|
170
|
+
setNumber(pasted, pasted.length);
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
const from = onlyDigits(input.value.slice(0, input.selectionStart ?? 0)).length;
|
|
174
|
+
const to = onlyDigits(input.value.slice(0, input.selectionEnd ?? 0)).length;
|
|
175
|
+
setNumber(digits.slice(0, from) + pasted + digits.slice(to), from + pasted.length);
|
|
176
|
+
}
|
|
177
|
+
const [open, setOpen] = useState(false);
|
|
178
|
+
const [query, setQuery] = useState("");
|
|
179
|
+
const openRef = useRef(false);
|
|
180
|
+
openRef.current = open;
|
|
181
|
+
function pick(key) {
|
|
182
|
+
const entry = list.find((item) => item.iso === key);
|
|
183
|
+
if (!entry) return;
|
|
184
|
+
if (entry.iso !== current.iso) {
|
|
185
|
+
const nextDigits = capDigits(entry, digits);
|
|
186
|
+
if (countryProp === void 0) setState({
|
|
187
|
+
iso: entry.iso,
|
|
188
|
+
digits: nextDigits
|
|
189
|
+
});
|
|
190
|
+
else setState((last) => ({
|
|
191
|
+
...last,
|
|
192
|
+
digits: nextDigits
|
|
193
|
+
}));
|
|
194
|
+
onCountryChange?.(entry.iso);
|
|
195
|
+
emit(entry, nextDigits);
|
|
196
|
+
setEdited(true);
|
|
197
|
+
setAnnouncement(`${entry.name}, +${entry.dial}`);
|
|
198
|
+
}
|
|
199
|
+
setOpen(false);
|
|
200
|
+
requestAnimationFrame(() => {
|
|
201
|
+
if (!openRef.current) inputRef.current?.focus({ preventScroll: true });
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
const filter = useCallback((textValue, inputValue) => {
|
|
205
|
+
const entry = SORTED.find((item) => textValueOf(item) === textValue);
|
|
206
|
+
return entry ? matchesCountry(entry, inputValue) : true;
|
|
207
|
+
}, []);
|
|
208
|
+
const field = useFieldWiring({
|
|
209
|
+
enabled: true,
|
|
210
|
+
id,
|
|
211
|
+
describedBy: void 0,
|
|
212
|
+
invalid: void 0,
|
|
213
|
+
description,
|
|
214
|
+
error: message
|
|
215
|
+
});
|
|
216
|
+
const showCheck = status === "valid";
|
|
217
|
+
const control = /* @__PURE__ */ jsxs(Group, {
|
|
218
|
+
ref: shellRef,
|
|
219
|
+
role: "presentation",
|
|
220
|
+
isDisabled: disabled,
|
|
221
|
+
isInvalid: Boolean(message),
|
|
222
|
+
"data-slot": "phone-input-shell",
|
|
223
|
+
className: shellClass,
|
|
224
|
+
children: [/* @__PURE__ */ jsxs(DialogTrigger, {
|
|
225
|
+
isOpen: open,
|
|
226
|
+
onOpenChange: (next) => {
|
|
227
|
+
setOpen(next);
|
|
228
|
+
if (next) setQuery("");
|
|
229
|
+
},
|
|
230
|
+
children: [/* @__PURE__ */ jsxs(Button, {
|
|
231
|
+
isDisabled: disabled,
|
|
232
|
+
"aria-label": `Country, ${current.name} +${current.dial}`,
|
|
233
|
+
"data-slot": "phone-input-country",
|
|
234
|
+
className: triggerClass,
|
|
235
|
+
children: [
|
|
236
|
+
/* @__PURE__ */ jsx("span", {
|
|
237
|
+
"aria-hidden": "true",
|
|
238
|
+
"data-slot": "phone-input-flag",
|
|
239
|
+
"data-glyph": flagEmoji ? "flag" : "code",
|
|
240
|
+
className: flagEmoji ? flagClass : codeClass,
|
|
241
|
+
children: flagEmoji ? flagOf(current.iso) : current.iso
|
|
242
|
+
}),
|
|
243
|
+
/* @__PURE__ */ jsx(SwapText, {
|
|
244
|
+
swapKey: current.iso,
|
|
245
|
+
motion: "roll",
|
|
246
|
+
className: "inline-flex",
|
|
247
|
+
children: /* @__PURE__ */ jsxs("span", {
|
|
248
|
+
"data-slot": "phone-input-dial",
|
|
249
|
+
children: ["+", current.dial]
|
|
250
|
+
})
|
|
251
|
+
}),
|
|
252
|
+
/* @__PURE__ */ jsx("svg", {
|
|
253
|
+
"aria-hidden": "true",
|
|
254
|
+
viewBox: "0 0 16 16",
|
|
255
|
+
fill: "none",
|
|
256
|
+
stroke: "currentColor",
|
|
257
|
+
strokeWidth: "1.75",
|
|
258
|
+
strokeLinecap: "round",
|
|
259
|
+
strokeLinejoin: "round",
|
|
260
|
+
className: "size-4 text-muted-foreground motion-safe:transition-[rotate] motion-safe:duration-(--duration-gentle) motion-safe:ease-gentle group-aria-expanded/trigger:rotate-180",
|
|
261
|
+
children: /* @__PURE__ */ jsx("path", { d: "m4 6 4 4 4-4" })
|
|
262
|
+
})
|
|
263
|
+
]
|
|
264
|
+
}), /* @__PURE__ */ jsx(Popover, {
|
|
265
|
+
triggerRef: shellRef,
|
|
266
|
+
placement: "bottom start",
|
|
267
|
+
offset: 8,
|
|
268
|
+
"data-slot": "phone-input-popover",
|
|
269
|
+
className: cnAll(selectPopoverVariants({ reveal: "fade" }), "flex w-80 max-w-[calc(100vw-2rem)] flex-col overflow-hidden"),
|
|
270
|
+
children: /* @__PURE__ */ jsx(Dialog, {
|
|
271
|
+
"aria-label": "Country",
|
|
272
|
+
className: "flex min-h-0 flex-col gap-1 outline-none",
|
|
273
|
+
children: /* @__PURE__ */ jsxs(Autocomplete, {
|
|
274
|
+
filter,
|
|
275
|
+
inputValue: query,
|
|
276
|
+
onInputChange: setQuery,
|
|
277
|
+
children: [/* @__PURE__ */ jsxs(SearchField, {
|
|
278
|
+
"aria-label": "Search countries or calling codes",
|
|
279
|
+
autoFocus: true,
|
|
280
|
+
className: searchClass,
|
|
281
|
+
children: [
|
|
282
|
+
/* @__PURE__ */ jsxs("svg", {
|
|
283
|
+
"aria-hidden": "true",
|
|
284
|
+
viewBox: "0 0 24 24",
|
|
285
|
+
fill: "none",
|
|
286
|
+
stroke: "currentColor",
|
|
287
|
+
strokeWidth: 2,
|
|
288
|
+
strokeLinecap: "round",
|
|
289
|
+
strokeLinejoin: "round",
|
|
290
|
+
className: "size-4 flex-none text-muted-foreground",
|
|
291
|
+
children: [/* @__PURE__ */ jsx("circle", {
|
|
292
|
+
cx: "11",
|
|
293
|
+
cy: "11",
|
|
294
|
+
r: "8"
|
|
295
|
+
}), /* @__PURE__ */ jsx("path", { d: "m21 21-4.34-4.34" })]
|
|
296
|
+
}),
|
|
297
|
+
/* @__PURE__ */ jsx(Input, {
|
|
298
|
+
placeholder: "Country or code",
|
|
299
|
+
"data-slot": "phone-input-search",
|
|
300
|
+
className: "min-w-0 flex-1 appearance-none border-0 bg-transparent p-0 font-sans text-sm text-foreground outline-none placeholder:text-muted-foreground [&::-webkit-search-cancel-button]:hidden"
|
|
301
|
+
}),
|
|
302
|
+
/* @__PURE__ */ jsx("svg", {
|
|
303
|
+
"aria-hidden": "true",
|
|
304
|
+
"data-slot": "phone-input-search-chevron",
|
|
305
|
+
viewBox: "0 0 16 16",
|
|
306
|
+
fill: "none",
|
|
307
|
+
stroke: "currentColor",
|
|
308
|
+
strokeWidth: "1.75",
|
|
309
|
+
strokeLinecap: "round",
|
|
310
|
+
strokeLinejoin: "round",
|
|
311
|
+
className: "size-4 flex-none text-muted-foreground",
|
|
312
|
+
children: /* @__PURE__ */ jsx("path", { d: "m4 10 4-4 4 4" })
|
|
313
|
+
})
|
|
314
|
+
]
|
|
315
|
+
}), /* @__PURE__ */ jsx(ScrollArea, {
|
|
316
|
+
label: "Country list",
|
|
317
|
+
className: "max-h-72 rounded-control",
|
|
318
|
+
viewportClassName: "rounded-control focus-visible:outline-(--color-control-edge)",
|
|
319
|
+
children: /* @__PURE__ */ jsx(ListBox, {
|
|
320
|
+
"aria-label": "Countries",
|
|
321
|
+
selectionMode: "single",
|
|
322
|
+
selectedKeys: [current.iso],
|
|
323
|
+
escapeKeyBehavior: "none",
|
|
324
|
+
onSelectionChange: (keys) => {
|
|
325
|
+
if (keys === "all") return;
|
|
326
|
+
pick([...keys][0] ?? current.iso);
|
|
327
|
+
},
|
|
328
|
+
items,
|
|
329
|
+
renderEmptyState: () => /* @__PURE__ */ jsx("p", {
|
|
330
|
+
className: emptyClass,
|
|
331
|
+
children: "No countries match"
|
|
332
|
+
}),
|
|
333
|
+
className: "outline-none",
|
|
334
|
+
children: (entry) => /* @__PURE__ */ jsxs(SelectItem, {
|
|
335
|
+
id: entry.id,
|
|
336
|
+
textValue: textValueOf(entry),
|
|
337
|
+
className: rowClass,
|
|
338
|
+
children: [
|
|
339
|
+
/* @__PURE__ */ jsx("span", {
|
|
340
|
+
"aria-hidden": "true",
|
|
341
|
+
"data-slot": "phone-input-row-flag",
|
|
342
|
+
"data-glyph": flagEmoji ? "flag" : "code",
|
|
343
|
+
className: flagEmoji ? flagClass : codeClass,
|
|
344
|
+
children: flagEmoji ? flagOf(entry.iso) : entry.iso
|
|
345
|
+
}),
|
|
346
|
+
/* @__PURE__ */ jsx("span", {
|
|
347
|
+
className: nameClass,
|
|
348
|
+
children: entry.name
|
|
349
|
+
}),
|
|
350
|
+
/* @__PURE__ */ jsxs("span", {
|
|
351
|
+
className: "flex-none text-xs tabular-nums text-muted-foreground",
|
|
352
|
+
children: ["+", entry.dial]
|
|
353
|
+
})
|
|
354
|
+
]
|
|
355
|
+
})
|
|
356
|
+
})
|
|
357
|
+
})]
|
|
358
|
+
})
|
|
359
|
+
})
|
|
360
|
+
})]
|
|
361
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
362
|
+
className: "relative flex h-full min-w-0 flex-1 items-center border-s-(length:--border-width) border-border",
|
|
363
|
+
children: [
|
|
364
|
+
/* @__PURE__ */ jsxs("span", {
|
|
365
|
+
"aria-hidden": "true",
|
|
366
|
+
"data-slot": "phone-input-guide",
|
|
367
|
+
className: "pointer-events-none absolute inset-0 flex items-center overflow-hidden whitespace-pre px-3 font-sans text-base tabular-nums text-muted-foreground",
|
|
368
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
369
|
+
className: "invisible",
|
|
370
|
+
children: formatted
|
|
371
|
+
}), guide]
|
|
372
|
+
}),
|
|
373
|
+
/* @__PURE__ */ jsx(Input, {
|
|
374
|
+
ref: inputRef,
|
|
375
|
+
...field.controlProps,
|
|
376
|
+
"data-slot": "phone-input-number",
|
|
377
|
+
type: "tel",
|
|
378
|
+
inputMode: "tel",
|
|
379
|
+
autoComplete: "tel",
|
|
380
|
+
value: formatted,
|
|
381
|
+
disabled,
|
|
382
|
+
required,
|
|
383
|
+
"aria-label": labelHidden ? label : void 0,
|
|
384
|
+
onChange: (event) => onNumberChange(event.currentTarget),
|
|
385
|
+
onKeyDown: onNumberKeyDown,
|
|
386
|
+
onPaste,
|
|
387
|
+
onBlur: (event) => {
|
|
388
|
+
setTouched(digits.length > 0);
|
|
389
|
+
onBlur?.(event);
|
|
390
|
+
},
|
|
391
|
+
className: numberClass
|
|
392
|
+
}),
|
|
393
|
+
showCheck ? /* @__PURE__ */ jsx("svg", {
|
|
394
|
+
"aria-hidden": "true",
|
|
395
|
+
"data-slot": "phone-input-valid",
|
|
396
|
+
viewBox: "0 0 16 16",
|
|
397
|
+
fill: "none",
|
|
398
|
+
stroke: "currentColor",
|
|
399
|
+
strokeWidth: "2",
|
|
400
|
+
strokeLinecap: "round",
|
|
401
|
+
strokeLinejoin: "round",
|
|
402
|
+
className: cn("pointer-events-none absolute end-3 size-4 text-success-strong", edited && "motion-safe:animate-select-check-in"),
|
|
403
|
+
children: /* @__PURE__ */ jsx("path", { d: "m3.5 8.5 3 3 6-6" })
|
|
404
|
+
}) : null
|
|
405
|
+
]
|
|
406
|
+
})]
|
|
407
|
+
});
|
|
408
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
409
|
+
className: cn("w-full min-w-0 font-sans", className),
|
|
410
|
+
"data-slot": "phone-input",
|
|
411
|
+
"data-country": current.iso,
|
|
412
|
+
children: [
|
|
413
|
+
/* @__PURE__ */ jsx(FieldShell, {
|
|
414
|
+
label: labelHidden ? null : label,
|
|
415
|
+
controlId: field.controlId,
|
|
416
|
+
description,
|
|
417
|
+
hintId: field.hintId,
|
|
418
|
+
error: message,
|
|
419
|
+
errorId: field.errorId,
|
|
420
|
+
children: control
|
|
421
|
+
}),
|
|
422
|
+
name ? /* @__PURE__ */ jsx("input", {
|
|
423
|
+
type: "hidden",
|
|
424
|
+
name,
|
|
425
|
+
value: e164
|
|
426
|
+
}) : null,
|
|
427
|
+
/* @__PURE__ */ jsx("span", {
|
|
428
|
+
className: "sr-only",
|
|
429
|
+
role: "status",
|
|
430
|
+
"aria-live": "polite",
|
|
431
|
+
children: announcement || (showCheck ? `Valid ${current.name} number` : "")
|
|
432
|
+
})
|
|
433
|
+
]
|
|
434
|
+
});
|
|
435
|
+
});
|
|
436
|
+
PhoneInput.displayName = "PhoneInput";
|
|
437
|
+
//#endregion
|
|
438
|
+
export { PhoneInput };
|
|
439
|
+
|
|
440
|
+
//# sourceMappingURL=phone-input.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"phone-input.js","names":["AriaGroup","AriaDialogTrigger","AriaButton","AriaPopover","AriaDialog","AriaAutocomplete","AriaSearchField","AriaInput","AriaListBox"],"sources":["../../src/phone-input/phone-input.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n forwardRef,\n useCallback,\n useImperativeHandle,\n useMemo,\n useReducer,\n useRef,\n useState,\n type ClipboardEvent,\n type FocusEvent,\n type KeyboardEvent,\n} from \"react\";\nimport {\n Autocomplete as AriaAutocomplete,\n Button as AriaButton,\n Dialog as AriaDialog,\n DialogTrigger as AriaDialogTrigger,\n Group as AriaGroup,\n Input as AriaInput,\n ListBox as AriaListBox,\n Popover as AriaPopover,\n SearchField as AriaSearchField,\n type Key,\n} from \"react-aria-components\";\n\nimport { cn, cnAll } from \"../lib/cn.js\";\nimport { FieldShell, useFieldWiring } from \"../lib/field-shell.js\";\nimport { fieldSurfaceVariants } from \"../lib/field-surface.js\";\nimport { useFlagEmoji } from \"../lib/flag-emoji.js\";\nimport { useIsomorphicLayoutEffect } from \"../lib/motion.js\";\nimport { SwapText } from \"../lib/swap-text.js\";\nimport { ScrollArea } from \"../scroll-area/scroll-area.js\";\nimport { SelectItem, selectPopoverVariants } from \"../select/select.js\";\nimport {\n PHONE_COUNTRIES,\n capDigits,\n caretAfterDigits,\n flagOf,\n formatNational,\n lengthsOf,\n matchesCountry,\n onlyDigits,\n parsePhoneNumber,\n phoneCountryByIso,\n phoneStatus,\n splitTrunk,\n toE164,\n type PhoneCountry,\n type PhoneStatus,\n} from \"./phone-countries.js\";\n\n/** What `onValueChange` hands over beside the E.164 number. */\nexport interface PhoneInputDetails {\n country: PhoneCountry;\n /** The number as shown in the field, without the calling code. */\n formatted: string;\n status: PhoneStatus;\n valid: boolean;\n}\n\n/**\n * PhoneInput: a phone number with a country picker. The number formats as it is\n * typed (grouping follows the country, the caret stays on the digit it was on, a\n * faint guide shows the rest of the expected shape), a pasted or autofilled\n * international number such as \"+44 7400 123456\" picks its own country, and the\n * value comes out in E.164. The picker is a searchable list (React Aria\n * Autocomplete) that matches names, ISO codes and calling codes; typing \"+\" in\n * the number opens it with a calling code search.\n *\n * Formatting and length checks come from a compact country table in this\n * package, not a phone-number library: it knows the usual digit count and\n * grouping of about fifty countries, not which prefixes a carrier has assigned.\n * Use a full library on the server when a number must be a real, assigned line.\n */\nexport interface PhoneInputProps {\n /** What the number is for, and the field's accessible name. */\n label: string;\n /** Keep the label for assistive technology only. */\n labelHidden?: boolean;\n /** The number in E.164, such as \"+14155550132\". An empty string clears the field. */\n value?: string;\n defaultValue?: string;\n /** Fires on every edit with the E.164 number (empty when there are no digits) and its parsed details. */\n onValueChange?: (value: string, details: PhoneInputDetails) => void;\n /** ISO code of the selected country. Controlled when given. */\n country?: string;\n /** ISO code used until someone picks a country or enters an international number. @default \"US\" */\n defaultCountry?: string;\n onCountryChange?: (iso: string) => void;\n /** Limit the picker to these ISO codes. */\n countries?: string[];\n /** Helper text under the field. A string, because the copy is animated word by word. */\n description?: string;\n /** Replaces the built-in length message. */\n error?: string;\n /** Show a message after blur when the number is incomplete. @default true */\n validate?: boolean;\n /**\n * How a country is marked in the field and the list. `auto` draws the flag emoji where the platform can (Windows\n * cannot) and the two-letter ISO code where it cannot; `emoji` and `code` force one.\n * @default \"auto\"\n */\n flags?: \"auto\" | \"emoji\" | \"code\";\n disabled?: boolean;\n required?: boolean;\n /** Adds a hidden input carrying the E.164 value for native form submission. */\n name?: string;\n id?: string;\n /** Extra classes for the wrapper. Merged last. */\n className?: string;\n onBlur?: (event: FocusEvent<HTMLInputElement>) => void;\n}\n\n/*\n * Focus turns the shell's own border dark (the foreground colour), as every text field does: no separate ring.\n * The outline comes back only under forced colours, where a border colour is overridden.\n */\nconst focusRing =\n \"data-[focus-visible]:not-data-[invalid]:border-foreground forced-colors:data-[focus-visible]:outline-solid forced-colors:data-[focus-visible]:outline-(length:--focus-ring-width) forced-colors:data-[focus-visible]:outline-offset-(--focus-ring-offset)\";\n\nconst shellClass = cnAll(\n fieldSurfaceVariants(),\n \"flex h-(--density-control-h-md) items-center\",\n // Focus turns the box's own border dark whenever the number holds focus; no separate ring.\n \"has-[input:focus]:not-data-[invalid]:border-foreground\",\n \"forced-colors:has-[input:focus]:outline-solid forced-colors:has-[input:focus]:outline-(length:--focus-ring-width) forced-colors:has-[input:focus]:outline-offset-(--focus-ring-offset)\",\n focusRing,\n);\n\nconst triggerClass = cnAll(\n \"group/trigger m-1 inline-flex h-[calc(100%-0.5rem)] flex-none cursor-pointer items-center gap-1.5 rounded-control border-0 bg-transparent ps-2.5 pe-2 font-sans text-base font-medium tabular-nums text-foreground outline-none\",\n \"motion-safe:transition-[background-color] motion-safe:duration-(--duration-quick) motion-safe:ease-settle\",\n \"data-[hovered]:bg-muted data-[pressed]:bg-muted data-[disabled]:cursor-not-allowed\",\n focusRing,\n);\n\n/** A native `<input>` styled flat: the shell draws the surface. */\nconst numberClass =\n \"relative z-10 m-0 h-full w-full min-w-0 appearance-none border-0 bg-transparent px-3 pe-10 font-sans text-base tabular-nums text-foreground outline-none selection:bg-primary selection:text-primary-foreground data-[disabled]:cursor-not-allowed\";\n\n/*\n * The search is a flat top row of the panel, not a boxed pill: icon, text, a decorative chevron at the end and a hairline\n * divider below. Focus is a 2px neutral underline (control-edge, 3:1 on the surface) drawn over the divider, so it can\n * never read as a second ring around a border.\n */\nconst searchClass =\n \"relative flex h-(--density-control-h-sm) items-center gap-2 border-b-(length:--border-width) border-border px-2.5 after:pointer-events-none after:absolute after:inset-x-0 after:bottom-0 after:h-(--focus-ring-width) after:bg-transparent has-[input:focus]:after:bg-(--color-control-edge)\";\n\n/*\n * Rows read quietly: secondary ink for the name, a soft pill under the row the pointer or the arrows reached, and\n * full ink once a row is the chosen country or the highlighted one. The list sits in an Autocomplete, which marks the\n * keyboard row data-focused but the pointer row only data-hovered, so both states are styled. The tick sits at the end (SelectItem draws it).\n */\nconst rowClass =\n \"group/row rounded-control data-[hovered]:bg-muted data-[focus-visible]:outline-(--color-control-edge)\";\nconst nameClass =\n \"min-w-0 flex-1 truncate font-medium text-text-secondary group-data-[selected]/row:text-foreground group-data-[focused]/row:text-foreground group-data-[hovered]/row:text-foreground\";\n\n/* A flag glyph, or (where the platform draws none) the ISO code in the same slot. */\nconst flagClass = \"w-6 flex-none text-center text-base leading-none\";\nconst codeClass = \"w-6 flex-none text-center text-xs font-semibold leading-none tracking-wide text-muted-foreground\";\n\nconst emptyClass = \"px-3 py-3 text-sm text-muted-foreground\";\n\nconst SORTED = [...PHONE_COUNTRIES].sort((a, b) => a.name.localeCompare(b.name));\nconst textValueOf = (entry: PhoneCountry) => `${entry.name} ${entry.iso} +${entry.dial}`;\n\nexport const PhoneInput = forwardRef<HTMLInputElement, PhoneInputProps>(function PhoneInput(\n {\n label,\n labelHidden = false,\n value,\n defaultValue,\n onValueChange,\n country: countryProp,\n defaultCountry = \"US\",\n onCountryChange,\n countries,\n description,\n error,\n validate = true,\n flags = \"auto\",\n disabled = false,\n required,\n name,\n id,\n className,\n onBlur,\n },\n forwardedRef,\n) {\n const pool = useMemo(\n () =>\n countries?.length ? PHONE_COUNTRIES.filter((entry) => countries.includes(entry.iso)) : PHONE_COUNTRIES,\n [countries],\n );\n const list = useMemo(() => SORTED.filter((entry) => pool.includes(entry)), [pool]);\n const items = useMemo(() => list.map((entry) => ({ ...entry, id: entry.iso })), [list]);\n const canDrawFlags = useFlagEmoji();\n const flagEmoji = flags === \"auto\" ? canDrawFlags : flags === \"emoji\";\n const fallback = phoneCountryByIso(defaultCountry) ?? PHONE_COUNTRIES[0]!;\n\n const [state, setState] = useState(() => {\n const parsed = parsePhoneNumber(value ?? defaultValue ?? \"\", pool);\n return parsed\n ? { iso: parsed.country.iso, digits: parsed.national }\n : { iso: fallback.iso, digits: \"\" };\n });\n const iso = countryProp ?? state.iso;\n const current = phoneCountryByIso(iso) ?? fallback;\n const digits = state.digits;\n const e164 = toE164(current, digits);\n\n // A controlled value that differs from what the field last produced is read back in.\n const [seenValue, setSeenValue] = useState(value);\n if (value !== seenValue) {\n setSeenValue(value);\n if (value !== undefined && value !== e164) {\n const parsed = parsePhoneNumber(value, pool);\n setState(parsed ? { iso: parsed.country.iso, digits: parsed.national } : { iso, digits: \"\" });\n }\n }\n\n const formatted = formatNational(current, digits);\n const status = phoneStatus(current, digits);\n const [touched, setTouched] = useState(false);\n // The check animates in only for a number that became valid after mount.\n const [edited, setEdited] = useState(false);\n const lengths = lengthsOf(current);\n const lengthCopy =\n lengths.length === 1\n ? `${lengths[0]}`\n : `${lengths.slice(0, -1).join(\", \")} or ${lengths[lengths.length - 1]}`;\n const builtIn =\n validate && touched && (status === \"incomplete\" || status === \"too-long\")\n ? `Numbers in ${current.name} have ${lengthCopy} digits`\n : undefined;\n const message = error ?? builtIn;\n\n /* The guide: the rest of the example number in the shape this country expects, drawn faintly after what was typed. */\n const guide = useMemo(() => {\n const [trunk, rest] = splitTrunk(current, digits);\n if (rest.length >= current.example.length) return \"\";\n const full = formatNational(current, trunk + rest + current.example.slice(rest.length));\n return full.startsWith(formatted) ? full.slice(formatted.length) : \"\";\n }, [current, digits, formatted]);\n\n const inputRef = useRef<HTMLInputElement>(null);\n useImperativeHandle(forwardedRef, () => inputRef.current as HTMLInputElement);\n const shellRef = useRef<HTMLDivElement>(null);\n const pendingCaret = useRef<number | null>(null);\n const [, rerender] = useReducer((count: number) => count + 1, 0);\n const [announcement, setAnnouncement] = useState(\"\");\n\n const emit = useCallback(\n (entry: PhoneCountry, nextDigits: string) => {\n const nextStatus = phoneStatus(entry, nextDigits);\n onValueChange?.(toE164(entry, nextDigits), {\n country: entry,\n formatted: formatNational(entry, nextDigits),\n status: nextStatus,\n valid: nextStatus === \"valid\",\n });\n },\n [onValueChange],\n );\n\n function setNumber(nextDigits: string, caretDigits: number | null, entry = current) {\n const capped = capDigits(entry, nextDigits);\n pendingCaret.current = caretDigits === null ? null : Math.min(caretDigits, capped.length);\n if (entry.iso !== current.iso) onCountryChange?.(entry.iso);\n // Nothing changed (a refused digit): React restores the old text after this handler, so the caret is placed on the next render.\n if (capped === digits && entry.iso === current.iso) {\n rerender();\n return;\n }\n setEdited(true);\n setState({ iso: entry.iso, digits: capped });\n emit(entry, capped);\n }\n\n useIsomorphicLayoutEffect(() => {\n const input = inputRef.current;\n const count = pendingCaret.current;\n pendingCaret.current = null;\n if (!input || count === null || document.activeElement !== input) return;\n const position = caretAfterDigits(input.value, count);\n input.setSelectionRange(position, position);\n });\n\n /* ---------------------------------------------- Number entry ---------------------------------------------- */\n\n function onNumberChange(input: HTMLInputElement) {\n const raw = input.value;\n // Autofill and dropped text arrive as a change: an international number chooses its own country.\n if (raw.includes(\"+\") || (/^\\s*00/.test(raw) && onlyDigits(raw).length > 6)) {\n const parsed = parsePhoneNumber(raw.slice(Math.max(0, raw.indexOf(\"+\"))), pool);\n if (parsed?.national) {\n if (parsed.country.iso !== current.iso) setAnnouncement(`Country set to ${parsed.country.name}`);\n setNumber(parsed.national, parsed.national.length, parsed.country);\n return;\n }\n }\n const caret = input.selectionStart ?? raw.length;\n setNumber(onlyDigits(raw), onlyDigits(raw.slice(0, caret)).length);\n }\n\n function onNumberKeyDown(event: KeyboardEvent<HTMLInputElement>) {\n const input = event.currentTarget;\n if (event.key === \"+\" && !event.metaKey && !event.ctrlKey) {\n event.preventDefault();\n setQuery(\"+\");\n setOpen(true);\n return;\n }\n const start = input.selectionStart ?? 0;\n const end = input.selectionEnd ?? 0;\n if (start !== end || event.metaKey || event.ctrlKey || event.altKey) return;\n // Deleting a separator deletes the digit beside it instead of doing nothing.\n if (event.key === \"Backspace\" && start > 0 && !/\\d/.test(input.value[start - 1]!)) {\n event.preventDefault();\n const index = onlyDigits(input.value.slice(0, start)).length;\n if (index > 0) setNumber(digits.slice(0, index - 1) + digits.slice(index), index - 1);\n }\n if (event.key === \"Delete\" && start < input.value.length && !/\\d/.test(input.value[start]!)) {\n event.preventDefault();\n const index = onlyDigits(input.value.slice(0, start)).length;\n setNumber(digits.slice(0, index) + digits.slice(index + 1), index);\n }\n }\n\n function onPaste(event: ClipboardEvent<HTMLInputElement>) {\n const text = event.clipboardData.getData(\"text\");\n if (!text) return;\n event.preventDefault();\n const input = event.currentTarget;\n const parsed = /^\\s*(\\+|00)/.test(text) ? parsePhoneNumber(text, pool) : null;\n if (parsed) {\n if (parsed.country.iso !== current.iso) setAnnouncement(`Country set to ${parsed.country.name}`);\n setNumber(parsed.national, parsed.national.length, parsed.country);\n return;\n }\n let pasted = onlyDigits(text);\n // \"1 415 555 0132\" pasted into a US field: the leading calling code goes when the number would not fit otherwise.\n if (pasted.startsWith(current.dial) && pasted.length > capDigits(current, pasted).length) {\n pasted = pasted.slice(current.dial.length);\n }\n // A whole number replaces what was there; a fragment goes in at the caret.\n if (pasted.length >= Math.min(...lengthsOf(current))) {\n setNumber(pasted, pasted.length);\n return;\n }\n const from = onlyDigits(input.value.slice(0, input.selectionStart ?? 0)).length;\n const to = onlyDigits(input.value.slice(0, input.selectionEnd ?? 0)).length;\n setNumber(digits.slice(0, from) + pasted + digits.slice(to), from + pasted.length);\n }\n\n /* ---------------------------------------------- Country picker ---------------------------------------------- */\n\n const [open, setOpen] = useState(false);\n const [query, setQuery] = useState(\"\");\n const openRef = useRef(false);\n openRef.current = open;\n\n function pick(key: Key | undefined) {\n const entry = list.find((item) => item.iso === key);\n if (!entry) return;\n if (entry.iso !== current.iso) {\n const nextDigits = capDigits(entry, digits);\n if (countryProp === undefined) setState({ iso: entry.iso, digits: nextDigits });\n else setState((last) => ({ ...last, digits: nextDigits }));\n onCountryChange?.(entry.iso);\n emit(entry, nextDigits);\n setEdited(true);\n setAnnouncement(`${entry.name}, +${entry.dial}`);\n }\n setOpen(false);\n // Back to the number, where the next keystroke belongs.\n requestAnimationFrame(() => {\n if (!openRef.current) inputRef.current?.focus({ preventScroll: true });\n });\n }\n\n const filter = useCallback((textValue: string, inputValue: string) => {\n const entry = SORTED.find((item) => textValueOf(item) === textValue);\n return entry ? matchesCountry(entry, inputValue) : true;\n }, []);\n\n /* ---------------------------------------------- Field wiring ---------------------------------------------- */\n\n const field = useFieldWiring({\n enabled: true,\n id,\n describedBy: undefined,\n invalid: undefined,\n description,\n error: message,\n });\n const showCheck = status === \"valid\";\n\n const control = (\n <AriaGroup\n ref={shellRef}\n role=\"presentation\"\n isDisabled={disabled}\n isInvalid={Boolean(message)}\n data-slot=\"phone-input-shell\"\n className={shellClass}\n >\n <AriaDialogTrigger\n isOpen={open}\n onOpenChange={(next) => {\n setOpen(next);\n if (next) setQuery(\"\");\n }}\n >\n <AriaButton\n isDisabled={disabled}\n aria-label={`Country, ${current.name} +${current.dial}`}\n data-slot=\"phone-input-country\"\n className={triggerClass}\n >\n <span\n aria-hidden=\"true\"\n data-slot=\"phone-input-flag\"\n data-glyph={flagEmoji ? \"flag\" : \"code\"}\n className={flagEmoji ? flagClass : codeClass}\n >\n {flagEmoji ? flagOf(current.iso) : current.iso}\n </span>\n <SwapText swapKey={current.iso} motion=\"roll\" className=\"inline-flex\">\n <span data-slot=\"phone-input-dial\">+{current.dial}</span>\n </SwapText>\n <svg\n aria-hidden=\"true\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.75\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className=\"size-4 text-muted-foreground motion-safe:transition-[rotate] motion-safe:duration-(--duration-gentle) motion-safe:ease-gentle group-aria-expanded/trigger:rotate-180\"\n >\n <path d=\"m4 6 4 4 4-4\" />\n </svg>\n </AriaButton>\n <AriaPopover\n triggerRef={shellRef}\n placement=\"bottom start\"\n offset={8}\n data-slot=\"phone-input-popover\"\n className={cnAll(selectPopoverVariants({ reveal: \"fade\" }), \"flex w-80 max-w-[calc(100vw-2rem)] flex-col overflow-hidden\")}\n >\n <AriaDialog aria-label=\"Country\" className=\"flex min-h-0 flex-col gap-1 outline-none\">\n <AriaAutocomplete filter={filter} inputValue={query} onInputChange={setQuery}>\n <AriaSearchField\n aria-label=\"Search countries or calling codes\"\n autoFocus\n className={searchClass}\n >\n <svg\n aria-hidden=\"true\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={2}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className=\"size-4 flex-none text-muted-foreground\"\n >\n <circle cx=\"11\" cy=\"11\" r=\"8\" />\n <path d=\"m21 21-4.34-4.34\" />\n </svg>\n <AriaInput\n placeholder=\"Country or code\"\n data-slot=\"phone-input-search\"\n className=\"min-w-0 flex-1 appearance-none border-0 bg-transparent p-0 font-sans text-sm text-foreground outline-none placeholder:text-muted-foreground [&::-webkit-search-cancel-button]:hidden\"\n />\n <svg\n aria-hidden=\"true\"\n data-slot=\"phone-input-search-chevron\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.75\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className=\"size-4 flex-none text-muted-foreground\"\n >\n <path d=\"m4 10 4-4 4 4\" />\n </svg>\n </AriaSearchField>\n {/* The list scrolls, and a scrolling region must be reachable by keyboard (the search field drives the options with arrow keys; this stop is for scrolling). */}\n <ScrollArea\n label=\"Country list\"\n className=\"max-h-72 rounded-control\"\n viewportClassName=\"rounded-control focus-visible:outline-(--color-control-edge)\"\n >\n <AriaListBox\n aria-label=\"Countries\"\n selectionMode=\"single\"\n selectedKeys={[current.iso]}\n // Escape closes the list instead of clearing the chosen country. Choosing the chosen country again\n // empties the selection, which is read as \"keep it\": the list just closes.\n escapeKeyBehavior=\"none\"\n onSelectionChange={(keys) => {\n if (keys === \"all\") return;\n pick([...keys][0] ?? current.iso);\n }}\n items={items}\n renderEmptyState={() => <p className={emptyClass}>No countries match</p>}\n className=\"outline-none\"\n >\n {(entry) => (\n <SelectItem id={entry.id} textValue={textValueOf(entry)} className={rowClass}>\n <span\n aria-hidden=\"true\"\n data-slot=\"phone-input-row-flag\"\n data-glyph={flagEmoji ? \"flag\" : \"code\"}\n className={flagEmoji ? flagClass : codeClass}\n >\n {flagEmoji ? flagOf(entry.iso) : entry.iso}\n </span>\n <span className={nameClass}>{entry.name}</span>\n <span className=\"flex-none text-xs tabular-nums text-muted-foreground\">\n +{entry.dial}\n </span>\n </SelectItem>\n )}\n </AriaListBox>\n </ScrollArea>\n </AriaAutocomplete>\n </AriaDialog>\n </AriaPopover>\n </AriaDialogTrigger>\n\n <div className=\"relative flex h-full min-w-0 flex-1 items-center border-s-(length:--border-width) border-border\">\n <span\n aria-hidden=\"true\"\n data-slot=\"phone-input-guide\"\n className=\"pointer-events-none absolute inset-0 flex items-center overflow-hidden whitespace-pre px-3 font-sans text-base tabular-nums text-muted-foreground\"\n >\n <span className=\"invisible\">{formatted}</span>\n {guide}\n </span>\n <AriaInput\n ref={inputRef}\n {...field.controlProps}\n data-slot=\"phone-input-number\"\n type=\"tel\"\n inputMode=\"tel\"\n autoComplete=\"tel\"\n value={formatted}\n disabled={disabled}\n required={required}\n aria-label={labelHidden ? label : undefined}\n onChange={(event) => onNumberChange(event.currentTarget)}\n onKeyDown={onNumberKeyDown}\n onPaste={onPaste}\n onBlur={(event) => {\n setTouched(digits.length > 0);\n onBlur?.(event);\n }}\n className={numberClass}\n />\n {showCheck ? (\n <svg\n aria-hidden=\"true\"\n data-slot=\"phone-input-valid\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={cn(\n \"pointer-events-none absolute end-3 size-4 text-success-strong\",\n edited && \"motion-safe:animate-select-check-in\",\n )}\n >\n <path d=\"m3.5 8.5 3 3 6-6\" />\n </svg>\n ) : null}\n </div>\n </AriaGroup>\n );\n\n return (\n <div className={cn(\"w-full min-w-0 font-sans\", className)} data-slot=\"phone-input\" data-country={current.iso}>\n <FieldShell\n label={labelHidden ? null : label}\n controlId={field.controlId}\n description={description}\n hintId={field.hintId}\n error={message}\n errorId={field.errorId}\n >\n {control}\n </FieldShell>\n {name ? <input type=\"hidden\" name={name} value={e164} /> : null}\n <span className=\"sr-only\" role=\"status\" aria-live=\"polite\">\n {announcement || (showCheck ? `Valid ${current.name} number` : \"\")}\n </span>\n </div>\n );\n});\n\nPhoneInput.displayName = \"PhoneInput\";\n"],"mappings":"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"}
|
|
@@ -51,6 +51,18 @@ interface PopoverProps$1 extends Omit<PopoverProps, "className" | "style" | "chi
|
|
|
51
51
|
* @default "rise"
|
|
52
52
|
*/
|
|
53
53
|
entrance?: "rise" | "none";
|
|
54
|
+
/**
|
|
55
|
+
* A heading at the top of the panel. Together with `description` and
|
|
56
|
+
* `actionLabel` it makes the message popover: a dark title, grey body text
|
|
57
|
+
* and a full-width button, with `children` rendered below them.
|
|
58
|
+
*/
|
|
59
|
+
title?: ReactNode;
|
|
60
|
+
/** Grey body text under the title. */
|
|
61
|
+
description?: ReactNode;
|
|
62
|
+
/** The label of the full-width button at the bottom. Pressing it runs `onAction` and closes the popover. */
|
|
63
|
+
actionLabel?: ReactNode;
|
|
64
|
+
/** Runs when the action button is pressed, before the popover closes. */
|
|
65
|
+
onAction?: () => void;
|
|
54
66
|
}
|
|
55
67
|
declare const Popover$1: import("react").ForwardRefExoticComponent<PopoverProps$1 & import("react").RefAttributes<HTMLElement>>;
|
|
56
68
|
//#endregion
|
package/dist/popover/popover.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { cnAll } from "../lib/cn.js";
|
|
3
|
+
import { Button as Button$1 } from "../button/button.js";
|
|
3
4
|
import { cva } from "class-variance-authority";
|
|
4
|
-
import { forwardRef } from "react";
|
|
5
|
-
import { DialogTrigger as PopoverTrigger, Popover } from "react-aria-components";
|
|
6
|
-
import { jsx } from "react/jsx-runtime";
|
|
5
|
+
import { forwardRef, useContext } from "react";
|
|
6
|
+
import { DialogTrigger as PopoverTrigger, OverlayTriggerStateContext, Popover } from "react-aria-components";
|
|
7
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
8
|
//#region src/popover/popover.tsx
|
|
8
9
|
/**
|
|
9
10
|
* Popover, ported from the legacy shadcn/Radix component.
|
|
@@ -26,20 +27,43 @@ import { jsx } from "react/jsx-runtime";
|
|
|
26
27
|
* already exists under another name.
|
|
27
28
|
*/
|
|
28
29
|
const popoverVariants = cva([
|
|
29
|
-
"w-72 rounded-
|
|
30
|
+
"w-72 rounded-panel border-(length:--border-width) border-border p-4",
|
|
30
31
|
"bg-card text-card-foreground shadow-floating",
|
|
31
32
|
"box-border font-sans text-sm leading-(--leading-normal)",
|
|
32
33
|
"outline-none"
|
|
33
34
|
]);
|
|
34
|
-
const Popover$1 = forwardRef(function Popover$2({ children, className, placement = "bottom", offset = 4, entrance = "rise", ...props }, ref) {
|
|
35
|
-
|
|
35
|
+
const Popover$1 = forwardRef(function Popover$2({ children, className, placement = "bottom", offset = 4, entrance = "rise", title, description, actionLabel, onAction, ...props }, ref) {
|
|
36
|
+
const state = useContext(OverlayTriggerStateContext);
|
|
37
|
+
return /* @__PURE__ */ jsxs(Popover, {
|
|
36
38
|
ref,
|
|
37
39
|
...props,
|
|
38
40
|
placement,
|
|
39
41
|
offset,
|
|
40
42
|
"data-slot": "popover",
|
|
41
43
|
className: cnAll(popoverVariants(), entrance === "rise" && "popover-motion", className),
|
|
42
|
-
children
|
|
44
|
+
children: [
|
|
45
|
+
title == null ? null : /* @__PURE__ */ jsx("h3", {
|
|
46
|
+
"data-slot": "popover-title",
|
|
47
|
+
className: "m-0 text-sm leading-(--leading-tight) font-semibold text-foreground",
|
|
48
|
+
children: title
|
|
49
|
+
}),
|
|
50
|
+
description == null ? null : /* @__PURE__ */ jsx("p", {
|
|
51
|
+
"data-slot": "popover-description",
|
|
52
|
+
className: "m-0 mt-1 text-sm leading-(--leading-normal) text-text-secondary",
|
|
53
|
+
children: description
|
|
54
|
+
}),
|
|
55
|
+
actionLabel == null ? null : /* @__PURE__ */ jsx(Button$1, {
|
|
56
|
+
variant: "secondary",
|
|
57
|
+
size: "sm",
|
|
58
|
+
className: "mt-4 w-full",
|
|
59
|
+
onPress: () => {
|
|
60
|
+
onAction?.();
|
|
61
|
+
state?.close();
|
|
62
|
+
},
|
|
63
|
+
children: actionLabel
|
|
64
|
+
}),
|
|
65
|
+
children
|
|
66
|
+
]
|
|
43
67
|
});
|
|
44
68
|
});
|
|
45
69
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"popover.js","names":["Popover","AriaPopover"],"sources":["../../src/popover/popover.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva } from \"class-variance-authority\";\nimport { forwardRef, type ReactNode } from \"react\";\nimport {\n Popover as AriaPopover,\n type PopoverProps as AriaPopoverProps,\n} from \"react-aria-components\";\n\nimport { cnAll } from \"../lib/cn.js\";\n\n/**\n * Popover, ported from the legacy shadcn/Radix component.\n *\n * Census: 2 files, 2 uses. `asChild` on the trigger twice, `className`\n * twice, `align=\"start\"` once. `PopoverAnchor` is exported by the legacy\n * and imported by nobody, so it is not ported; neither are `side`,\n * `sideOffset`, `alignOffset`, `open` / `onOpenChange`, `modal`,\n * `collisionPadding` or `avoidCollisions` -- zero call sites each. Both\n * existing uses are uncontrolled.\n *\n * IT GAINS AN ACCESSIBLE NAME. The legacy renders `role=\"dialog\"` with no\n * `aria-label` and no `aria-labelledby`, so both call sites announce an\n * unnamed dialog. React Aria labels the popover from its trigger\n * automatically once the two are paired, which is the only way to build\n * one here.\n *\n * The surface is `card`: in the legacy theme `--popover` is byte-identical\n * to `--card` in both themes, so the role the migration plan proposed\n * already exists under another name.\n */\nexport const popoverVariants = cva([\n // w-72 (288px) is the legacy default width, as a spacing-scale step.\n \"w-72 rounded-
|
|
1
|
+
{"version":3,"file":"popover.js","names":["Popover","AriaPopover","Button"],"sources":["../../src/popover/popover.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva } from \"class-variance-authority\";\nimport { forwardRef, useContext, type ReactNode } from \"react\";\nimport {\n OverlayTriggerStateContext,\n Popover as AriaPopover,\n type PopoverProps as AriaPopoverProps,\n} from \"react-aria-components\";\n\nimport { Button } from \"../button/button.js\";\nimport { cnAll } from \"../lib/cn.js\";\n\n/**\n * Popover, ported from the legacy shadcn/Radix component.\n *\n * Census: 2 files, 2 uses. `asChild` on the trigger twice, `className`\n * twice, `align=\"start\"` once. `PopoverAnchor` is exported by the legacy\n * and imported by nobody, so it is not ported; neither are `side`,\n * `sideOffset`, `alignOffset`, `open` / `onOpenChange`, `modal`,\n * `collisionPadding` or `avoidCollisions` -- zero call sites each. Both\n * existing uses are uncontrolled.\n *\n * IT GAINS AN ACCESSIBLE NAME. The legacy renders `role=\"dialog\"` with no\n * `aria-label` and no `aria-labelledby`, so both call sites announce an\n * unnamed dialog. React Aria labels the popover from its trigger\n * automatically once the two are paired, which is the only way to build\n * one here.\n *\n * The surface is `card`: in the legacy theme `--popover` is byte-identical\n * to `--card` in both themes, so the role the migration plan proposed\n * already exists under another name.\n */\nexport const popoverVariants = cva([\n // w-72 (288px) is the legacy default width, as a spacing-scale step.\n \"w-72 rounded-panel border-(length:--border-width) border-border p-4\",\n \"bg-card text-card-foreground shadow-floating\",\n // The reset in @seekalfred/styles is OPT-IN, so a consumer may load\n // none: an unstyled document renders text in a serif, and content-box\n // would add the 16px padding on top of the width.\n \"box-border font-sans text-sm leading-(--leading-normal)\",\n // React Aria gives the popover tabindex=-1 and focuses it on open, so it\n // can take a focus ring it does not want.\n \"outline-none\",\n]);\n\n/**\n * Content anchored to a trigger.\n *\n * @example\n * <PopoverTrigger>\n * <Button>Pick a date</Button>\n * <Popover placement=\"bottom start\">...</Popover>\n * </PopoverTrigger>\n */\nexport interface PopoverProps\n extends Omit<AriaPopoverProps, \"className\" | \"style\" | \"children\"> {\n children?: ReactNode;\n /** Extra classes. Merged last, so they win conflicts with ours. */\n className?: string;\n /**\n * Where it opens, as React Aria's single `placement` string. The legacy\n * split this across `side` and `align`; its one non-default call site is\n * `align=\"start\"`, which is `\"bottom start\"` here.\n *\n * @default \"bottom\"\n */\n placement?: AriaPopoverProps[\"placement\"];\n /**\n * How it comes in: `rise` grows out of the trigger (offset toward it, a little\n * small, then settles on a spring) and leaves quicker; `none` leaves the entrance\n * to the caller, as a picker that animates its own popover does.\n *\n * @default \"rise\"\n */\n entrance?: \"rise\" | \"none\";\n /**\n * A heading at the top of the panel. Together with `description` and\n * `actionLabel` it makes the message popover: a dark title, grey body text\n * and a full-width button, with `children` rendered below them.\n */\n title?: ReactNode;\n /** Grey body text under the title. */\n description?: ReactNode;\n /** The label of the full-width button at the bottom. Pressing it runs `onAction` and closes the popover. */\n actionLabel?: ReactNode;\n /** Runs when the action button is pressed, before the popover closes. */\n onAction?: () => void;\n}\n\nexport const Popover = forwardRef<HTMLElement, PopoverProps>(function Popover(\n {\n children,\n className,\n placement = \"bottom\",\n offset = 4,\n entrance = \"rise\",\n title,\n description,\n actionLabel,\n onAction,\n ...props\n },\n ref,\n) {\n // The open state the enclosing PopoverTrigger owns, so the action button can close it.\n const state = useContext(OverlayTriggerStateContext);\n return (\n <AriaPopover\n ref={ref}\n {...props}\n placement={placement}\n offset={offset}\n data-slot=\"popover\"\n className={cnAll(popoverVariants(), entrance === \"rise\" && \"popover-motion\", className)}\n >\n {/*\n No inner Dialog. Wrapping the content in one is the common React\n Aria idiom and it was in the first draft here -- but deleting it\n broke no assertion, so it was doing nothing: a Popover under a\n DialogTrigger already gets role=\"dialog\", a focus scope, and\n aria-labelledby pointing at the trigger. The wrapper was one more\n element and one more className to explain.\n */}\n {title == null ? null : (\n <h3\n data-slot=\"popover-title\"\n className=\"m-0 text-sm leading-(--leading-tight) font-semibold text-foreground\"\n >\n {title}\n </h3>\n )}\n {description == null ? null : (\n <p\n data-slot=\"popover-description\"\n className=\"m-0 mt-1 text-sm leading-(--leading-normal) text-text-secondary\"\n >\n {description}\n </p>\n )}\n {actionLabel == null ? null : (\n <Button\n variant=\"secondary\"\n size=\"sm\"\n className=\"mt-4 w-full\"\n onPress={() => {\n onAction?.();\n state?.close();\n }}\n >\n {actionLabel}\n </Button>\n )}\n {children}\n </AriaPopover>\n );\n});\n\n/**\n * Pairs a trigger with a popover and owns the open state.\n *\n * React Aria's `DialogTrigger`, renamed for the migration: it takes the\n * trigger element directly as its first child, which is what `asChild` was\n * doing on both legacy call sites.\n */\nexport { DialogTrigger as PopoverTrigger } from \"react-aria-components\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiCA,MAAa,kBAAkB,IAAI;CAEjC;CACA;CAIA;CAGA;AACF,CAAC;AA8CD,MAAaA,YAAU,WAAsC,SAASA,UACpE,EACE,UACA,WACA,YAAY,UACZ,SAAS,GACT,WAAW,QACX,OACA,aACA,aACA,UACA,GAAG,SAEL,KACA;CAEA,MAAM,QAAQ,WAAW,0BAA0B;CACnD,OACE,qBAACC,SAAD;EACO;EACL,GAAI;EACO;EACH;EACR,aAAU;EACV,WAAW,MAAM,gBAAgB,GAAG,aAAa,UAAU,kBAAkB,SAAS;EANxF,UAAA;GAgBG,SAAS,OAAO,OACf,oBAAC,MAAD;IACE,aAAU;IACV,WAAU;IAET,UAAA;GACC,CAAA;GAEL,eAAe,OAAO,OACrB,oBAAC,KAAD;IACE,aAAU;IACV,WAAU;IAET,UAAA;GACA,CAAA;GAEJ,eAAe,OAAO,OACrB,oBAACC,UAAD;IACE,SAAQ;IACR,MAAK;IACL,WAAU;IACV,eAAe;KACb,WAAW;KACX,OAAO,MAAM;IACf;IAEC,UAAA;GACK,CAAA;GAET;EACU;;AAEjB,CAAC"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { cn, cnAll } from "../lib/cn.js";
|
|
3
|
-
import { SwapText } from "../lib/swap-text.js";
|
|
4
3
|
import { useAfter, useCountTo } from "../lib/count-to.js";
|
|
4
|
+
import { SwapText } from "../lib/swap-text.js";
|
|
5
5
|
import { cva } from "class-variance-authority";
|
|
6
6
|
import { forwardRef } from "react";
|
|
7
7
|
import { Label, ProgressBar } from "react-aria-components";
|