@krizaka/ui 3.45.0 → 3.48.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/dist/account-menu.d.ts +2 -111
- package/dist/account-menu.js +39 -118
- package/dist/account-menu.js.map +1 -1
- package/dist/account-settings.d.ts +163 -0
- package/dist/account-settings.js +354 -0
- package/dist/account-settings.js.map +1 -0
- package/dist/agenda.d.ts +136 -0
- package/dist/agenda.js +90 -0
- package/dist/agenda.js.map +1 -0
- package/dist/avatar-iajODANU.d.ts +114 -0
- package/dist/chunk-3D2YI63L.js +27 -0
- package/dist/chunk-3D2YI63L.js.map +1 -0
- package/dist/chunk-3RVFD3UX.js +85 -0
- package/dist/chunk-3RVFD3UX.js.map +1 -0
- package/dist/chunk-S6NYGOJT.js +22 -0
- package/dist/chunk-S6NYGOJT.js.map +1 -0
- package/dist/docs-layout.js +13 -32
- package/dist/docs-layout.js.map +1 -1
- package/dist/docs-nav.js +1 -1
- package/dist/drop-overlay.d.ts +31 -0
- package/dist/drop-overlay.js +86 -0
- package/dist/drop-overlay.js.map +1 -0
- package/dist/motion.css +27 -0
- package/dist/nav-bar.d.ts +41 -1
- package/dist/nav-bar.js +26 -1
- package/dist/nav-bar.js.map +1 -1
- package/dist/settings-layout.d.ts +59 -4
- package/dist/settings-layout.js +229 -24
- package/dist/settings-layout.js.map +1 -1
- package/dist/upload-queue.d.ts +67 -0
- package/dist/upload-queue.js +76 -0
- package/dist/upload-queue.js.map +1 -0
- package/package.json +31 -5
- package/registry/account-menu.json +2 -2
- package/registry/account-settings.json +721 -0
- package/registry/achievement.json +1 -1
- package/registry/action-bar.json +1 -1
- package/registry/activity-feed.json +1 -1
- package/registry/agenda.json +293 -0
- package/registry/alert.json +1 -1
- package/registry/anatomy-plate.json +1 -1
- package/registry/app-header.json +1 -1
- package/registry/app-shell.json +1 -1
- package/registry/argument-card.json +1 -1
- package/registry/audio-player.json +1 -1
- package/registry/auth-layout.json +1 -1
- package/registry/avatar.json +1 -1
- package/registry/badge.json +1 -1
- package/registry/bird-plate.json +1 -1
- package/registry/button.json +1 -1
- package/registry/captcha.json +1 -1
- package/registry/card-grid.json +1 -1
- package/registry/card.json +1 -1
- package/registry/catalogue-page.json +1 -1
- package/registry/changelog-page.json +1 -1
- package/registry/chat.json +3 -3
- package/registry/checkbox.json +1 -1
- package/registry/chip-link.json +1 -1
- package/registry/chip-row.json +1 -1
- package/registry/chip.json +1 -1
- package/registry/choice-grid.json +1 -1
- package/registry/code-block.json +1 -1
- package/registry/combobox.json +1 -1
- package/registry/command.json +1 -1
- package/registry/comparison-table.json +1 -1
- package/registry/component-page.json +1 -1
- package/registry/confirm-button.json +1 -1
- package/registry/contact-page.json +1 -1
- package/registry/continue-prompt.json +1 -1
- package/registry/countdown.json +1 -1
- package/registry/cover-hero.json +1 -1
- package/registry/cta-panel.json +1 -1
- package/registry/dashboard-layout.json +1 -1
- package/registry/data-table.json +3 -3
- package/registry/date-picker.json +1 -1
- package/registry/decision-list.json +1 -1
- package/registry/description-list.json +1 -1
- package/registry/detail-layout.json +1 -1
- package/registry/dialog.json +1 -1
- package/registry/docs-article-page.json +1 -1
- package/registry/docs-hub-page.json +1 -1
- package/registry/docs-layout.json +1 -1
- package/registry/docs-nav.json +1 -1
- package/registry/document-viewer.json +1 -1
- package/registry/drop-overlay.json +182 -0
- package/registry/dropdown-menu.json +1 -1
- package/registry/elephant.json +2 -2
- package/registry/empty-state.json +1 -1
- package/registry/engine-flow.json +1 -1
- package/registry/error-page.json +1 -1
- package/registry/examples/account-settings/avatar-picker.d.ts +4 -0
- package/registry/examples/account-settings/avatar-picker.js +15 -0
- package/registry/examples/account-settings/avatar-picker.tsx +15 -0
- package/registry/examples/account-settings/choice-cards.d.ts +4 -0
- package/registry/examples/account-settings/choice-cards.js +24 -0
- package/registry/examples/account-settings/choice-cards.tsx +20 -0
- package/registry/examples/account-settings/language-picker.d.ts +4 -0
- package/registry/examples/account-settings/language-picker.js +25 -0
- package/registry/examples/account-settings/language-picker.tsx +21 -0
- package/registry/examples/account-settings/patterns.d.ts +4 -0
- package/registry/examples/account-settings/patterns.js +29 -0
- package/registry/examples/account-settings/patterns.tsx +39 -0
- package/registry/examples/account-settings/profile.d.ts +4 -0
- package/registry/examples/account-settings/profile.js +70 -0
- package/registry/examples/account-settings/profile.tsx +80 -0
- package/registry/examples/agenda/day.d.ts +4 -0
- package/registry/examples/agenda/day.js +43 -0
- package/registry/examples/agenda/day.tsx +35 -0
- package/registry/examples/drop-overlay/default.d.ts +4 -0
- package/registry/examples/drop-overlay/default.js +20 -0
- package/registry/examples/drop-overlay/default.tsx +18 -0
- package/registry/examples/upload-queue/default.d.ts +4 -0
- package/registry/examples/upload-queue/default.js +22 -0
- package/registry/examples/upload-queue/default.tsx +20 -0
- package/registry/examples/upload-queue/tray.d.ts +4 -0
- package/registry/examples/upload-queue/tray.js +21 -0
- package/registry/examples/upload-queue/tray.tsx +19 -0
- package/registry/field.json +1 -1
- package/registry/file-upload.json +1 -1
- package/registry/filter-bar.json +1 -1
- package/registry/flock.json +1 -1
- package/registry/footnote.json +1 -1
- package/registry/force-update-screen.json +1 -1
- package/registry/form.json +3 -3
- package/registry/gallery-page.json +1 -1
- package/registry/genui.json +1 -1
- package/registry/glow-card.json +1 -1
- package/registry/goal-gauge.json +1 -1
- package/registry/how-it-works-page.json +1 -1
- package/registry/image-cropper.json +1 -1
- package/registry/index.json +148 -7
- package/registry/iso.json +1 -1
- package/registry/kanban.json +1 -1
- package/registry/kbd.json +1 -1
- package/registry/landing-page.json +1 -1
- package/registry/landscape.json +1 -1
- package/registry/language-switch.json +1 -1
- package/registry/legal-page.json +1 -1
- package/registry/link-card.json +1 -1
- package/registry/list.json +3 -3
- package/registry/loader.json +1 -1
- package/registry/longform-page.json +1 -1
- package/registry/maintenance-screen.json +1 -1
- package/registry/map.json +2 -2
- package/registry/markdown.json +1 -1
- package/registry/marketing-layout.json +1 -1
- package/registry/marquee.json +1 -1
- package/registry/mascot-quote.json +3 -3
- package/registry/mascot-state.json +3 -3
- package/registry/mascots.json +3 -3
- package/registry/match-dialog.json +1 -1
- package/registry/media-capture.json +1 -1
- package/registry/media-gallery.json +1 -1
- package/registry/media-grid.json +1 -1
- package/registry/meta-row.json +1 -1
- package/registry/meter.json +1 -1
- package/registry/nav-bar.json +93 -5
- package/registry/number-ticker.json +1 -1
- package/registry/opening-hours.json +1 -1
- package/registry/page-header.json +1 -1
- package/registry/page-hero.json +1 -1
- package/registry/page-index.json +1 -1
- package/registry/page.json +1 -1
- package/registry/pagination.json +1 -1
- package/registry/photo-grid.json +1 -1
- package/registry/pointer-light.json +1 -1
- package/registry/popover.json +1 -1
- package/registry/pricing-page.json +1 -1
- package/registry/product-page.json +1 -1
- package/registry/profile-card.json +1 -1
- package/registry/progress.json +1 -1
- package/registry/promise-card.json +1 -1
- package/registry/radio-group.json +93 -5
- package/registry/rail-nav.json +2 -2
- package/registry/recipes.json +1 -1
- package/registry/router.json +1 -1
- package/registry/scroll-stage.json +1 -1
- package/registry/search-field.json +1 -1
- package/registry/section-backdrop.json +1 -1
- package/registry/separator.json +1 -1
- package/registry/service-card.json +1 -1
- package/registry/settings-layout.json +34 -4
- package/registry/showcase.json +1 -1
- package/registry/site-footer.json +2 -2
- package/registry/sites.json +1 -1
- package/registry/skeleton.json +1 -1
- package/registry/skip-link.json +1 -1
- package/registry/slider.json +1 -1
- package/registry/slot-picker.json +1 -1
- package/registry/slot.json +1 -1
- package/registry/specimen-tag.json +1 -1
- package/registry/spinner.json +1 -1
- package/registry/split-view.json +1 -1
- package/registry/stage-banner.json +1 -1
- package/registry/stat.json +1 -1
- package/registry/status-page.json +1 -1
- package/registry/step-progress.json +1 -1
- package/registry/step-showcase.json +1 -1
- package/registry/stepper.json +1 -1
- package/registry/sticky-action.json +1 -1
- package/registry/swipe-deck.json +1 -1
- package/registry/switch.json +1 -1
- package/registry/tabs.json +1 -1
- package/registry/tag-input.json +3 -3
- package/registry/tag-selector.json +1 -1
- package/registry/terminal-replay.json +1 -1
- package/registry/theme.json +1 -1
- package/registry/toast.json +1 -1
- package/registry/toc.json +1 -1
- package/registry/tooltip.json +1 -1
- package/registry/tree-view.json +1 -1
- package/registry/update-prompt.json +1 -1
- package/registry/upload-queue.json +243 -0
- package/registry/verified-badge.json +1 -1
- package/registry/vertical-video.json +1 -1
- package/registry/video-player.json +1 -1
- package/registry/video-trimmer.json +1 -1
- package/registry/virtual.json +1 -1
- package/registry/waveform.json +1 -1
package/dist/account-menu.d.ts
CHANGED
|
@@ -1,115 +1,6 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
type DefaultAvatarBrand = "krizaka" | "orazaka" | "orochia" | "yasakia" | "yororis" | "yomizaka" | "kachizaka" | "akinaza";
|
|
6
|
-
/** `person`: a neutral silhouette. `plain`: the brand's gradient alone, ready for initials. */
|
|
7
|
-
type DefaultAvatarKind = "person" | "plain";
|
|
8
|
-
interface DefaultAvatar {
|
|
9
|
-
/** The brand of the product showing the avatar: its gradient, its hues. */
|
|
10
|
-
brand: DefaultAvatarBrand;
|
|
11
|
-
/** Any stable text or number of the person (their id): the same seed gives the same tone everywhere. */
|
|
12
|
-
seed?: string | number;
|
|
13
|
-
/** Force a tone, 1 to 6, instead of deriving it from `seed`. */
|
|
14
|
-
tone?: number;
|
|
15
|
-
/**
|
|
16
|
-
* `person` (default): a silhouette. `plain`: the gradient alone.
|
|
17
|
-
* @default "person"
|
|
18
|
-
*/
|
|
19
|
-
kind?: DefaultAvatarKind;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
declare const avatar: tailwind_variants.TVReturnType<{
|
|
23
|
-
size: {
|
|
24
|
-
xs: {
|
|
25
|
-
root: string;
|
|
26
|
-
};
|
|
27
|
-
sm: {
|
|
28
|
-
root: string;
|
|
29
|
-
};
|
|
30
|
-
md: {
|
|
31
|
-
root: string;
|
|
32
|
-
};
|
|
33
|
-
lg: {
|
|
34
|
-
root: string;
|
|
35
|
-
};
|
|
36
|
-
xl: {
|
|
37
|
-
root: string;
|
|
38
|
-
};
|
|
39
|
-
};
|
|
40
|
-
}, {
|
|
41
|
-
root: string;
|
|
42
|
-
image: string;
|
|
43
|
-
fallback: string;
|
|
44
|
-
group: string;
|
|
45
|
-
}, undefined, {
|
|
46
|
-
size: {
|
|
47
|
-
xs: {
|
|
48
|
-
root: string;
|
|
49
|
-
};
|
|
50
|
-
sm: {
|
|
51
|
-
root: string;
|
|
52
|
-
};
|
|
53
|
-
md: {
|
|
54
|
-
root: string;
|
|
55
|
-
};
|
|
56
|
-
lg: {
|
|
57
|
-
root: string;
|
|
58
|
-
};
|
|
59
|
-
xl: {
|
|
60
|
-
root: string;
|
|
61
|
-
};
|
|
62
|
-
};
|
|
63
|
-
}, {
|
|
64
|
-
root: string;
|
|
65
|
-
image: string;
|
|
66
|
-
fallback: string;
|
|
67
|
-
group: string;
|
|
68
|
-
}, tailwind_variants.TVReturnTypeLike<{
|
|
69
|
-
size: {
|
|
70
|
-
xs: {
|
|
71
|
-
root: string;
|
|
72
|
-
};
|
|
73
|
-
sm: {
|
|
74
|
-
root: string;
|
|
75
|
-
};
|
|
76
|
-
md: {
|
|
77
|
-
root: string;
|
|
78
|
-
};
|
|
79
|
-
lg: {
|
|
80
|
-
root: string;
|
|
81
|
-
};
|
|
82
|
-
xl: {
|
|
83
|
-
root: string;
|
|
84
|
-
};
|
|
85
|
-
};
|
|
86
|
-
}, {
|
|
87
|
-
root: string;
|
|
88
|
-
image: string;
|
|
89
|
-
fallback: string;
|
|
90
|
-
group: string;
|
|
91
|
-
}>>;
|
|
92
|
-
type AvatarSize = NonNullable<VariantProps<typeof avatar>["size"]>;
|
|
93
|
-
type AvatarProps = Omit<React.ComponentProps<"span">, "children"> & {
|
|
94
|
-
/** The image's URL; `null` or nothing shows the `fallback`. */
|
|
95
|
-
src?: string | null;
|
|
96
|
-
/** The image's text alternative (the person's name); empty when a visible name sits next to the avatar. */
|
|
97
|
-
alt?: string;
|
|
98
|
-
/** Shown while the image loads, when it fails or when there is none: initials, an icon. */
|
|
99
|
-
fallback?: React.ReactNode;
|
|
100
|
-
/** The picture to show instead when `src` is missing or fails to load (an image URL). The `fallback` still covers its own loading. */
|
|
101
|
-
fallbackSrc?: string;
|
|
102
|
-
/**
|
|
103
|
-
* The Krizaka default picture when the person has no photo (or it fails): their brand's gradient and a silhouette,
|
|
104
|
-
* one of six tones from `seed` — `defaultAvatar={{ brand: "orochia", seed: user.id }}`. `fallbackSrc` wins over it.
|
|
105
|
-
*/
|
|
106
|
-
defaultAvatar?: DefaultAvatar;
|
|
107
|
-
/**
|
|
108
|
-
* xs · sm · md · lg · xl.
|
|
109
|
-
* @default "md"
|
|
110
|
-
*/
|
|
111
|
-
size?: AvatarSize;
|
|
112
|
-
};
|
|
2
|
+
import { A as AvatarProps } from './avatar-iajODANU.js';
|
|
3
|
+
import 'tailwind-variants';
|
|
113
4
|
|
|
114
5
|
interface AccountMenuLink {
|
|
115
6
|
id: string;
|
package/dist/account-menu.js
CHANGED
|
@@ -1,99 +1,20 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Avatar
|
|
3
|
+
} from "./chunk-3RVFD3UX.js";
|
|
1
4
|
import {
|
|
2
5
|
cn
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
|
|
5
|
-
// src/avatar/avatar.tsx
|
|
6
|
-
import { Avatar as AvatarPrimitive } from "radix-ui";
|
|
7
|
-
import * as React from "react";
|
|
8
|
-
import { tv } from "tailwind-variants";
|
|
9
|
-
|
|
10
|
-
// src/avatar/default-avatar.ts
|
|
11
|
-
var DEFAULT_AVATAR_TONES = 6;
|
|
12
|
-
var DEFAULT_AVATAR_BASE = "https://krizaka-assets.b-cdn.net/people/defaults/v1";
|
|
13
|
-
var clampTone = (tone) => Math.min(DEFAULT_AVATAR_TONES, Math.max(1, Math.round(tone)));
|
|
14
|
-
function defaultAvatarToneOf(seed) {
|
|
15
|
-
if (seed === void 0) return 1;
|
|
16
|
-
let h = 2166136261;
|
|
17
|
-
for (const ch of String(seed)) {
|
|
18
|
-
h ^= ch.codePointAt(0) ?? 0;
|
|
19
|
-
h = Math.imul(h, 16777619) >>> 0;
|
|
20
|
-
}
|
|
21
|
-
return h % DEFAULT_AVATAR_TONES + 1;
|
|
22
|
-
}
|
|
23
|
-
var resolveTone = (a) => a.tone === void 0 ? defaultAvatarToneOf(a.seed) : clampTone(a.tone);
|
|
24
|
-
function defaultAvatarFile(kind, tone, size) {
|
|
25
|
-
return size === "svg" ? `${kind}-${tone}.svg` : `${kind}-${tone}-${size}.webp`;
|
|
26
|
-
}
|
|
27
|
-
function trimTrailingSlashes(value) {
|
|
28
|
-
let end = value.length;
|
|
29
|
-
while (end > 0 && value.charCodeAt(end - 1) === 47) end--;
|
|
30
|
-
return value.slice(0, end);
|
|
31
|
-
}
|
|
32
|
-
function defaultAvatarUrl(avatar2, { size = 256, base = DEFAULT_AVATAR_BASE } = {}) {
|
|
33
|
-
return `${trimTrailingSlashes(base)}/${avatar2.brand}/${defaultAvatarFile(avatar2.kind ?? "person", resolveTone(avatar2), size)}`;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// src/avatar/avatar.tsx
|
|
37
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
38
|
-
var avatar = tv({
|
|
39
|
-
slots: {
|
|
40
|
-
root: "relative inline-flex shrink-0 overflow-hidden rounded-full bg-surface-3 align-middle",
|
|
41
|
-
image: "h-full w-full object-cover",
|
|
42
|
-
fallback: "flex h-full w-full items-center justify-center font-semibold uppercase text-fg-secondary",
|
|
43
|
-
group: "flex items-center -space-x-2 [&>*]:ring-2 [&>*]:ring-surface-0"
|
|
44
|
-
},
|
|
45
|
-
variants: {
|
|
46
|
-
size: {
|
|
47
|
-
xs: { root: "h-5 w-5 text-[9px]" },
|
|
48
|
-
sm: { root: "h-8 w-8 text-xs" },
|
|
49
|
-
md: { root: "h-10 w-10 text-sm" },
|
|
50
|
-
lg: { root: "h-14 w-14 text-base" },
|
|
51
|
-
xl: { root: "h-20 w-20 text-lg" }
|
|
52
|
-
}
|
|
53
|
-
},
|
|
54
|
-
defaultVariants: { size: "md" }
|
|
55
|
-
});
|
|
56
|
-
function AvatarRoot({ src, alt = "", fallback, fallbackSrc, defaultAvatar, size, className, ...props }) {
|
|
57
|
-
const s2 = avatar({ size });
|
|
58
|
-
const [failed, setFailed] = React.useState(null);
|
|
59
|
-
const backup = fallbackSrc ?? (defaultAvatar ? defaultAvatarUrl(defaultAvatar, { size: size === "xl" || size === "lg" ? 512 : 256 }) : void 0);
|
|
60
|
-
const shown = src && failed !== src ? src : backup;
|
|
61
|
-
return /* @__PURE__ */ jsxs(AvatarPrimitive.Root, { "data-size": size ?? "md", className: s2.root({ className }), ...props, children: [
|
|
62
|
-
shown && /* @__PURE__ */ jsx(
|
|
63
|
-
AvatarPrimitive.Image,
|
|
64
|
-
{
|
|
65
|
-
src: shown,
|
|
66
|
-
alt,
|
|
67
|
-
className: s2.image(),
|
|
68
|
-
onLoadingStatusChange: (status) => {
|
|
69
|
-
if (status === "error" && shown === src) setFailed(src);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
),
|
|
73
|
-
/* @__PURE__ */ jsx(AvatarPrimitive.Fallback, { delayMs: shown ? 300 : 0, className: s2.fallback(), children: fallback })
|
|
74
|
-
] });
|
|
75
|
-
}
|
|
76
|
-
function AvatarGroup({ max, size, className, children, ...props }) {
|
|
77
|
-
const items = React.Children.toArray(children);
|
|
78
|
-
const shown = max === void 0 ? items : items.slice(0, max);
|
|
79
|
-
const rest = items.length - shown.length;
|
|
80
|
-
return /* @__PURE__ */ jsxs("div", { className: avatar().group({ className }), ...props, children: [
|
|
81
|
-
shown,
|
|
82
|
-
rest > 0 && /* @__PURE__ */ jsx(AvatarRoot, { size, fallback: `+${rest}` })
|
|
83
|
-
] });
|
|
84
|
-
}
|
|
85
|
-
var Avatar = Object.assign(AvatarRoot, { Group: AvatarGroup });
|
|
6
|
+
} from "./chunk-S6NYGOJT.js";
|
|
86
7
|
|
|
87
8
|
// src/dropdown-menu/dropdown-menu.tsx
|
|
88
9
|
import { DropdownMenu as MenuPrimitive } from "radix-ui";
|
|
89
|
-
import { tv
|
|
10
|
+
import { tv } from "tailwind-variants";
|
|
90
11
|
|
|
91
12
|
// src/floating.ts
|
|
92
13
|
var floating = "kz-pop z-50 rounded-lg border border-border-default bg-surface-2 text-fg shadow-lg outline-hidden";
|
|
93
14
|
|
|
94
15
|
// src/dropdown-menu/dropdown-menu.tsx
|
|
95
|
-
import { jsx
|
|
96
|
-
var menu =
|
|
16
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
17
|
+
var menu = tv({
|
|
97
18
|
slots: {
|
|
98
19
|
content: cn(floating, "min-w-48 max-h-(--radix-dropdown-menu-content-available-height) overflow-y-auto overflow-x-hidden overscroll-contain p-1 origin-(--radix-dropdown-menu-content-transform-origin)"),
|
|
99
20
|
item: "relative flex cursor-pointer select-none items-center gap-2 rounded-md px-2.5 py-2 text-sm text-fg outline-hidden transition-colors data-highlighted:bg-surface-3 data-disabled:pointer-events-none data-disabled:opacity-40 [&>svg]:h-4 [&>svg]:w-4 [&>svg]:shrink-0 [&>svg]:text-fg-secondary",
|
|
@@ -110,37 +31,37 @@ var menu = tv2({
|
|
|
110
31
|
});
|
|
111
32
|
var s = menu();
|
|
112
33
|
function DropdownMenuContent({ sideOffset = 6, align = "end", collisionPadding = 8, container, className, ...props }) {
|
|
113
|
-
return /* @__PURE__ */
|
|
34
|
+
return /* @__PURE__ */ jsx(MenuPrimitive.Portal, { container, children: /* @__PURE__ */ jsx(MenuPrimitive.Content, { sideOffset, align, collisionPadding, className: s.content({ className }), ...props }) });
|
|
114
35
|
}
|
|
115
36
|
function DropdownMenuItem({ tone, className, ...props }) {
|
|
116
|
-
return /* @__PURE__ */
|
|
37
|
+
return /* @__PURE__ */ jsx(MenuPrimitive.Item, { "data-tone": tone ?? "default", className: menu({ tone }).item({ className }), ...props });
|
|
117
38
|
}
|
|
118
39
|
function DropdownMenuCheckboxItem({ className, children, ...props }) {
|
|
119
|
-
return /* @__PURE__ */
|
|
40
|
+
return /* @__PURE__ */ jsxs(MenuPrimitive.CheckboxItem, { className: s.item({ className }), ...props, children: [
|
|
120
41
|
children,
|
|
121
|
-
/* @__PURE__ */
|
|
42
|
+
/* @__PURE__ */ jsx(MenuPrimitive.ItemIndicator, { className: s.indicator(), children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: /* @__PURE__ */ jsx("path", { d: "M20 6 9 17l-5-5" }) }) })
|
|
122
43
|
] });
|
|
123
44
|
}
|
|
124
45
|
function DropdownMenuSubTrigger({ className, children, ...props }) {
|
|
125
|
-
return /* @__PURE__ */
|
|
46
|
+
return /* @__PURE__ */ jsxs(MenuPrimitive.SubTrigger, { className: s.item({ className: cn("data-[state=open]:bg-surface-3", className) }), ...props, children: [
|
|
126
47
|
children,
|
|
127
|
-
/* @__PURE__ */
|
|
48
|
+
/* @__PURE__ */ jsx("svg", { className: "ml-auto !h-4 !w-4 text-fg-secondary", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: /* @__PURE__ */ jsx("path", { d: "m9 18 6-6-6-6" }) })
|
|
128
49
|
] });
|
|
129
50
|
}
|
|
130
51
|
function DropdownMenuSubContent({ sideOffset = 4, collisionPadding = 8, className, ...props }) {
|
|
131
|
-
return /* @__PURE__ */
|
|
52
|
+
return /* @__PURE__ */ jsx(MenuPrimitive.Portal, { children: /* @__PURE__ */ jsx(MenuPrimitive.SubContent, { sideOffset, collisionPadding, className: s.content({ className: cn("max-h-(--radix-dropdown-menu-content-available-height)", className) }), ...props }) });
|
|
132
53
|
}
|
|
133
54
|
function DropdownMenuRadioItem({ className, children, ...props }) {
|
|
134
|
-
return /* @__PURE__ */
|
|
55
|
+
return /* @__PURE__ */ jsxs(MenuPrimitive.RadioItem, { className: s.item({ className }), ...props, children: [
|
|
135
56
|
children,
|
|
136
|
-
/* @__PURE__ */
|
|
57
|
+
/* @__PURE__ */ jsx(MenuPrimitive.ItemIndicator, { className: s.indicator(), children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: /* @__PURE__ */ jsx("path", { d: "M20 6 9 17l-5-5" }) }) })
|
|
137
58
|
] });
|
|
138
59
|
}
|
|
139
60
|
function DropdownMenuLabel({ className, ...props }) {
|
|
140
|
-
return /* @__PURE__ */
|
|
61
|
+
return /* @__PURE__ */ jsx(MenuPrimitive.Label, { className: s.label({ className }), ...props });
|
|
141
62
|
}
|
|
142
63
|
function DropdownMenuSeparator({ className, ...props }) {
|
|
143
|
-
return /* @__PURE__ */
|
|
64
|
+
return /* @__PURE__ */ jsx(MenuPrimitive.Separator, { className: s.separator({ className }), ...props });
|
|
144
65
|
}
|
|
145
66
|
var DropdownMenu = {
|
|
146
67
|
Root: MenuPrimitive.Root,
|
|
@@ -159,46 +80,46 @@ var DropdownMenu = {
|
|
|
159
80
|
};
|
|
160
81
|
|
|
161
82
|
// src/account-menu/account-menu.tsx
|
|
162
|
-
import { jsx as
|
|
163
|
-
function AccountMenu({ label, name, email, avatar
|
|
83
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
84
|
+
function AccountMenu({ label, name, email, avatar, showName = true, links = [], linkAs: Link = "a", language, children, signOut, className }) {
|
|
164
85
|
const title = name || email || "";
|
|
165
86
|
const current = language?.options.find((o) => o.value === language.value)?.label;
|
|
166
|
-
return /* @__PURE__ */
|
|
167
|
-
/* @__PURE__ */
|
|
87
|
+
return /* @__PURE__ */ jsxs2(DropdownMenu.Root, { children: [
|
|
88
|
+
/* @__PURE__ */ jsxs2(
|
|
168
89
|
DropdownMenu.Trigger,
|
|
169
90
|
{
|
|
170
91
|
"aria-label": label,
|
|
171
92
|
className: cn("flex items-center gap-2.5 rounded-lg p-1 transition-colors duration-150 hover:bg-surface-2 focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring", className),
|
|
172
93
|
children: [
|
|
173
|
-
/* @__PURE__ */
|
|
174
|
-
showName && title && /* @__PURE__ */
|
|
94
|
+
/* @__PURE__ */ jsx2(Avatar, { size: "sm", fallback: title.charAt(0).toUpperCase() || "?", ...avatar }),
|
|
95
|
+
showName && title && /* @__PURE__ */ jsx2("span", { "aria-hidden": true, "data-name": title, className: "hidden max-w-40 truncate text-sm font-medium text-fg-secondary after:content-[attr(data-name)] sm:inline-block" })
|
|
175
96
|
]
|
|
176
97
|
}
|
|
177
98
|
),
|
|
178
|
-
/* @__PURE__ */
|
|
179
|
-
/* @__PURE__ */
|
|
180
|
-
/* @__PURE__ */
|
|
181
|
-
/* @__PURE__ */
|
|
182
|
-
/* @__PURE__ */
|
|
183
|
-
name && email && /* @__PURE__ */
|
|
99
|
+
/* @__PURE__ */ jsxs2(DropdownMenu.Content, { align: "end", className: "w-64", children: [
|
|
100
|
+
/* @__PURE__ */ jsxs2(DropdownMenu.Label, { className: "flex items-center gap-3 py-2 normal-case tracking-normal", children: [
|
|
101
|
+
/* @__PURE__ */ jsx2(Avatar, { size: "md", fallback: title.charAt(0).toUpperCase() || "?", ...avatar }),
|
|
102
|
+
/* @__PURE__ */ jsxs2("span", { className: "min-w-0", children: [
|
|
103
|
+
/* @__PURE__ */ jsx2("span", { className: "block truncate text-sm font-semibold text-fg", children: title }),
|
|
104
|
+
name && email && name !== email && /* @__PURE__ */ jsx2("span", { className: "block truncate text-xs font-normal text-fg-secondary", children: email })
|
|
184
105
|
] })
|
|
185
106
|
] }),
|
|
186
|
-
links.length > 0 && /* @__PURE__ */
|
|
187
|
-
links.map((link) => /* @__PURE__ */
|
|
107
|
+
links.length > 0 && /* @__PURE__ */ jsx2(DropdownMenu.Separator, {}),
|
|
108
|
+
links.map((link) => /* @__PURE__ */ jsx2(DropdownMenu.Item, { asChild: true, children: /* @__PURE__ */ jsxs2(Link, { href: link.href, children: [
|
|
188
109
|
link.icon,
|
|
189
110
|
link.label
|
|
190
111
|
] }) }, link.id)),
|
|
191
|
-
(language || children) && /* @__PURE__ */
|
|
192
|
-
language && /* @__PURE__ */
|
|
193
|
-
/* @__PURE__ */
|
|
112
|
+
(language || children) && /* @__PURE__ */ jsx2(DropdownMenu.Separator, {}),
|
|
113
|
+
language && /* @__PURE__ */ jsxs2(DropdownMenu.Sub, { children: [
|
|
114
|
+
/* @__PURE__ */ jsxs2(DropdownMenu.SubTrigger, { children: [
|
|
194
115
|
language.label,
|
|
195
|
-
current && /* @__PURE__ */
|
|
116
|
+
current && /* @__PURE__ */ jsx2("span", { className: "ml-auto mr-1 text-xs text-fg-secondary", children: current })
|
|
196
117
|
] }),
|
|
197
|
-
/* @__PURE__ */
|
|
118
|
+
/* @__PURE__ */ jsx2(DropdownMenu.SubContent, { children: /* @__PURE__ */ jsx2(DropdownMenu.RadioGroup, { value: language.value, onValueChange: language.onChange, children: language.options.map((o) => /* @__PURE__ */ jsx2(DropdownMenu.RadioItem, { value: o.value, children: o.label }, o.value)) }) })
|
|
198
119
|
] }),
|
|
199
120
|
children,
|
|
200
|
-
/* @__PURE__ */
|
|
201
|
-
/* @__PURE__ */
|
|
121
|
+
/* @__PURE__ */ jsx2(DropdownMenu.Separator, {}),
|
|
122
|
+
/* @__PURE__ */ jsxs2(DropdownMenu.Item, { onSelect: () => signOut.onSelect(), children: [
|
|
202
123
|
signOut.icon,
|
|
203
124
|
signOut.label
|
|
204
125
|
] })
|
package/dist/account-menu.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/avatar/avatar.tsx","../src/avatar/default-avatar.ts","../src/dropdown-menu/dropdown-menu.tsx","../src/floating.ts","../src/account-menu/account-menu.tsx"],"sourcesContent":["// Client: Radix tracks the image's loading state and shows the fallback until it has loaded (or when it fails).\nimport { Avatar as AvatarPrimitive } from \"radix-ui\";\nimport * as React from \"react\";\nimport { tv, type VariantProps } from \"tailwind-variants\";\n\nimport { type DefaultAvatar, defaultAvatarUrl } from \"./default-avatar\";\n\nconst avatar = tv({\n slots: {\n root: \"relative inline-flex shrink-0 overflow-hidden rounded-full bg-surface-3 align-middle\",\n image: \"h-full w-full object-cover\",\n fallback: \"flex h-full w-full items-center justify-center font-semibold uppercase text-fg-secondary\",\n group: \"flex items-center -space-x-2 [&>*]:ring-2 [&>*]:ring-surface-0\",\n },\n variants: {\n size: {\n xs: { root: \"h-5 w-5 text-[9px]\" },\n sm: { root: \"h-8 w-8 text-xs\" },\n md: { root: \"h-10 w-10 text-sm\" },\n lg: { root: \"h-14 w-14 text-base\" },\n xl: { root: \"h-20 w-20 text-lg\" },\n },\n },\n defaultVariants: { size: \"md\" },\n});\n\nexport type AvatarSize = NonNullable<VariantProps<typeof avatar>[\"size\"]>;\n\nexport type AvatarProps = Omit<React.ComponentProps<\"span\">, \"children\"> & {\n /** The image's URL; `null` or nothing shows the `fallback`. */\n src?: string | null;\n /** The image's text alternative (the person's name); empty when a visible name sits next to the avatar. */\n alt?: string;\n /** Shown while the image loads, when it fails or when there is none: initials, an icon. */\n fallback?: React.ReactNode;\n /** The picture to show instead when `src` is missing or fails to load (an image URL). The `fallback` still covers its own loading. */\n fallbackSrc?: string;\n /**\n * The Krizaka default picture when the person has no photo (or it fails): their brand's gradient and a silhouette,\n * one of six tones from `seed` — `defaultAvatar={{ brand: \"orochia\", seed: user.id }}`. `fallbackSrc` wins over it.\n */\n defaultAvatar?: DefaultAvatar;\n /**\n * xs · sm · md · lg · xl.\n * @default \"md\"\n */\n size?: AvatarSize;\n};\n\nexport function AvatarRoot({ src, alt = \"\", fallback, fallbackSrc, defaultAvatar, size, className, ...props }: AvatarProps) {\n const s = avatar({ size });\n const [failed, setFailed] = React.useState<string | null>(null);\n // 512 px for the big sizes, 256 for the others: sharp on a 2x screen either way.\n const backup = fallbackSrc ?? (defaultAvatar ? defaultAvatarUrl(defaultAvatar, { size: size === \"xl\" || size === \"lg\" ? 512 : 256 }) : undefined);\n const shown = src && failed !== src ? src : backup;\n return (\n <AvatarPrimitive.Root data-size={size ?? \"md\"} className={s.root({ className })} {...props}>\n {shown && (\n <AvatarPrimitive.Image\n src={shown}\n alt={alt}\n className={s.image()}\n onLoadingStatusChange={(status) => {\n if (status === \"error\" && shown === src) setFailed(src);\n }}\n />\n )}\n <AvatarPrimitive.Fallback delayMs={shown ? 300 : 0} className={s.fallback()}>\n {fallback}\n </AvatarPrimitive.Fallback>\n </AvatarPrimitive.Root>\n );\n}\n\nexport type AvatarGroupProps = React.ComponentProps<\"div\"> & {\n /** How many avatars are shown; the rest becomes a `+n` avatar. */\n max?: number;\n /** The size of the `+n` avatar (match the avatars of the group). */\n size?: AvatarSize;\n};\n\n/** Overlapping avatars; past `max`, a `+n` avatar counts the rest. */\nexport function AvatarGroup({ max, size, className, children, ...props }: AvatarGroupProps) {\n const items = React.Children.toArray(children);\n const shown = max === undefined ? items : items.slice(0, max);\n const rest = items.length - shown.length;\n return (\n <div className={avatar().group({ className })} {...props}>\n {shown}\n {rest > 0 && <AvatarRoot size={size} fallback={`+${rest}`} />}\n </div>\n );\n}\n\n/** `Avatar` (an image with a fallback) and `Avatar.Group`. */\nexport const Avatar = Object.assign(AvatarRoot, { Group: AvatarGroup });\n","// The default profile pictures of the Krizaka family: what a product shows when a person has no photo.\n// They are generated from this file (tools/export-brand.mjs renders them to SVG and WebP and uploads them to Bunny,\n// `people/defaults/v1/<brand>/…`), so the pictures and the URLs that name them cannot drift apart.\n\nexport type DefaultAvatarBrand = \"krizaka\" | \"orazaka\" | \"orochia\" | \"yasakia\" | \"yororis\" | \"yomizaka\" | \"kachizaka\" | \"akinaza\";\n/** `person`: a neutral silhouette. `plain`: the brand's gradient alone, ready for initials. */\nexport type DefaultAvatarKind = \"person\" | \"plain\";\nexport type DefaultAvatarSize = 256 | 512;\n\nexport const DEFAULT_AVATAR_BRANDS: readonly DefaultAvatarBrand[] = [\"krizaka\", \"orazaka\", \"orochia\", \"yasakia\", \"yororis\", \"yomizaka\", \"kachizaka\", \"akinaza\"];\nexport const DEFAULT_AVATAR_KINDS: readonly DefaultAvatarKind[] = [\"person\", \"plain\"];\nexport const DEFAULT_AVATAR_SIZES: readonly DefaultAvatarSize[] = [256, 512];\n/** How many tones each brand has: a person always gets the same one from their seed, so a list is not one colour. */\nexport const DEFAULT_AVATAR_TONES = 6;\n\n/** Where the set lives. `v1` is the set's version: a changed picture is a new version folder, never an overwrite. */\nexport const DEFAULT_AVATAR_BASE = \"https://krizaka-assets.b-cdn.net/people/defaults/v1\";\n\nexport interface DefaultAvatar {\n /** The brand of the product showing the avatar: its gradient, its hues. */\n brand: DefaultAvatarBrand;\n /** Any stable text or number of the person (their id): the same seed gives the same tone everywhere. */\n seed?: string | number;\n /** Force a tone, 1 to 6, instead of deriving it from `seed`. */\n tone?: number;\n /**\n * `person` (default): a silhouette. `plain`: the gradient alone.\n * @default \"person\"\n */\n kind?: DefaultAvatarKind;\n}\n\n/** The mark gradient of each brand (BRAND.md, `$extensions[\"com.krizaka\"].mark`) as [hue, saturation %, lightness %] and the hue spread of its tones. */\nconst BRANDS: Record<DefaultAvatarBrand, { from: [number, number, number]; to: [number, number, number]; spread: number }> = {\n krizaka: { from: [217, 92, 53], to: [217, 92, 68], spread: 14 },\n orazaka: { from: [26, 90.5, 37.1], to: [26, 92, 45], spread: 7 },\n orochia: { from: [262.1, 83.3, 57.8], to: [328.6, 85.5, 70.2], spread: 12 },\n yasakia: { from: [156, 92, 32], to: [178, 90, 32], spread: 12 },\n yororis: { from: [34, 46, 44], to: [104, 28, 42], spread: 10 },\n yomizaka: { from: [232, 50, 55], to: [248, 50, 58], spread: 10 },\n kachizaka: { from: [226, 80, 56], to: [44, 92, 33], spread: 10 },\n akinaza: { from: [140, 48, 34], to: [22, 60, 46], spread: 10 },\n};\n\n/** White on top of a stop must stay visible: a stop is darkened until the contrast reaches this (WCAG 1.4.11, non-text). */\nconst MIN_CONTRAST = 3.2;\nconst OFFSETS = [0, -1, 1, -2, 2, 3] as const;\n\nfunction hslToHex([h, s, l]: [number, number, number]): string {\n const sat = s / 100;\n const light = l / 100;\n const k = (n: number) => (n + (((h % 360) + 360) % 360) / 30) % 12;\n const a = sat * Math.min(light, 1 - light);\n const channel = (n: number) => Math.round(255 * (light - a * Math.max(-1, Math.min(k(n) - 3, Math.min(9 - k(n), 1)))));\n return `#${[0, 8, 4].map((n) => channel(n).toString(16).padStart(2, \"0\")).join(\"\")}`;\n}\n\nconst channelLuminance = (v: number) => (v <= 0.03928 ? v / 12.92 : ((v + 0.055) / 1.055) ** 2.4);\nconst whiteContrast = (hex: string) => {\n const [r, g, b] = [1, 3, 5].map((i) => channelLuminance(Number.parseInt(hex.slice(i, i + 2), 16) / 255));\n return 1.05 / (0.2126 * r! + 0.7152 * g! + 0.0722 * b! + 0.05);\n};\n/** The stop, darkened one point of lightness at a time until white reads on it. */\nfunction readable([h, s, l]: [number, number, number]): string {\n let hex = hslToHex([h, s, l]);\n for (let light = l; whiteContrast(hex) < MIN_CONTRAST && light > 10; light -= 1) hex = hslToHex([h, s, light]);\n return hex;\n}\n\n/** The two stops (hex) of tone `tone` (1 to 6) of a brand. */\nexport function defaultAvatarTone(brand: DefaultAvatarBrand, tone: number): readonly [string, string] {\n const b = BRANDS[brand];\n const o = OFFSETS[(clampTone(tone) - 1) % OFFSETS.length] * b.spread;\n return [readable([b.from[0] + o, b.from[1], b.from[2]]), readable([b.to[0] + o, b.to[1], b.to[2]])];\n}\n\nconst clampTone = (tone: number) => Math.min(DEFAULT_AVATAR_TONES, Math.max(1, Math.round(tone)));\n\n/** 1 to 6 from a seed (FNV-1a): stable across renders, devices and products. */\nexport function defaultAvatarToneOf(seed: string | number | undefined): number {\n if (seed === undefined) return 1;\n let h = 0x811c9dc5;\n for (const ch of String(seed)) {\n h ^= ch.codePointAt(0) ?? 0;\n h = Math.imul(h, 0x01000193) >>> 0;\n }\n return (h % DEFAULT_AVATAR_TONES) + 1;\n}\n\nconst resolveTone = (a: DefaultAvatar) => (a.tone === undefined ? defaultAvatarToneOf(a.seed) : clampTone(a.tone));\n\n/** The file name of a picture in the set: `person-3` + 256 → `person-3-256.webp`; `svg` has no size. */\nexport function defaultAvatarFile(kind: DefaultAvatarKind, tone: number, size: DefaultAvatarSize | \"svg\"): string {\n return size === \"svg\" ? `${kind}-${tone}.svg` : `${kind}-${tone}-${size}.webp`;\n}\n\n/** Linear on purpose: a `/\\/+$/` regex backtracks quadratically on a long run of slashes. */\nfunction trimTrailingSlashes(value: string): string {\n let end = value.length;\n while (end > 0 && value.charCodeAt(end - 1) === 47) end--;\n return value.slice(0, end);\n}\n\n/**\n * The URL of a default avatar: `defaultAvatarUrl({ brand: \"orochia\", seed: user.id })`.\n * @param size 256 for lists, 512 for a profile; `\"svg\"` for the vector.\n * @param base Another host or version of the set (a self-hosted copy); the default is the Krizaka CDN.\n */\nexport function defaultAvatarUrl(avatar: DefaultAvatar, { size = 256, base = DEFAULT_AVATAR_BASE }: { size?: DefaultAvatarSize | \"svg\"; base?: string } = {}): string {\n return `${trimTrailingSlashes(base)}/${avatar.brand}/${defaultAvatarFile(avatar.kind ?? \"person\", resolveTone(avatar), size)}`;\n}\n\n/** The SVG source of a default avatar, 512 × 512 and square (the Avatar clips it round). */\nexport function defaultAvatarSvg(avatar: DefaultAvatar): string {\n const [from, to] = defaultAvatarTone(avatar.brand, resolveTone(avatar));\n const person =\n (avatar.kind ?? \"person\") === \"person\"\n ? `<circle cx=\"256\" cy=\"200\" r=\"86\" fill=\"#fff\" fill-opacity=\".92\"/><path d=\"M84 512c0-108 78-182 172-182s172 74 172 182z\" fill=\"#fff\" fill-opacity=\".92\"/>`\n : \"\";\n return `<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\" width=\"512\" height=\"512\"><defs><linearGradient id=\"g\" x1=\"0\" y1=\"0\" x2=\"1\" y2=\"1\"><stop offset=\"0\" stop-color=\"${from}\"/><stop offset=\"1\" stop-color=\"${to}\"/></linearGradient><radialGradient id=\"l\" cx=\".28\" cy=\".18\" r=\".85\"><stop offset=\"0\" stop-color=\"#fff\" stop-opacity=\".24\"/><stop offset=\"1\" stop-color=\"#fff\" stop-opacity=\"0\"/></radialGradient></defs><rect width=\"512\" height=\"512\" fill=\"url(#g)\"/><rect width=\"512\" height=\"512\" fill=\"url(#l)\"/>${person}</svg>`;\n}\n","// Client (a portal). Radix does the roving focus, the arrows, Home/End, typeahead, Escape and the focus return.\nimport { DropdownMenu as MenuPrimitive } from \"radix-ui\";\nimport type * as React from \"react\";\nimport { tv } from \"tailwind-variants\";\n\nimport { cn } from \"../cn\";\nimport { floating } from \"../floating\";\n\nexport const menu = tv({\n slots: {\n content: cn(floating, \"min-w-48 max-h-(--radix-dropdown-menu-content-available-height) overflow-y-auto overflow-x-hidden overscroll-contain p-1 origin-(--radix-dropdown-menu-content-transform-origin)\"),\n item:\n \"relative flex cursor-pointer select-none items-center gap-2 rounded-md px-2.5 py-2 text-sm text-fg outline-hidden transition-colors \" +\n \"data-highlighted:bg-surface-3 data-disabled:pointer-events-none data-disabled:opacity-40 [&>svg]:h-4 [&>svg]:w-4 [&>svg]:shrink-0 \" +\n \"[&>svg]:text-fg-secondary\",\n indicator: \"ml-auto flex h-4 w-4 items-center justify-center text-fg-accent\",\n label: \"px-2.5 pb-1 pt-2 text-[11px] font-semibold text-fg-secondary\",\n separator: \"-mx-1 my-1 h-px bg-border-subtle\",\n },\n variants: {\n // The label reads in the danger text role (≥ 4.5:1 on the menu surface, both themes); the icon and the highlight\n // carry the danger fill.\n tone: { default: {}, danger: { item: \"text-fg-danger data-highlighted:bg-danger/15 [&>svg]:text-danger\" } },\n },\n defaultVariants: { tone: \"default\" },\n});\nconst s = menu();\n\nexport type DropdownMenuContentProps = React.ComponentProps<typeof MenuPrimitive.Content> & {\n /** The element the portal renders into (default: document.body). */\n container?: HTMLElement | null;\n};\n\nexport function DropdownMenuContent({ sideOffset = 6, align = \"end\", collisionPadding = 8, container, className, ...props }: DropdownMenuContentProps) {\n return (\n <MenuPrimitive.Portal container={container}>\n <MenuPrimitive.Content sideOffset={sideOffset} align={align} collisionPadding={collisionPadding} className={s.content({ className })} {...props} />\n </MenuPrimitive.Portal>\n );\n}\n\nexport type DropdownMenuItemProps = React.ComponentProps<typeof MenuPrimitive.Item> & {\n /** `danger` for a destructive action: the icon and the highlight carry the danger, the label stays legible. */\n tone?: \"default\" | \"danger\";\n};\n\n/** An action. `tone=\"danger\"` for a destructive one; `onSelect` runs it (the menu closes). */\nexport function DropdownMenuItem({ tone, className, ...props }: DropdownMenuItemProps) {\n return <MenuPrimitive.Item data-tone={tone ?? \"default\"} className={menu({ tone }).item({ className })} {...props} />;\n}\n\n/** A toggled option: `checked` + `onCheckedChange`. */\nexport function DropdownMenuCheckboxItem({ className, children, ...props }: React.ComponentProps<typeof MenuPrimitive.CheckboxItem>) {\n return (\n <MenuPrimitive.CheckboxItem className={s.item({ className })} {...props}>\n {children}\n <MenuPrimitive.ItemIndicator className={s.indicator()}>\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"M20 6 9 17l-5-5\" />\n </svg>\n </MenuPrimitive.ItemIndicator>\n </MenuPrimitive.CheckboxItem>\n );\n}\n\n/** A submenu's trigger: an item that opens `DropdownMenu.SubContent`, with a chevron. */\nexport function DropdownMenuSubTrigger({ className, children, ...props }: React.ComponentProps<typeof MenuPrimitive.SubTrigger>) {\n return (\n <MenuPrimitive.SubTrigger className={s.item({ className: cn(\"data-[state=open]:bg-surface-3\", className) })} {...props}>\n {children}\n <svg className=\"ml-auto !h-4 !w-4 text-fg-secondary\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"m9 18 6-6-6-6\" />\n </svg>\n </MenuPrimitive.SubTrigger>\n );\n}\n\n/** The submenu's panel (same material as the menu). */\nexport function DropdownMenuSubContent({ sideOffset = 4, collisionPadding = 8, className, ...props }: React.ComponentProps<typeof MenuPrimitive.SubContent>) {\n return (\n <MenuPrimitive.Portal>\n <MenuPrimitive.SubContent sideOffset={sideOffset} collisionPadding={collisionPadding} className={s.content({ className: cn(\"max-h-(--radix-dropdown-menu-content-available-height)\", className) })} {...props} />\n </MenuPrimitive.Portal>\n );\n}\n\n/** One choice among several (`DropdownMenu.RadioGroup` holds the value): a language, a sort order. */\nexport function DropdownMenuRadioItem({ className, children, ...props }: React.ComponentProps<typeof MenuPrimitive.RadioItem>) {\n return (\n <MenuPrimitive.RadioItem className={s.item({ className })} {...props}>\n {children}\n <MenuPrimitive.ItemIndicator className={s.indicator()}>\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"M20 6 9 17l-5-5\" />\n </svg>\n </MenuPrimitive.ItemIndicator>\n </MenuPrimitive.RadioItem>\n );\n}\n\nexport function DropdownMenuLabel({ className, ...props }: React.ComponentProps<typeof MenuPrimitive.Label>) {\n return <MenuPrimitive.Label className={s.label({ className })} {...props} />;\n}\n\nexport function DropdownMenuSeparator({ className, ...props }: React.ComponentProps<typeof MenuPrimitive.Separator>) {\n return <MenuPrimitive.Separator className={s.separator({ className })} {...props} />;\n}\n\n/** `DropdownMenu.Root/Trigger/Content/Item/CheckboxItem/Label/Separator/Group`. `open`/`defaultOpen` + `onOpenChange`. */\nexport const DropdownMenu = {\n Root: MenuPrimitive.Root,\n Trigger: MenuPrimitive.Trigger,\n Content: DropdownMenuContent,\n Item: DropdownMenuItem,\n CheckboxItem: DropdownMenuCheckboxItem,\n Label: DropdownMenuLabel,\n Separator: DropdownMenuSeparator,\n Group: MenuPrimitive.Group,\n Sub: MenuPrimitive.Sub,\n SubTrigger: DropdownMenuSubTrigger,\n SubContent: DropdownMenuSubContent,\n RadioGroup: MenuPrimitive.RadioGroup,\n RadioItem: DropdownMenuRadioItem,\n};\n","// The one surface of everything that floats next to its trigger (popover, menu, tooltip): a raised surface, a border,\n// the large shadow and the `kz-pop` entrance from the side Radix measured (`--radix-*-transform-origin`).\nexport const floating =\n \"kz-pop z-50 rounded-lg border border-border-default bg-surface-2 text-fg shadow-lg outline-hidden\";\n","// Client (a portal). The signed-in person's menu under their avatar — who you are first (picture, name, e-mail), then\n// the places (profile, settings), the language as a compact submenu, the extras, and Sign out (a quiet item, not a\n// red alert). Built on DropdownMenu, so the keyboard, the focus return and the collisions are Radix's.\nimport * as React from \"react\";\n\nimport { Avatar, type AvatarProps } from \"../avatar/avatar\";\nimport { cn } from \"../cn\";\nimport { DropdownMenu } from \"../dropdown-menu/dropdown-menu\";\n\nexport interface AccountMenuLink {\n id: string;\n label: string;\n href: string;\n /** A signature icon. */\n icon?: React.ReactNode;\n}\n\nexport interface AccountMenuLanguage {\n /** The row's words (`Language`). */\n label: string;\n /** The current value (`en`). */\n value: string;\n options: readonly { value: string; label: string }[];\n onChange: (value: string) => void;\n}\n\nexport interface AccountMenuProps {\n /** The trigger's accessible name (`Account`). */\n label: string;\n /** Their name; falls back to the e-mail. */\n name?: string;\n /** Their e-mail, shown under the name. */\n email?: string;\n /** The picture: `src`, or `defaultAvatar` (`{ brand, seed }`) for the product's default person. */\n avatar?: Omit<AvatarProps, \"size\">;\n /** Show the name beside the avatar in the trigger from the `sm` breakpoint. */\n showName?: boolean;\n /** Where to go: profile, settings… */\n links?: readonly AccountMenuLink[];\n /** The app's link component (Next's `Link`); an `<a>` otherwise. */\n linkAs?: React.ElementType;\n /** The language, as a submenu that shows the current one. */\n language?: AccountMenuLanguage;\n /** Extra rows between the language and Sign out (a theme switch…). */\n children?: React.ReactNode;\n /** The last row: its words, what it does, an optional icon. */\n signOut: { label: string; onSelect: () => void; icon?: React.ReactNode };\n /** Classes for the trigger. */\n className?: string;\n}\n\n/** The account menu: avatar trigger → identity header, links, language submenu, Sign out. */\nexport function AccountMenu({ label, name, email, avatar, showName = true, links = [], linkAs: Link = \"a\", language, children, signOut, className }: AccountMenuProps) {\n const title = name || email || \"\";\n const current = language?.options.find((o) => o.value === language.value)?.label;\n return (\n <DropdownMenu.Root>\n <DropdownMenu.Trigger\n aria-label={label}\n className={cn(\"flex items-center gap-2.5 rounded-lg p-1 transition-colors duration-150 hover:bg-surface-2 focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring\", className)}\n >\n <Avatar size=\"sm\" fallback={title.charAt(0).toUpperCase() || \"?\"} {...avatar} />\n {/* Drawn by CSS: the name is not text of the button, so its accessible name stays `label` with no label-content-name-mismatch. */}\n {showName && title && <span aria-hidden data-name={title} className=\"hidden max-w-40 truncate text-sm font-medium text-fg-secondary after:content-[attr(data-name)] sm:inline-block\" />}\n </DropdownMenu.Trigger>\n <DropdownMenu.Content align=\"end\" className=\"w-64\">\n <DropdownMenu.Label className=\"flex items-center gap-3 py-2 normal-case tracking-normal\">\n <Avatar size=\"md\" fallback={title.charAt(0).toUpperCase() || \"?\"} {...avatar} />\n <span className=\"min-w-0\">\n <span className=\"block truncate text-sm font-semibold text-fg\">{title}</span>\n {name && email && <span className=\"block truncate text-xs font-normal text-fg-secondary\">{email}</span>}\n </span>\n </DropdownMenu.Label>\n {links.length > 0 && <DropdownMenu.Separator />}\n {links.map((link) => (\n <DropdownMenu.Item key={link.id} asChild>\n <Link href={link.href}>\n {link.icon}\n {link.label}\n </Link>\n </DropdownMenu.Item>\n ))}\n {(language || children) && <DropdownMenu.Separator />}\n {language && (\n <DropdownMenu.Sub>\n <DropdownMenu.SubTrigger>\n {language.label}\n {current && <span className=\"ml-auto mr-1 text-xs text-fg-secondary\">{current}</span>}\n </DropdownMenu.SubTrigger>\n <DropdownMenu.SubContent>\n <DropdownMenu.RadioGroup value={language.value} onValueChange={language.onChange}>\n {language.options.map((o) => (\n <DropdownMenu.RadioItem key={o.value} value={o.value}>\n {o.label}\n </DropdownMenu.RadioItem>\n ))}\n </DropdownMenu.RadioGroup>\n </DropdownMenu.SubContent>\n </DropdownMenu.Sub>\n )}\n {children}\n <DropdownMenu.Separator />\n <DropdownMenu.Item onSelect={() => signOut.onSelect()}>\n {signOut.icon}\n {signOut.label}\n </DropdownMenu.Item>\n </DropdownMenu.Content>\n </DropdownMenu.Root>\n );\n}\n"],"mappings":"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","names":["avatar","s","tv","jsx","jsxs","tv","jsx","jsxs","avatar"]}
|
|
1
|
+
{"version":3,"sources":["../src/dropdown-menu/dropdown-menu.tsx","../src/floating.ts","../src/account-menu/account-menu.tsx"],"sourcesContent":["// Client (a portal). Radix does the roving focus, the arrows, Home/End, typeahead, Escape and the focus return.\nimport { DropdownMenu as MenuPrimitive } from \"radix-ui\";\nimport type * as React from \"react\";\nimport { tv } from \"tailwind-variants\";\n\nimport { cn } from \"../cn\";\nimport { floating } from \"../floating\";\n\nexport const menu = tv({\n slots: {\n content: cn(floating, \"min-w-48 max-h-(--radix-dropdown-menu-content-available-height) overflow-y-auto overflow-x-hidden overscroll-contain p-1 origin-(--radix-dropdown-menu-content-transform-origin)\"),\n item:\n \"relative flex cursor-pointer select-none items-center gap-2 rounded-md px-2.5 py-2 text-sm text-fg outline-hidden transition-colors \" +\n \"data-highlighted:bg-surface-3 data-disabled:pointer-events-none data-disabled:opacity-40 [&>svg]:h-4 [&>svg]:w-4 [&>svg]:shrink-0 \" +\n \"[&>svg]:text-fg-secondary\",\n indicator: \"ml-auto flex h-4 w-4 items-center justify-center text-fg-accent\",\n label: \"px-2.5 pb-1 pt-2 text-[11px] font-semibold text-fg-secondary\",\n separator: \"-mx-1 my-1 h-px bg-border-subtle\",\n },\n variants: {\n // The label reads in the danger text role (≥ 4.5:1 on the menu surface, both themes); the icon and the highlight\n // carry the danger fill.\n tone: { default: {}, danger: { item: \"text-fg-danger data-highlighted:bg-danger/15 [&>svg]:text-danger\" } },\n },\n defaultVariants: { tone: \"default\" },\n});\nconst s = menu();\n\nexport type DropdownMenuContentProps = React.ComponentProps<typeof MenuPrimitive.Content> & {\n /** The element the portal renders into (default: document.body). */\n container?: HTMLElement | null;\n};\n\nexport function DropdownMenuContent({ sideOffset = 6, align = \"end\", collisionPadding = 8, container, className, ...props }: DropdownMenuContentProps) {\n return (\n <MenuPrimitive.Portal container={container}>\n <MenuPrimitive.Content sideOffset={sideOffset} align={align} collisionPadding={collisionPadding} className={s.content({ className })} {...props} />\n </MenuPrimitive.Portal>\n );\n}\n\nexport type DropdownMenuItemProps = React.ComponentProps<typeof MenuPrimitive.Item> & {\n /** `danger` for a destructive action: the icon and the highlight carry the danger, the label stays legible. */\n tone?: \"default\" | \"danger\";\n};\n\n/** An action. `tone=\"danger\"` for a destructive one; `onSelect` runs it (the menu closes). */\nexport function DropdownMenuItem({ tone, className, ...props }: DropdownMenuItemProps) {\n return <MenuPrimitive.Item data-tone={tone ?? \"default\"} className={menu({ tone }).item({ className })} {...props} />;\n}\n\n/** A toggled option: `checked` + `onCheckedChange`. */\nexport function DropdownMenuCheckboxItem({ className, children, ...props }: React.ComponentProps<typeof MenuPrimitive.CheckboxItem>) {\n return (\n <MenuPrimitive.CheckboxItem className={s.item({ className })} {...props}>\n {children}\n <MenuPrimitive.ItemIndicator className={s.indicator()}>\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"M20 6 9 17l-5-5\" />\n </svg>\n </MenuPrimitive.ItemIndicator>\n </MenuPrimitive.CheckboxItem>\n );\n}\n\n/** A submenu's trigger: an item that opens `DropdownMenu.SubContent`, with a chevron. */\nexport function DropdownMenuSubTrigger({ className, children, ...props }: React.ComponentProps<typeof MenuPrimitive.SubTrigger>) {\n return (\n <MenuPrimitive.SubTrigger className={s.item({ className: cn(\"data-[state=open]:bg-surface-3\", className) })} {...props}>\n {children}\n <svg className=\"ml-auto !h-4 !w-4 text-fg-secondary\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"m9 18 6-6-6-6\" />\n </svg>\n </MenuPrimitive.SubTrigger>\n );\n}\n\n/** The submenu's panel (same material as the menu). */\nexport function DropdownMenuSubContent({ sideOffset = 4, collisionPadding = 8, className, ...props }: React.ComponentProps<typeof MenuPrimitive.SubContent>) {\n return (\n <MenuPrimitive.Portal>\n <MenuPrimitive.SubContent sideOffset={sideOffset} collisionPadding={collisionPadding} className={s.content({ className: cn(\"max-h-(--radix-dropdown-menu-content-available-height)\", className) })} {...props} />\n </MenuPrimitive.Portal>\n );\n}\n\n/** One choice among several (`DropdownMenu.RadioGroup` holds the value): a language, a sort order. */\nexport function DropdownMenuRadioItem({ className, children, ...props }: React.ComponentProps<typeof MenuPrimitive.RadioItem>) {\n return (\n <MenuPrimitive.RadioItem className={s.item({ className })} {...props}>\n {children}\n <MenuPrimitive.ItemIndicator className={s.indicator()}>\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"M20 6 9 17l-5-5\" />\n </svg>\n </MenuPrimitive.ItemIndicator>\n </MenuPrimitive.RadioItem>\n );\n}\n\nexport function DropdownMenuLabel({ className, ...props }: React.ComponentProps<typeof MenuPrimitive.Label>) {\n return <MenuPrimitive.Label className={s.label({ className })} {...props} />;\n}\n\nexport function DropdownMenuSeparator({ className, ...props }: React.ComponentProps<typeof MenuPrimitive.Separator>) {\n return <MenuPrimitive.Separator className={s.separator({ className })} {...props} />;\n}\n\n/** `DropdownMenu.Root/Trigger/Content/Item/CheckboxItem/Label/Separator/Group`. `open`/`defaultOpen` + `onOpenChange`. */\nexport const DropdownMenu = {\n Root: MenuPrimitive.Root,\n Trigger: MenuPrimitive.Trigger,\n Content: DropdownMenuContent,\n Item: DropdownMenuItem,\n CheckboxItem: DropdownMenuCheckboxItem,\n Label: DropdownMenuLabel,\n Separator: DropdownMenuSeparator,\n Group: MenuPrimitive.Group,\n Sub: MenuPrimitive.Sub,\n SubTrigger: DropdownMenuSubTrigger,\n SubContent: DropdownMenuSubContent,\n RadioGroup: MenuPrimitive.RadioGroup,\n RadioItem: DropdownMenuRadioItem,\n};\n","// The one surface of everything that floats next to its trigger (popover, menu, tooltip): a raised surface, a border,\n// the large shadow and the `kz-pop` entrance from the side Radix measured (`--radix-*-transform-origin`).\nexport const floating =\n \"kz-pop z-50 rounded-lg border border-border-default bg-surface-2 text-fg shadow-lg outline-hidden\";\n","// Client (a portal). The signed-in person's menu under their avatar — who you are first (picture, name, e-mail), then\n// the places (profile, settings), the language as a compact submenu, the extras, and Sign out (a quiet item, not a\n// red alert). Built on DropdownMenu, so the keyboard, the focus return and the collisions are Radix's.\nimport * as React from \"react\";\n\nimport { Avatar, type AvatarProps } from \"../avatar/avatar\";\nimport { cn } from \"../cn\";\nimport { DropdownMenu } from \"../dropdown-menu/dropdown-menu\";\n\nexport interface AccountMenuLink {\n id: string;\n label: string;\n href: string;\n /** A signature icon. */\n icon?: React.ReactNode;\n}\n\nexport interface AccountMenuLanguage {\n /** The row's words (`Language`). */\n label: string;\n /** The current value (`en`). */\n value: string;\n options: readonly { value: string; label: string }[];\n onChange: (value: string) => void;\n}\n\nexport interface AccountMenuProps {\n /** The trigger's accessible name (`Account`). */\n label: string;\n /** Their name; falls back to the e-mail. */\n name?: string;\n /** Their e-mail, shown under the name. */\n email?: string;\n /** The picture: `src`, or `defaultAvatar` (`{ brand, seed }`) for the product's default person. */\n avatar?: Omit<AvatarProps, \"size\">;\n /** Show the name beside the avatar in the trigger from the `sm` breakpoint. */\n showName?: boolean;\n /** Where to go: profile, settings… */\n links?: readonly AccountMenuLink[];\n /** The app's link component (Next's `Link`); an `<a>` otherwise. */\n linkAs?: React.ElementType;\n /** The language, as a submenu that shows the current one. */\n language?: AccountMenuLanguage;\n /** Extra rows between the language and Sign out (a theme switch…). */\n children?: React.ReactNode;\n /** The last row: its words, what it does, an optional icon. */\n signOut: { label: string; onSelect: () => void; icon?: React.ReactNode };\n /** Classes for the trigger. */\n className?: string;\n}\n\n/** The account menu: avatar trigger → identity header, links, language submenu, Sign out. */\nexport function AccountMenu({ label, name, email, avatar, showName = true, links = [], linkAs: Link = \"a\", language, children, signOut, className }: AccountMenuProps) {\n const title = name || email || \"\";\n const current = language?.options.find((o) => o.value === language.value)?.label;\n return (\n <DropdownMenu.Root>\n <DropdownMenu.Trigger\n aria-label={label}\n className={cn(\"flex items-center gap-2.5 rounded-lg p-1 transition-colors duration-150 hover:bg-surface-2 focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring\", className)}\n >\n <Avatar size=\"sm\" fallback={title.charAt(0).toUpperCase() || \"?\"} {...avatar} />\n {/* Drawn by CSS: the name is not text of the button, so its accessible name stays `label` with no label-content-name-mismatch. */}\n {showName && title && <span aria-hidden data-name={title} className=\"hidden max-w-40 truncate text-sm font-medium text-fg-secondary after:content-[attr(data-name)] sm:inline-block\" />}\n </DropdownMenu.Trigger>\n <DropdownMenu.Content align=\"end\" className=\"w-64\">\n <DropdownMenu.Label className=\"flex items-center gap-3 py-2 normal-case tracking-normal\">\n <Avatar size=\"md\" fallback={title.charAt(0).toUpperCase() || \"?\"} {...avatar} />\n <span className=\"min-w-0\">\n <span className=\"block truncate text-sm font-semibold text-fg\">{title}</span>\n {name && email && name !== email && <span className=\"block truncate text-xs font-normal text-fg-secondary\">{email}</span>}\n </span>\n </DropdownMenu.Label>\n {links.length > 0 && <DropdownMenu.Separator />}\n {links.map((link) => (\n <DropdownMenu.Item key={link.id} asChild>\n <Link href={link.href}>\n {link.icon}\n {link.label}\n </Link>\n </DropdownMenu.Item>\n ))}\n {(language || children) && <DropdownMenu.Separator />}\n {language && (\n <DropdownMenu.Sub>\n <DropdownMenu.SubTrigger>\n {language.label}\n {current && <span className=\"ml-auto mr-1 text-xs text-fg-secondary\">{current}</span>}\n </DropdownMenu.SubTrigger>\n <DropdownMenu.SubContent>\n <DropdownMenu.RadioGroup value={language.value} onValueChange={language.onChange}>\n {language.options.map((o) => (\n <DropdownMenu.RadioItem key={o.value} value={o.value}>\n {o.label}\n </DropdownMenu.RadioItem>\n ))}\n </DropdownMenu.RadioGroup>\n </DropdownMenu.SubContent>\n </DropdownMenu.Sub>\n )}\n {children}\n <DropdownMenu.Separator />\n <DropdownMenu.Item onSelect={() => signOut.onSelect()}>\n {signOut.icon}\n {signOut.label}\n </DropdownMenu.Item>\n </DropdownMenu.Content>\n </DropdownMenu.Root>\n );\n}\n"],"mappings":"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","names":["jsx","jsxs"]}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { A as AvatarProps, D as DefaultAvatar } from './avatar-iajODANU.js';
|
|
3
|
+
import { RadioGroup as RadioGroup$1 } from 'radix-ui';
|
|
4
|
+
import 'tailwind-variants';
|
|
5
|
+
|
|
6
|
+
type AccountProfileCardProps = Omit<React.ComponentProps<"section">, "title"> & {
|
|
7
|
+
/** The name (the card's heading). */
|
|
8
|
+
name: string;
|
|
9
|
+
/** The e-mail address. */
|
|
10
|
+
email: string;
|
|
11
|
+
/** Whether the address is verified. */
|
|
12
|
+
verified: boolean;
|
|
13
|
+
/** “Verified” — passed translated. */
|
|
14
|
+
verifiedLabel: string;
|
|
15
|
+
/** “Not verified” — passed translated. */
|
|
16
|
+
unverifiedLabel: string;
|
|
17
|
+
/** “Member since October 2026” — formatted by the app, in the viewer's language. */
|
|
18
|
+
memberSince?: string;
|
|
19
|
+
/** The picture: the person's photo, or the default avatar of the brand. */
|
|
20
|
+
avatar?: Pick<AvatarProps, "src" | "defaultAvatar" | "fallback">;
|
|
21
|
+
/** Beside the picture: the change action (a `Button`). */
|
|
22
|
+
action?: React.ReactNode;
|
|
23
|
+
/**
|
|
24
|
+
* One calm line for a settings row: a medium picture beside the name and the e-mail, no wrapping.
|
|
25
|
+
* @default false
|
|
26
|
+
*/
|
|
27
|
+
compact?: boolean;
|
|
28
|
+
};
|
|
29
|
+
/** The account's identity card: picture, name, verified e-mail, member since. */
|
|
30
|
+
declare function AccountProfileCard({ name, email, verified, verifiedLabel, unverifiedLabel, memberSince, avatar, action, compact, className, ...props }: AccountProfileCardProps): React.JSX.Element;
|
|
31
|
+
|
|
32
|
+
interface AvatarOption {
|
|
33
|
+
/** The value of the choice. */
|
|
34
|
+
id: string;
|
|
35
|
+
/** Its name for assistive technology (“Teinte 3”) — passed translated. */
|
|
36
|
+
label: string;
|
|
37
|
+
/** A picture's address; otherwise `defaultAvatar` draws one of the family's pictures. */
|
|
38
|
+
src?: string;
|
|
39
|
+
/** One of Krizaka's default pictures (`{ brand, tone }`). */
|
|
40
|
+
defaultAvatar?: DefaultAvatar;
|
|
41
|
+
}
|
|
42
|
+
type AvatarPickerProps = Omit<React.ComponentProps<typeof RadioGroup$1.Root>, "children" | "onChange" | "dir"> & {
|
|
43
|
+
/** The group's accessible name (“Profile picture”) — passed translated. */
|
|
44
|
+
label: string;
|
|
45
|
+
/**
|
|
46
|
+
* Smaller tiles, for a settings row.
|
|
47
|
+
* @default false
|
|
48
|
+
*/
|
|
49
|
+
dense?: boolean;
|
|
50
|
+
/** Shows the label above the grid, with `description` under it. */
|
|
51
|
+
showLabel?: boolean;
|
|
52
|
+
/** One line under the visible label: what it changes. */
|
|
53
|
+
description?: React.ReactNode;
|
|
54
|
+
/** The pictures. */
|
|
55
|
+
options: readonly AvatarOption[];
|
|
56
|
+
/** The chosen id (controlled). */
|
|
57
|
+
value?: string;
|
|
58
|
+
/** The viewer chooses another picture. */
|
|
59
|
+
onValueChange?: (id: string) => void;
|
|
60
|
+
/** After the pictures, in the grid: the upload action (a `Button`, a file input's label). Not a radio. */
|
|
61
|
+
upload?: React.ReactNode;
|
|
62
|
+
};
|
|
63
|
+
/** A grid of pictures to choose from, with an upload beside them: a radio group, never a row of loose radios. */
|
|
64
|
+
declare function AvatarPicker({ label, showLabel, description, dense, options, upload, className, ...props }: AvatarPickerProps): React.JSX.Element;
|
|
65
|
+
|
|
66
|
+
type RadioGroupRootProps = React.ComponentProps<typeof RadioGroup$1.Root> & {
|
|
67
|
+
/** The group's accessible name — passed translated (or `aria-labelledby` a visible heading). */
|
|
68
|
+
label?: string;
|
|
69
|
+
/** Marks every radio invalid: aria-invalid (pair it with a `Field.Error`). */
|
|
70
|
+
invalid?: boolean;
|
|
71
|
+
/**
|
|
72
|
+
* The layout of the radios: `list` (dots), `cards` (a grid of `Card`s), `buttons` (a row of tactile `Button`s), or
|
|
73
|
+
* `segmented` (a track of `Segment`s, one light gliding to the choice).
|
|
74
|
+
* @default "list"
|
|
75
|
+
*/
|
|
76
|
+
variant?: "list" | "cards" | "buttons" | "segmented";
|
|
77
|
+
/** Binds the choice to this search parameter of the URL (`?kind=…`): the URL is the state — for a view's selection (a segmented switch), not a form field. */
|
|
78
|
+
routeKey?: string;
|
|
79
|
+
/** The values the parameter may hold (the radios' `value`s); the first is the default unless `defaultValue` says otherwise. */
|
|
80
|
+
routeValues?: readonly string[];
|
|
81
|
+
};
|
|
82
|
+
/** `value`/`defaultValue` + `onValueChange`, `orientation`, `required`, `name` for a form, `variant` for the layout. */
|
|
83
|
+
declare function RadioGroupRoot({ label, invalid, variant, className, routeKey, routeValues, ...rest }: RadioGroupRootProps): React.JSX.Element;
|
|
84
|
+
type RadioGroupItemProps = Omit<React.ComponentProps<typeof RadioGroup$1.Item>, "children"> & {
|
|
85
|
+
/** The words next to the dot, clickable. Otherwise name it with `Field.Label htmlFor` or `aria-label`. */
|
|
86
|
+
children?: React.ReactNode;
|
|
87
|
+
};
|
|
88
|
+
/** A radio: a dot, and its words when given. */
|
|
89
|
+
declare function RadioGroupItem({ className, children, ...props }: RadioGroupItemProps): React.JSX.Element;
|
|
90
|
+
/** A whole card as the radio: its content is the radio's name; its check lights up when chosen. */
|
|
91
|
+
declare function RadioGroupCard({ className, children, ...props }: React.ComponentProps<typeof RadioGroup$1.Item>): React.JSX.Element;
|
|
92
|
+
type RadioGroupButtonProps = React.ComponentProps<typeof RadioGroup$1.Item> & {
|
|
93
|
+
/** A second line under the words ("+15 %"), part of the radio's name. */
|
|
94
|
+
hint?: React.ReactNode;
|
|
95
|
+
};
|
|
96
|
+
/** A tactile key: raised on its ledge, pressed in while held and once chosen — sizes, a tip, a level. */
|
|
97
|
+
declare function RadioGroupButton({ className, children, hint, ...props }: RadioGroupButtonProps): React.JSX.Element;
|
|
98
|
+
/** A segment of a `variant="segmented"` group: equal widths, one light gliding to the choice. */
|
|
99
|
+
declare function RadioGroupSegment({ className, ...props }: React.ComponentProps<typeof RadioGroup$1.Item>): React.JSX.Element;
|
|
100
|
+
/** `RadioGroup.Root/Item/Card/Button/Segment`. */
|
|
101
|
+
declare const RadioGroup: {
|
|
102
|
+
Root: typeof RadioGroupRoot;
|
|
103
|
+
Item: typeof RadioGroupItem;
|
|
104
|
+
Card: typeof RadioGroupCard;
|
|
105
|
+
Button: typeof RadioGroupButton;
|
|
106
|
+
Segment: typeof RadioGroupSegment;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
interface ChoiceCardOption {
|
|
110
|
+
/** The value of the choice. */
|
|
111
|
+
id: string;
|
|
112
|
+
/** Its name, short. */
|
|
113
|
+
title: React.ReactNode;
|
|
114
|
+
/** One line under the title: what it means. It describes the radio. */
|
|
115
|
+
description?: React.ReactNode;
|
|
116
|
+
/** A glyph before the title (decorative). */
|
|
117
|
+
icon?: React.ReactNode;
|
|
118
|
+
disabled?: boolean;
|
|
119
|
+
}
|
|
120
|
+
type ChoiceCardsProps = Omit<React.ComponentProps<typeof RadioGroup.Root>, "children" | "variant" | "label"> & {
|
|
121
|
+
/** The question (“Which side of the lease are you?”) — the group's accessible name, passed translated. */
|
|
122
|
+
label: string;
|
|
123
|
+
/** Shows the question above the cards, with `description` under it. */
|
|
124
|
+
showLabel?: boolean;
|
|
125
|
+
/** One line under the visible question. */
|
|
126
|
+
description?: React.ReactNode;
|
|
127
|
+
/** The choices. */
|
|
128
|
+
options: readonly ChoiceCardOption[];
|
|
129
|
+
/** The chosen id (controlled). */
|
|
130
|
+
value?: string;
|
|
131
|
+
/** The viewer chooses another card. */
|
|
132
|
+
onValueChange?: (id: string) => void;
|
|
133
|
+
};
|
|
134
|
+
/** Cards to choose one from: a radio group where each card has an icon, a title and a description. */
|
|
135
|
+
declare function ChoiceCards({ label, showLabel, description, options, className, ...props }: ChoiceCardsProps): React.JSX.Element;
|
|
136
|
+
|
|
137
|
+
interface LanguageOption {
|
|
138
|
+
/** The BCP 47 code (`fr`, `en`): the value, and the `lang` of the name. */
|
|
139
|
+
code: string;
|
|
140
|
+
/** Its native name (`Français`, `English`) — never the current language's name for it. */
|
|
141
|
+
name: string;
|
|
142
|
+
}
|
|
143
|
+
type LanguagePickerProps = Omit<React.ComponentProps<typeof RadioGroup.Root>, "children" | "variant" | "label" | "aria-labelledby"> & {
|
|
144
|
+
/** The visible label (“Language”) — passed translated. */
|
|
145
|
+
label: React.ReactNode;
|
|
146
|
+
/**
|
|
147
|
+
* Shows the label above the control; `false` when a `SettingsRow` already says it (the group keeps its accessible name).
|
|
148
|
+
* @default true
|
|
149
|
+
*/
|
|
150
|
+
showLabel?: boolean;
|
|
151
|
+
/** One line under it: what it changes. */
|
|
152
|
+
description?: React.ReactNode;
|
|
153
|
+
/** The languages offered. */
|
|
154
|
+
languages: readonly LanguageOption[];
|
|
155
|
+
/** The chosen code (controlled). */
|
|
156
|
+
value?: string;
|
|
157
|
+
/** The viewer chooses another language. */
|
|
158
|
+
onValueChange?: (code: string) => void;
|
|
159
|
+
};
|
|
160
|
+
/** The language control of an account page: a labelled segmented choice, native names. */
|
|
161
|
+
declare function LanguagePicker({ label, showLabel, description, languages, className, ...props }: LanguagePickerProps): React.JSX.Element;
|
|
162
|
+
|
|
163
|
+
export { AccountProfileCard, type AccountProfileCardProps, type AvatarOption, AvatarPicker, type AvatarPickerProps, type ChoiceCardOption, ChoiceCards, type ChoiceCardsProps, type LanguageOption, LanguagePicker, type LanguagePickerProps };
|