@evinvest/uikit 0.29.4 → 0.31.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/README.md +294 -90
- package/dist/{chunk-3IPB4QQJ.js → chunk-35SPIU4U.js} +10 -6
- package/dist/chunk-35SPIU4U.js.map +1 -0
- package/dist/components/account-list.js +128 -0
- package/dist/components/account-list.js.map +1 -0
- package/dist/components/account-menu-profile.js +114 -0
- package/dist/components/account-menu-profile.js.map +1 -0
- package/dist/components/account-menu.js +50 -23
- package/dist/components/account-menu.js.map +1 -1
- package/dist/components/account-settings-dialog.js +84 -0
- package/dist/components/account-settings-dialog.js.map +1 -0
- package/dist/components/alert-dialog.js +3 -2
- package/dist/components/alert-dialog.js.map +1 -1
- package/dist/components/animated-number-columns.js +105 -0
- package/dist/components/animated-number-columns.js.map +1 -0
- package/dist/components/animated-number.js +157 -0
- package/dist/components/animated-number.js.map +1 -0
- package/dist/components/app-shell.js +9 -0
- package/dist/components/app-shell.js.map +1 -1
- package/dist/components/aside.js +140 -0
- package/dist/components/aside.js.map +1 -0
- package/dist/components/badge.js +1 -1
- package/dist/components/badge.js.map +1 -1
- package/dist/components/button.js +3 -11
- package/dist/components/button.js.map +1 -1
- package/dist/components/chat-composer.js +141 -0
- package/dist/components/chat-composer.js.map +1 -0
- package/dist/components/chat-message.js +195 -0
- package/dist/components/chat-message.js.map +1 -0
- package/dist/components/chat-panel.js +137 -0
- package/dist/components/chat-panel.js.map +1 -0
- package/dist/components/chat-stream-text.js +69 -0
- package/dist/components/chat-stream-text.js.map +1 -0
- package/dist/components/chat-typing.js +26 -0
- package/dist/components/chat-typing.js.map +1 -0
- package/dist/components/command-palette.js +119 -0
- package/dist/components/command-palette.js.map +1 -0
- package/dist/components/command.js +3 -1
- package/dist/components/command.js.map +1 -1
- package/dist/components/confirm-dialog.js +123 -0
- package/dist/components/confirm-dialog.js.map +1 -0
- package/dist/components/context-menu.js +53 -5
- package/dist/components/context-menu.js.map +1 -1
- package/dist/components/dialog.js +3 -2
- package/dist/components/dialog.js.map +1 -1
- package/dist/components/drawer.js +3 -1
- package/dist/components/drawer.js.map +1 -1
- package/dist/components/dropdown-menu.js +64 -5
- package/dist/components/dropdown-menu.js.map +1 -1
- package/dist/components/form-row.js +51 -0
- package/dist/components/form-row.js.map +1 -0
- package/dist/components/header-controls.js +149 -0
- package/dist/components/header-controls.js.map +1 -0
- package/dist/components/icon-button.js +35 -0
- package/dist/components/icon-button.js.map +1 -0
- package/dist/components/key-value.js +68 -0
- package/dist/components/key-value.js.map +1 -0
- package/dist/components/menubar.js +39 -5
- package/dist/components/menubar.js.map +1 -1
- package/dist/components/native-select.js +3 -4
- package/dist/components/native-select.js.map +1 -1
- package/dist/components/nav-collapse.js +142 -0
- package/dist/components/nav-collapse.js.map +1 -0
- package/dist/components/nav-model.js.map +1 -1
- package/dist/components/navigation-menu.js +4 -5
- package/dist/components/navigation-menu.js.map +1 -1
- package/dist/components/page-frame.js +2 -5
- package/dist/components/page-frame.js.map +1 -1
- package/dist/components/popover-form.js +60 -0
- package/dist/components/popover-form.js.map +1 -0
- package/dist/components/resource-error.js +1 -1
- package/dist/components/resource-error.js.map +1 -1
- package/dist/components/section-nav.js +73 -20
- package/dist/components/section-nav.js.map +1 -1
- package/dist/components/select.js +16 -6
- package/dist/components/select.js.map +1 -1
- package/dist/components/settings.js +94 -0
- package/dist/components/settings.js.map +1 -0
- package/dist/components/sheet.js +3 -2
- package/dist/components/sheet.js.map +1 -1
- package/dist/components/shell-nav.js +80 -38
- package/dist/components/shell-nav.js.map +1 -1
- package/dist/components/smart-header.js +53 -0
- package/dist/components/smart-header.js.map +1 -0
- package/dist/components/status-item.js +42 -0
- package/dist/components/status-item.js.map +1 -0
- package/dist/components/system-banner.js +22 -12
- package/dist/components/system-banner.js.map +1 -1
- package/dist/components/top-bar.js +4 -3
- package/dist/components/top-bar.js.map +1 -1
- package/dist/components/ui-icons.js +77 -0
- package/dist/components/ui-icons.js.map +1 -0
- package/dist/components/use-count-tween.js +42 -0
- package/dist/components/use-count-tween.js.map +1 -0
- package/dist/components/workspace-tab.js +140 -0
- package/dist/components/workspace-tab.js.map +1 -0
- package/dist/generated/accent.js +2 -2
- package/dist/generated/accent.js.map +1 -1
- package/dist/generated/account-list.js +35 -0
- package/dist/generated/account-list.js.map +1 -0
- package/dist/generated/account-settings-dialog.js +27 -0
- package/dist/generated/account-settings-dialog.js.map +1 -0
- package/dist/generated/alert-dialog.js +1 -1
- package/dist/generated/alert-dialog.js.map +1 -1
- package/dist/generated/alert.js +2 -2
- package/dist/generated/alert.js.map +1 -1
- package/dist/generated/aside.js +36 -0
- package/dist/generated/aside.js.map +1 -0
- package/dist/generated/badge.js +7 -6
- package/dist/generated/badge.js.map +1 -1
- package/dist/generated/band.js +1 -1
- package/dist/generated/band.js.map +1 -1
- package/dist/generated/breadcrumb.js +2 -2
- package/dist/generated/breadcrumb.js.map +1 -1
- package/dist/generated/button-group.js +2 -2
- package/dist/generated/button-group.js.map +1 -1
- package/dist/generated/button.js +4 -4
- package/dist/generated/button.js.map +1 -1
- package/dist/generated/card.js +6 -6
- package/dist/generated/card.js.map +1 -1
- package/dist/generated/chat-composer.js +21 -0
- package/dist/generated/chat-composer.js.map +1 -0
- package/dist/generated/chat-message.js +86 -0
- package/dist/generated/chat-message.js.map +1 -0
- package/dist/generated/chat-typing.js +9 -0
- package/dist/generated/chat-typing.js.map +1 -0
- package/dist/generated/command-palette.js +17 -0
- package/dist/generated/command-palette.js.map +1 -0
- package/dist/generated/command.js +3 -3
- package/dist/generated/command.js.map +1 -1
- package/dist/generated/confirm-dialog.js +28 -0
- package/dist/generated/confirm-dialog.js.map +1 -0
- package/dist/generated/context-menu.js +1 -1
- package/dist/generated/context-menu.js.map +1 -1
- package/dist/generated/date-time-picker.js +1 -1
- package/dist/generated/date-time-picker.js.map +1 -1
- package/dist/generated/dialog.js +1 -1
- package/dist/generated/dialog.js.map +1 -1
- package/dist/generated/drawer.js +1 -1
- package/dist/generated/drawer.js.map +1 -1
- package/dist/generated/dropdown-menu.js +1 -1
- package/dist/generated/dropdown-menu.js.map +1 -1
- package/dist/generated/form-row.js +17 -0
- package/dist/generated/form-row.js.map +1 -0
- package/dist/generated/header-controls.js +56 -0
- package/dist/generated/header-controls.js.map +1 -0
- package/dist/generated/icon-button.js +13 -0
- package/dist/generated/icon-button.js.map +1 -0
- package/dist/generated/key-value.js +15 -0
- package/dist/generated/key-value.js.map +1 -0
- package/dist/generated/menubar.js +1 -1
- package/dist/generated/menubar.js.map +1 -1
- package/dist/generated/navigation-menu.js +1 -1
- package/dist/generated/navigation-menu.js.map +1 -1
- package/dist/generated/popover-form.js +13 -0
- package/dist/generated/popover-form.js.map +1 -0
- package/dist/generated/section-label.js +10 -0
- package/dist/generated/section-label.js.map +1 -0
- package/dist/generated/select.js +15 -3
- package/dist/generated/select.js.map +1 -1
- package/dist/generated/settings.js +36 -0
- package/dist/generated/settings.js.map +1 -0
- package/dist/generated/sheet.js +1 -1
- package/dist/generated/sheet.js.map +1 -1
- package/dist/generated/slider.js +1 -1
- package/dist/generated/slider.js.map +1 -1
- package/dist/generated/smart-header.js +11 -0
- package/dist/generated/smart-header.js.map +1 -0
- package/dist/generated/sonner.js +4 -4
- package/dist/generated/sonner.js.map +1 -1
- package/dist/generated/status-item.js +17 -0
- package/dist/generated/status-item.js.map +1 -0
- package/dist/generated/system-banner.js +33 -0
- package/dist/generated/system-banner.js.map +1 -0
- package/dist/generated/top-bar.js +9 -0
- package/dist/generated/top-bar.js.map +1 -0
- package/dist/generated/workspace-tab.js +17 -0
- package/dist/generated/workspace-tab.js.map +1 -0
- package/dist/index.d.ts +852 -156
- package/dist/index.js +21 -1
- package/dist/lib/cn.js +8 -1
- package/dist/lib/cn.js.map +1 -1
- package/dist/lib/number-parts.js +90 -0
- package/dist/lib/number-parts.js.map +1 -0
- package/dist/palette-cli.js +1 -1
- package/dist/palette.d.ts +6 -4
- package/dist/palette.js +3 -1
- package/dist/primitives/dismissable-layer.js +76 -4
- package/dist/primitives/dismissable-layer.js.map +1 -1
- package/dist/primitives/focus-scope.js +20 -5
- package/dist/primitives/focus-scope.js.map +1 -1
- package/dist/primitives/use-floating.js +16 -1
- package/dist/primitives/use-floating.js.map +1 -1
- package/dist/primitives/use-reduced-motion.js +20 -0
- package/dist/primitives/use-reduced-motion.js.map +1 -0
- package/package.json +1 -1
- package/styles/ev.css +281 -49
- package/styles/theme.css +335 -39
- package/styles/tokens.css +643 -103
- package/dist/chunk-3IPB4QQJ.js.map +0 -1
- package/dist/components/terminal.js +0 -206
- package/dist/components/terminal.js.map +0 -1
- package/dist/generated/terminal.js +0 -70
- package/dist/generated/terminal.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/palette/contract.ts","../src/palette/toml.ts","../src/palette/brand.ts"],"sourcesContent":["/**\n * The colour contract, read out of the kit's own token sheet rather than\n * restated here: `@theme inline` maps every name the class tables can paint\n * (`--color-X: var(--X)`), and the derived rule lists the ones the contract\n * computes from the rest. A palette owes the difference.\n */\nexport interface Contract {\n /** Names every palette scope must declare. */\n required: readonly string[];\n /** Names the contract derives from the palette; a palette may override them. */\n derived: readonly string[];\n /** The tokens each derived default reads, so a palette's references can be checked for cycles through them. */\n derivedFrom: Readonly<Record<string, readonly string[]>>;\n}\n\n/** The `--name`s a value reads through `var()`. */\nexport function references(value: string): string[] {\n return [...value.matchAll(/var\\(--([A-Za-z0-9-]+)/g)].flatMap((m) => (m[1] ? [m[1]] : []));\n}\n\n/** The rule the derived tokens live on — see `theme.css`, the contract half of `tokens.css`. */\nexport const DERIVED_SCOPE = \":where(:root, .dark, .light, [data-brand], [data-theme])\";\n\n/** The same scopes in another order: the button faces and chips, derived the same way. */\nexport const FACES_SCOPE = \":where([data-theme], [data-brand], .light, .dark, :root)\";\n\nexport interface CssRule {\n selector: string;\n /** Custom properties declared directly in the rule, in source order. */\n declarations: Map<string, string>;\n}\n\n/**\n * Top-level and `@media`-nested rules of a flat sheet, comments stripped. Only\n * as much CSS as a token sheet is made of: no strings containing braces.\n */\nexport function readRules(css: string): CssRule[] {\n const rules: CssRule[] = [];\n const src = css.replace(/\\/\\*[\\s\\S]*?\\*\\//g, \"\");\n const stack: string[] = [];\n let start = 0;\n for (let i = 0; i < src.length; i++) {\n const c = src[i];\n if (c === \"{\") {\n stack.push(src.slice(start, i).trim());\n start = i + 1;\n } else if (c === \"}\") {\n const selector = stack.pop();\n if (selector === undefined) throw new Error(\"unbalanced `}` in token sheet\");\n const body = src.slice(start, i);\n if (!body.includes(\"{\")) rules.push({ selector: normalise(selector), declarations: declarations(body) });\n start = i + 1;\n } else if (c === \";\" && stack.length === 0) {\n start = i + 1;\n }\n }\n if (stack.length > 0) throw new Error(\"unbalanced `{` in token sheet\");\n return rules;\n}\n\nfunction declarations(body: string): Map<string, string> {\n const out = new Map<string, string>();\n for (const decl of body.split(\";\")) {\n const match = /^\\s*--([A-Za-z0-9-]+)\\s*:\\s*([\\s\\S]+?)\\s*$/.exec(decl);\n if (match?.[1] && match[2]) out.set(match[1], match[2]);\n }\n return out;\n}\n\nexport function normalise(selector: string): string {\n return selector.replace(/\\s+/g, \" \").replace(/\\s*,\\s*/g, \", \").trim();\n}\n\nexport function readContract(css: string): Contract {\n const rules = readRules(css);\n const theme = rules.find((r) => r.selector === \"@theme inline\");\n if (!theme) throw new Error(\"token sheet has no `@theme inline` block — not the kit's contract\");\n const painted: string[] = [];\n for (const [name, value] of theme.declarations) {\n const match = /^color-(.+)$/.exec(name);\n if (match?.[1] && value === `var(--${match[1]})`) painted.push(match[1]);\n }\n const derivedRule = rules.find((r) => r.selector === DERIVED_SCOPE);\n if (!derivedRule) throw new Error(`token sheet has no \\`${DERIVED_SCOPE}\\` rule — not the kit's contract`);\n const facesRule = rules.find((r) => r.selector === FACES_SCOPE);\n const formulas = new Map([...derivedRule.declarations, ...(facesRule?.declarations ?? [])]);\n const derived = [...formulas.keys()];\n const stray = derived.filter((d) => !painted.includes(d));\n if (stray.length > 0) throw new Error(`derived tokens with no @theme mapping: ${stray.join(\", \")}`);\n const derivedFrom = Object.fromEntries([...formulas].map(([name, value]) => [name, references(value)]));\n return { required: painted.filter((p) => !derived.includes(p)), derived, derivedFrom };\n}\n","/**\n * The subset of TOML a brand file is written in: `[table.headers]`, bare keys,\n * and single-line values that are a basic string, a number, a boolean, or a\n * flat array of those. Anything else is an error naming the line, rather than a\n * silent misread — the kit stays dependency-free, and a brand file has no use\n * for the rest of the language.\n */\n\nexport type TomlValue = string | number | boolean | TomlValue[];\nexport interface TomlTable {\n [key: string]: TomlValue | TomlTable;\n}\n\nconst BARE_KEY = /^[A-Za-z0-9_-]+$/;\n\nexport function parseToml(text: string): TomlTable {\n const root: TomlTable = {};\n let table = root;\n const headers = new Set<string>();\n text.split(/\\r?\\n/).forEach((raw, index) => {\n const line = stripComment(raw).trim();\n if (line === \"\") return;\n const fail = (why: string): never => {\n throw new Error(`TOML line ${index + 1}: ${why}: ${raw.trim()}`);\n };\n\n if (line.startsWith(\"[\")) {\n if (!line.endsWith(\"]\") || line.startsWith(\"[[\")) fail(\"only plain [table] headers are supported\");\n const path = line.slice(1, -1).trim().split(\".\").map((part) => part.trim());\n // Re-opening a table would merge two halves of it silently.\n if (headers.has(path.join(\".\"))) fail(\"duplicate table header\");\n headers.add(path.join(\".\"));\n table = descend(root, path, fail);\n return;\n }\n\n const eq = line.indexOf(\"=\");\n if (eq < 0) fail(\"expected `key = value`\");\n const key = line.slice(0, eq).trim();\n if (!BARE_KEY.test(key)) fail(\"only bare keys are supported\");\n if (key in table) fail(`duplicate key \\`${key}\\``);\n table[key] = parseValue(line.slice(eq + 1).trim(), fail);\n });\n return root;\n}\n\nfunction descend(root: TomlTable, path: string[], fail: (why: string) => never): TomlTable {\n let table = root;\n for (const part of path) {\n const key = part.trim();\n if (!BARE_KEY.test(key)) fail(\"only bare keys are supported in a header\");\n const next = table[key] ?? {};\n if (typeof next !== \"object\" || Array.isArray(next)) fail(`\\`${key}\\` is already a value`);\n table[key] = next;\n table = next as TomlTable;\n }\n return table;\n}\n\n// A `#` inside a string is not a comment; the brand files carry `\"#rrggbb\"`.\nfunction stripComment(line: string): string {\n let quoted = false;\n for (let i = 0; i < line.length; i++) {\n const c = line[i];\n if (c === \"\\\\\" && quoted) i++;\n else if (c === '\"') quoted = !quoted;\n else if (c === \"#\" && !quoted) return line.slice(0, i);\n }\n return line;\n}\n\nfunction parseValue(src: string, fail: (why: string) => never): TomlValue {\n if (src.startsWith('\"')) {\n if (src.length < 2 || !src.endsWith('\"')) fail(\"unterminated string\");\n const body = src.slice(1, -1);\n if (/(^|[^\\\\])(\\\\\\\\)*\"/.test(body)) fail(\"one string per value\");\n // Only the escapes decoded below; `\\u…` and friends would otherwise pass through as literal text.\n if (/\\\\(?![\"\\\\nt])/.test(body.replace(/\\\\\\\\/g, \"\"))) fail(\"unsupported escape\");\n return body.replace(/\\\\([\"\\\\nt])/g, (_, c: string) => ({ n: \"\\n\", t: \"\\t\" })[c] ?? c);\n }\n if (src.startsWith(\"[\")) {\n if (!src.endsWith(\"]\")) fail(\"arrays must close on the same line\");\n const inner = src.slice(1, -1).trim();\n if (inner === \"\") return [];\n return splitTopLevel(inner).map((item) => parseValue(item.trim(), fail));\n }\n if (src === \"true\" || src === \"false\") return src === \"true\";\n if (/^[+-]?\\d+(\\.\\d+)?$/.test(src)) return Number(src);\n return fail(\"unsupported value\");\n}\n\nfunction splitTopLevel(inner: string): string[] {\n const items: string[] = [];\n let quoted = false;\n let start = 0;\n for (let i = 0; i < inner.length; i++) {\n const c = inner[i];\n if (c === \"\\\\\" && quoted) i++;\n else if (c === '\"') quoted = !quoted;\n else if (c === \",\" && !quoted) {\n items.push(inner.slice(start, i));\n start = i + 1;\n }\n }\n const last = inner.slice(start);\n if (last.trim() !== \"\") items.push(last);\n return items;\n}\n","import { references, type Contract } from \"./contract\";\nimport { parseToml, type TomlTable, type TomlValue } from \"./toml\";\n\nexport type PolarityName = \"light\" | \"dark\";\nexport const POLARITIES: readonly PolarityName[] = [\"light\", \"dark\"];\n\n/** A brand's palette: one value per contract name, for each polarity. */\nexport interface BrandConfig {\n colors: Record<PolarityName, Record<string, string>>;\n /** `--brand-mark`: the URL `<Logo>` masks. */\n mark?: string;\n /** `--brand-aspect`: the mark's width / height, e.g. `\"387 / 335\"`. */\n markAspect?: string;\n /** `--brand-font-<role>`: a family name per role; unset roles keep the kit's. */\n fonts?: Partial<Record<FontRole, string>>;\n}\n\nexport type FontRole = \"sans\" | \"serif\" | \"mono\";\nexport const FONT_ROLES: readonly FontRole[] = [\"sans\", \"serif\", \"mono\"];\n// aquafix's file calls the body family `text`.\nconst FONT_KEYS: Record<string, FontRole> = { sans: \"sans\", text: \"sans\", serif: \"serif\", mono: \"mono\" };\n\n/** Every problem with a brand file at once, so one run fixes them all. */\nexport class PaletteError extends Error {\n constructor(readonly problems: readonly string[]) {\n super(`invalid brand palette:\\n - ${problems.join(\"\\n - \")}`);\n this.name = \"PaletteError\";\n }\n}\n\n// Written into CSS verbatim, so a value is held to a shape that cannot close\n// the declaration: a hex colour, or a reference to another token.\nconst COLOUR = /^(#[0-9a-fA-F]{3}|#[0-9a-fA-F]{6}|#[0-9a-fA-F]{8}|var\\(--[a-z0-9-]+\\))$/;\nconst SLUG = /^[a-z0-9][a-z0-9-]*$/;\nconst ASPECT = /^\\d+(\\.\\d+)?\\s*\\/\\s*\\d+(\\.\\d+)?$/;\n// Written inside `url(\"…\")`: nothing that closes the string or the function,\n// and no markup characters for a sheet that ends up inlined in HTML.\nconst MARK_URL = /^[^\"'`\\\\\\s()<>]+$/;\nconst FAMILY = /^[A-Za-z0-9][A-Za-z0-9 _-]*$/;\n\n/**\n * Reads the aquafix `brand.toml` shape: `[colors.light]` and `[colors.dark]`\n * keyed by contract name, optional top-level `mark` / `mark-aspect`, and the\n * family names in `[fonts]` (`sans` or `text`, `serif`, `mono`). The rest of\n * `[fonts]` and every other table (`[print]`, …) belong to other tools and are\n * ignored — loading the font files is the site's job, not the palette's.\n */\nexport function brandFromToml(text: string): BrandConfig {\n const doc = parseToml(text);\n const problems: string[] = [];\n const colors = table(doc[\"colors\"]);\n if (!colors) problems.push(\"no [colors] table\");\n const scope = (name: PolarityName): Record<string, string> => {\n const t = colors ? table(colors[name]) : undefined;\n if (colors && !t) problems.push(`no [colors.${name}] table`);\n const out: Record<string, string> = {};\n for (const [key, value] of Object.entries(t ?? {})) {\n if (typeof value === \"string\") out[key] = value;\n else problems.push(`colors.${name}.${key} is not a string`);\n }\n return out;\n };\n for (const key of Object.keys(colors ?? {})) {\n if (!(POLARITIES as readonly string[]).includes(key)) problems.push(`[colors.${key}] is not a polarity (light, dark)`);\n }\n const config: BrandConfig = { colors: { light: scope(\"light\"), dark: scope(\"dark\") } };\n const mark = optionalString(doc[\"mark\"], \"mark\", problems);\n const markAspect = optionalString(doc[\"mark-aspect\"], \"mark-aspect\", problems);\n if (mark !== undefined) config.mark = mark;\n if (markAspect !== undefined) config.markAspect = markAspect;\n const fonts = table(doc[\"fonts\"]);\n for (const [key, role] of Object.entries(FONT_KEYS)) {\n const family = optionalString(fonts?.[key], `fonts.${key}`, problems);\n if (family !== undefined) config.fonts = { ...config.fonts, [role]: family };\n }\n if (problems.length > 0) throw new PaletteError(problems);\n return config;\n}\n\nfunction table(value: TomlValue | TomlTable | undefined): TomlTable | undefined {\n return typeof value === \"object\" && !Array.isArray(value) ? value : undefined;\n}\n\nfunction optionalString(value: TomlValue | TomlTable | undefined, key: string, problems: string[]) {\n if (value === undefined || typeof value === \"string\") return value;\n problems.push(`${key} is not a string`);\n return undefined;\n}\n\n/** The problems with `config` against `contract`; empty when it is complete. */\nexport function validateBrand(config: BrandConfig, contract: Contract): string[] {\n const problems: string[] = [];\n const known = new Set([...contract.required, ...contract.derived]);\n for (const polarity of POLARITIES) {\n const values = config.colors[polarity];\n const missing = contract.required.filter((t) => !(t in values));\n if (missing.length > 0) problems.push(`${polarity} is missing ${missing.join(\", \")}`);\n for (const [key, value] of Object.entries(values)) {\n if (!known.has(key)) problems.push(`${polarity}.${key} is not a token of the contract`);\n else if (!COLOUR.test(value)) problems.push(`${polarity}.${key} = ${JSON.stringify(value)} is not #hex or var(--token)`);\n else {\n for (const ref of references(value)) {\n if (!known.has(ref)) problems.push(`${polarity}.${key} refers to --${ref}, which is not a token of the contract`);\n }\n }\n }\n const cycle = findCycle(values, contract, known);\n if (cycle) problems.push(`${polarity} has a reference cycle: ${cycle.map((t) => `--${t}`).join(\" → \")}`);\n }\n if (config.mark !== undefined && !MARK_URL.test(config.mark)) problems.push(`mark ${JSON.stringify(config.mark)} is not a plain URL`);\n if (config.markAspect !== undefined && !ASPECT.test(config.markAspect)) {\n problems.push(`mark-aspect ${JSON.stringify(config.markAspect)} is not \"width / height\"`);\n }\n for (const [role, family] of Object.entries(config.fonts ?? {})) {\n if (!FAMILY.test(family)) problems.push(`fonts.${role} ${JSON.stringify(family)} is not a plain family name`);\n }\n return problems;\n}\n\n/**\n * A `var()` cycle makes every token on it invalid at computed-value time — the\n * same silent hole as a missing token. Edges are the palette's own references,\n * and for a derived token it leaves to the contract, the contract's formula\n * (`border` reads `ink`), so `ink = \"var(--border)\"` is caught too.\n */\nfunction findCycle(values: Record<string, string>, contract: Contract, known: Set<string>): string[] | undefined {\n const edges = (name: string): readonly string[] => {\n const value = values[name];\n if (value !== undefined) return references(value).filter((r) => known.has(r));\n return contract.derivedFrom[name] ?? [];\n };\n const done = new Set<string>();\n const path: string[] = [];\n const visit = (name: string): string[] | undefined => {\n const at = path.indexOf(name);\n if (at >= 0) return [...path.slice(at), name];\n if (done.has(name)) return undefined;\n path.push(name);\n for (const next of edges(name)) {\n const cycle = visit(next);\n if (cycle) return cycle;\n }\n path.pop();\n done.add(name);\n return undefined;\n };\n for (const name of known) {\n const cycle = visit(name);\n if (cycle) return cycle;\n }\n return undefined;\n}\n\n/**\n * A flat sheet scoping `config` under `[data-brand=\"<brand>\"]`: light is the\n * brand's default and `.light` / `.dark` beneath it pick a polarity. Selectors\n * outrank the zero-specificity EV palette, so import order does not matter.\n * Throws a {@link PaletteError} when a scope is incomplete — Tailwind answers an\n * undefined token with no rule at all, so a hole would be silent on the page.\n */\nexport function renderPalette(brand: string, config: BrandConfig, contract: Contract, source = \"a brand file\"): string {\n const problems = SLUG.test(brand) ? [] : [`brand ${JSON.stringify(brand)} is not a lowercase slug`];\n problems.push(...validateBrand(config, contract));\n if (problems.length > 0) throw new PaletteError(problems);\n\n const at = `[data-brand=\"${brand}\"]`;\n const order = [...contract.required, ...contract.derived];\n const block = (selectors: string[], polarity: PolarityName) => {\n const values = config.colors[polarity];\n const body = order.filter((t) => t in values).map((t) => ` --${t}: ${values[t]};\\n`);\n return `${selectors.join(\",\\n\")} {\\n --scheme: ${polarity};\\n${body.join(\"\")}}\\n`;\n };\n const extras: string[] = [];\n if (config.mark !== undefined) extras.push(` --brand-mark: url(\"${config.mark}\");\\n`);\n if (config.markAspect !== undefined) extras.push(` --brand-aspect: ${config.markAspect};\\n`);\n for (const role of FONT_ROLES) {\n const family = config.fonts?.[role];\n if (family !== undefined) extras.push(` --brand-font-${role}: \"${family}\";\\n`);\n }\n\n return [\n // `source` is a file name, free text as far as CSS is concerned: it must not close the comment.\n `/* GENERATED by evinvest-palette from ${source.replaceAll(\"*/\", \"*\\\\/\")} — do not edit. */\\n`,\n block([at, `${at}.light`, `${at} .light`], \"light\"),\n block([`${at}.dark`, `${at} .dark`], \"dark\"),\n ...(extras.length > 0 ? [`${at} {\\n${extras.join(\"\")}}\\n`] : []),\n ].join(\"\\n\");\n}\n"],"mappings":"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","names":["table"]}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import {
|
|
5
|
+
ACCOUNT_LIST_BASE,
|
|
6
|
+
ACCOUNT_LIST_ITEM_ACTIONS,
|
|
7
|
+
ACCOUNT_LIST_ITEM_AVATAR,
|
|
8
|
+
ACCOUNT_LIST_ITEM_AVATAR_FALLBACK,
|
|
9
|
+
ACCOUNT_LIST_ITEM_BADGES,
|
|
10
|
+
ACCOUNT_LIST_ITEM_BASE,
|
|
11
|
+
ACCOUNT_LIST_ITEM_CHECK,
|
|
12
|
+
ACCOUNT_LIST_ITEM_CHECK_HIDDEN,
|
|
13
|
+
ACCOUNT_LIST_ITEM_CHECKED,
|
|
14
|
+
ACCOUNT_LIST_ITEM_DISABLED,
|
|
15
|
+
ACCOUNT_LIST_ITEM_FIGURE,
|
|
16
|
+
ACCOUNT_LIST_ITEM_HANDLE,
|
|
17
|
+
ACCOUNT_LIST_ITEM_NAME,
|
|
18
|
+
ACCOUNT_LIST_ITEM_RADIO,
|
|
19
|
+
ACCOUNT_LIST_ITEM_TEXT,
|
|
20
|
+
ACCOUNT_LIST_ITEM_UNCHECKED
|
|
21
|
+
} from "../generated/account-list.js";
|
|
22
|
+
import { cn } from "../lib/cn.js";
|
|
23
|
+
import { useControllableState } from "../primitives/use-controllable-state.js";
|
|
24
|
+
import { Avatar, AvatarFallback } from "./avatar.js";
|
|
25
|
+
import { UiIcon } from "./ui-icons.js";
|
|
26
|
+
const AccountListContext = React.createContext(null);
|
|
27
|
+
function AccountList({
|
|
28
|
+
value,
|
|
29
|
+
defaultValue = "",
|
|
30
|
+
onValueChange,
|
|
31
|
+
className,
|
|
32
|
+
onKeyDown,
|
|
33
|
+
...props
|
|
34
|
+
}) {
|
|
35
|
+
const [current, setValue] = useControllableState({
|
|
36
|
+
...value !== void 0 ? { value } : {},
|
|
37
|
+
defaultValue,
|
|
38
|
+
...onValueChange ? { onChange: onValueChange } : {}
|
|
39
|
+
});
|
|
40
|
+
const move = (e) => {
|
|
41
|
+
const forward = e.key === "ArrowDown" || e.key === "ArrowRight";
|
|
42
|
+
const back = e.key === "ArrowUp" || e.key === "ArrowLeft";
|
|
43
|
+
if (!forward && !back) return;
|
|
44
|
+
const radios = Array.from(
|
|
45
|
+
e.currentTarget.querySelectorAll('[role="radio"]:not(:disabled)')
|
|
46
|
+
);
|
|
47
|
+
const at = radios.indexOf(document.activeElement);
|
|
48
|
+
if (at < 0) return;
|
|
49
|
+
e.preventDefault();
|
|
50
|
+
const next = radios[(at + (forward ? 1 : -1) + radios.length) % radios.length];
|
|
51
|
+
next?.focus();
|
|
52
|
+
next?.click();
|
|
53
|
+
};
|
|
54
|
+
return /* @__PURE__ */ jsx(AccountListContext.Provider, { value: { value: current, setValue }, children: /* @__PURE__ */ jsx(
|
|
55
|
+
"div",
|
|
56
|
+
{
|
|
57
|
+
role: "radiogroup",
|
|
58
|
+
"data-slot": "account-list",
|
|
59
|
+
className: cn(ACCOUNT_LIST_BASE, className),
|
|
60
|
+
onKeyDown: (e) => {
|
|
61
|
+
onKeyDown?.(e);
|
|
62
|
+
if (!e.defaultPrevented) move(e);
|
|
63
|
+
},
|
|
64
|
+
...props
|
|
65
|
+
}
|
|
66
|
+
) });
|
|
67
|
+
}
|
|
68
|
+
function AccountListItem({
|
|
69
|
+
value,
|
|
70
|
+
name,
|
|
71
|
+
handle,
|
|
72
|
+
avatar,
|
|
73
|
+
badges,
|
|
74
|
+
figure,
|
|
75
|
+
actions,
|
|
76
|
+
disabled,
|
|
77
|
+
className,
|
|
78
|
+
...props
|
|
79
|
+
}) {
|
|
80
|
+
const ctx = React.useContext(AccountListContext);
|
|
81
|
+
if (!ctx) throw new Error("AccountListItem must be used within <AccountList>");
|
|
82
|
+
const checked = ctx.value === value;
|
|
83
|
+
const initial = typeof name === "string" ? name.trim().charAt(0).toUpperCase() : "";
|
|
84
|
+
return /* @__PURE__ */ jsxs(
|
|
85
|
+
"div",
|
|
86
|
+
{
|
|
87
|
+
"data-slot": "account-list-item",
|
|
88
|
+
"data-state": checked ? "checked" : "unchecked",
|
|
89
|
+
className: cn(
|
|
90
|
+
ACCOUNT_LIST_ITEM_BASE,
|
|
91
|
+
checked ? ACCOUNT_LIST_ITEM_CHECKED : ACCOUNT_LIST_ITEM_UNCHECKED,
|
|
92
|
+
disabled && ACCOUNT_LIST_ITEM_DISABLED,
|
|
93
|
+
className
|
|
94
|
+
),
|
|
95
|
+
...props,
|
|
96
|
+
children: [
|
|
97
|
+
/* @__PURE__ */ jsxs(
|
|
98
|
+
"button",
|
|
99
|
+
{
|
|
100
|
+
type: "button",
|
|
101
|
+
role: "radio",
|
|
102
|
+
"aria-checked": checked,
|
|
103
|
+
disabled,
|
|
104
|
+
tabIndex: checked || ctx.value === "" ? 0 : -1,
|
|
105
|
+
onClick: () => ctx.setValue(value),
|
|
106
|
+
className: ACCOUNT_LIST_ITEM_RADIO,
|
|
107
|
+
children: [
|
|
108
|
+
avatar ?? /* @__PURE__ */ jsx(Avatar, { className: ACCOUNT_LIST_ITEM_AVATAR, children: /* @__PURE__ */ jsx(AvatarFallback, { className: ACCOUNT_LIST_ITEM_AVATAR_FALLBACK, children: initial }) }),
|
|
109
|
+
/* @__PURE__ */ jsxs("span", { className: ACCOUNT_LIST_ITEM_TEXT, children: [
|
|
110
|
+
/* @__PURE__ */ jsx("span", { className: ACCOUNT_LIST_ITEM_NAME, children: name }),
|
|
111
|
+
handle != null && /* @__PURE__ */ jsx("span", { className: ACCOUNT_LIST_ITEM_HANDLE, children: handle })
|
|
112
|
+
] }),
|
|
113
|
+
badges != null && /* @__PURE__ */ jsx("span", { className: ACCOUNT_LIST_ITEM_BADGES, children: badges }),
|
|
114
|
+
figure != null && /* @__PURE__ */ jsx("span", { className: ACCOUNT_LIST_ITEM_FIGURE, children: figure }),
|
|
115
|
+
/* @__PURE__ */ jsx("span", { className: cn(ACCOUNT_LIST_ITEM_CHECK, !checked && ACCOUNT_LIST_ITEM_CHECK_HIDDEN), "aria-hidden": true, children: /* @__PURE__ */ jsx(UiIcon, { name: "check" }) })
|
|
116
|
+
]
|
|
117
|
+
}
|
|
118
|
+
),
|
|
119
|
+
actions != null && /* @__PURE__ */ jsx("div", { className: ACCOUNT_LIST_ITEM_ACTIONS, children: actions })
|
|
120
|
+
]
|
|
121
|
+
}
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
export {
|
|
125
|
+
AccountList,
|
|
126
|
+
AccountListItem
|
|
127
|
+
};
|
|
128
|
+
//# sourceMappingURL=account-list.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/components/account-list.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport {\n ACCOUNT_LIST_BASE,\n ACCOUNT_LIST_ITEM_ACTIONS,\n ACCOUNT_LIST_ITEM_AVATAR,\n ACCOUNT_LIST_ITEM_AVATAR_FALLBACK,\n ACCOUNT_LIST_ITEM_BADGES,\n ACCOUNT_LIST_ITEM_BASE,\n ACCOUNT_LIST_ITEM_CHECK,\n ACCOUNT_LIST_ITEM_CHECK_HIDDEN,\n ACCOUNT_LIST_ITEM_CHECKED,\n ACCOUNT_LIST_ITEM_DISABLED,\n ACCOUNT_LIST_ITEM_FIGURE,\n ACCOUNT_LIST_ITEM_HANDLE,\n ACCOUNT_LIST_ITEM_NAME,\n ACCOUNT_LIST_ITEM_RADIO,\n ACCOUNT_LIST_ITEM_TEXT,\n ACCOUNT_LIST_ITEM_UNCHECKED,\n} from \"../generated/account-list\";\nimport { cn } from \"../lib/cn\";\nimport { useControllableState } from \"../primitives/use-controllable-state\";\nimport { Avatar, AvatarFallback } from \"./avatar\";\nimport { UiIcon } from \"./ui-icons\";\n\ninterface AccountListContextValue {\n value: string;\n setValue: (next: string) => void;\n}\n\nconst AccountListContext = React.createContext<AccountListContextValue | null>(null);\n\nexport interface AccountListProps extends Omit<React.ComponentProps<\"div\">, \"onChange\" | \"defaultValue\"> {\n value?: string;\n defaultValue?: string;\n onValueChange?: (value: string) => void;\n /** Accessible name of the group, e.g. \"Accounts\". */\n \"aria-label\": string;\n}\n\n/**\n * Pick the account you act as: one is current, arrows move and select\n * (radio-group semantics), so the current account is the only tab stop.\n */\nexport function AccountList({\n value,\n defaultValue = \"\",\n onValueChange,\n className,\n onKeyDown,\n ...props\n}: AccountListProps) {\n const [current, setValue] = useControllableState<string>({\n ...(value !== undefined ? { value } : {}),\n defaultValue,\n ...(onValueChange ? { onChange: onValueChange } : {}),\n });\n\n const move = (e: React.KeyboardEvent<HTMLDivElement>) => {\n const forward = e.key === \"ArrowDown\" || e.key === \"ArrowRight\";\n const back = e.key === \"ArrowUp\" || e.key === \"ArrowLeft\";\n if (!forward && !back) return;\n const radios = Array.from(\n e.currentTarget.querySelectorAll<HTMLButtonElement>('[role=\"radio\"]:not(:disabled)'),\n );\n const at = radios.indexOf(document.activeElement as HTMLButtonElement);\n if (at < 0) return;\n e.preventDefault();\n const next = radios[(at + (forward ? 1 : -1) + radios.length) % radios.length];\n next?.focus();\n next?.click();\n };\n\n return (\n <AccountListContext.Provider value={{ value: current, setValue }}>\n <div\n role=\"radiogroup\"\n data-slot=\"account-list\"\n className={cn(ACCOUNT_LIST_BASE, className)}\n onKeyDown={(e) => {\n onKeyDown?.(e);\n if (!e.defaultPrevented) move(e);\n }}\n {...props}\n />\n </AccountListContext.Provider>\n );\n}\n\nexport interface AccountListItemProps extends Omit<React.ComponentProps<\"div\">, \"value\" | \"title\"> {\n value: string;\n name: React.ReactNode;\n /** A line under the name: handle, email. */\n handle?: React.ReactNode;\n /** Replaces the initial-in-a-square default. */\n avatar?: React.ReactNode;\n /** Role badges after the name block. */\n badges?: React.ReactNode;\n /** A figure at the end, set in the serif face: a balance. */\n figure?: React.ReactNode;\n /** Controls beside the row (a menu button); they cannot live inside the radio. */\n actions?: React.ReactNode;\n disabled?: boolean;\n}\n\nexport function AccountListItem({\n value,\n name,\n handle,\n avatar,\n badges,\n figure,\n actions,\n disabled,\n className,\n ...props\n}: AccountListItemProps) {\n const ctx = React.useContext(AccountListContext);\n if (!ctx) throw new Error(\"AccountListItem must be used within <AccountList>\");\n const checked = ctx.value === value;\n const initial = typeof name === \"string\" ? name.trim().charAt(0).toUpperCase() : \"\";\n return (\n <div\n data-slot=\"account-list-item\"\n data-state={checked ? \"checked\" : \"unchecked\"}\n className={cn(\n ACCOUNT_LIST_ITEM_BASE,\n checked ? ACCOUNT_LIST_ITEM_CHECKED : ACCOUNT_LIST_ITEM_UNCHECKED,\n disabled && ACCOUNT_LIST_ITEM_DISABLED,\n className,\n )}\n {...props}\n >\n <button\n type=\"button\"\n role=\"radio\"\n aria-checked={checked}\n disabled={disabled}\n // Until one is chosen every radio is a tab stop, or the group could not be entered.\n tabIndex={checked || ctx.value === \"\" ? 0 : -1}\n onClick={() => ctx.setValue(value)}\n className={ACCOUNT_LIST_ITEM_RADIO}\n >\n {avatar ?? (\n <Avatar className={ACCOUNT_LIST_ITEM_AVATAR}>\n <AvatarFallback className={ACCOUNT_LIST_ITEM_AVATAR_FALLBACK}>{initial}</AvatarFallback>\n </Avatar>\n )}\n <span className={ACCOUNT_LIST_ITEM_TEXT}>\n <span className={ACCOUNT_LIST_ITEM_NAME}>{name}</span>\n {handle != null && <span className={ACCOUNT_LIST_ITEM_HANDLE}>{handle}</span>}\n </span>\n {badges != null && <span className={ACCOUNT_LIST_ITEM_BADGES}>{badges}</span>}\n {figure != null && <span className={ACCOUNT_LIST_ITEM_FIGURE}>{figure}</span>}\n <span className={cn(ACCOUNT_LIST_ITEM_CHECK, !checked && ACCOUNT_LIST_ITEM_CHECK_HIDDEN)} aria-hidden>\n <UiIcon name=\"check\" />\n </span>\n </button>\n {actions != null && <div className={ACCOUNT_LIST_ITEM_ACTIONS}>{actions}</div>}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { cn } from "../lib/cn.js";
|
|
5
|
+
import { Avatar, AvatarFallback } from "./avatar.js";
|
|
6
|
+
import { Badge } from "./badge.js";
|
|
7
|
+
import {
|
|
8
|
+
DropdownMenuItem,
|
|
9
|
+
DropdownMenuRadioGroup,
|
|
10
|
+
DropdownMenuRadioItem,
|
|
11
|
+
DropdownMenuSeparator,
|
|
12
|
+
DropdownMenuSub,
|
|
13
|
+
DropdownMenuSubContent,
|
|
14
|
+
DropdownMenuSubTrigger
|
|
15
|
+
} from "./dropdown-menu.js";
|
|
16
|
+
import { Switch } from "./switch.js";
|
|
17
|
+
import { UiIcon } from "./ui-icons.js";
|
|
18
|
+
function ProfileTabs({
|
|
19
|
+
tabs,
|
|
20
|
+
value,
|
|
21
|
+
onChange,
|
|
22
|
+
label = "Sections"
|
|
23
|
+
}) {
|
|
24
|
+
const move = (e, index) => {
|
|
25
|
+
const step = e.key === "ArrowRight" ? 1 : e.key === "ArrowLeft" ? -1 : 0;
|
|
26
|
+
if (step === 0) return;
|
|
27
|
+
e.stopPropagation();
|
|
28
|
+
e.preventDefault();
|
|
29
|
+
const target = (index + step + tabs.length) % tabs.length;
|
|
30
|
+
const next = tabs[target];
|
|
31
|
+
if (!next) return;
|
|
32
|
+
onChange(next.id);
|
|
33
|
+
e.currentTarget.parentElement?.querySelectorAll('[role="menuitemradio"]')[target]?.focus();
|
|
34
|
+
};
|
|
35
|
+
return /* @__PURE__ */ jsx("div", { role: "group", "aria-label": label, "data-slot": "account-menu-tabs", className: "flex border-b border-border-subtle", children: tabs.map((t, i) => {
|
|
36
|
+
const selected = t.id === value;
|
|
37
|
+
return /* @__PURE__ */ jsx(
|
|
38
|
+
"button",
|
|
39
|
+
{
|
|
40
|
+
type: "button",
|
|
41
|
+
role: "menuitemradio",
|
|
42
|
+
"aria-checked": selected,
|
|
43
|
+
tabIndex: -1,
|
|
44
|
+
onClick: () => onChange(t.id),
|
|
45
|
+
onKeyDown: (e) => move(e, i),
|
|
46
|
+
className: cn(
|
|
47
|
+
"-mb-px h-8 flex-1 border-b px-3 text-body font-medium outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
48
|
+
selected ? "border-primary text-ink" : "border-transparent text-ink-mid hover:text-ink"
|
|
49
|
+
),
|
|
50
|
+
children: t.label
|
|
51
|
+
},
|
|
52
|
+
t.id
|
|
53
|
+
);
|
|
54
|
+
}) });
|
|
55
|
+
}
|
|
56
|
+
function ProfileCard({
|
|
57
|
+
name,
|
|
58
|
+
plan,
|
|
59
|
+
initials,
|
|
60
|
+
onOpen
|
|
61
|
+
}) {
|
|
62
|
+
const content = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
63
|
+
/* @__PURE__ */ jsx(Avatar, { className: "size-14 shrink-0 rounded-full", children: /* @__PURE__ */ jsx(AvatarFallback, { className: "font-serif text-h2 font-light text-ink", children: initials.slice(0, 1) }) }),
|
|
64
|
+
/* @__PURE__ */ jsxs("span", { className: "flex min-w-0 flex-1 flex-col gap-0.5", children: [
|
|
65
|
+
/* @__PURE__ */ jsx("span", { className: "truncate text-body font-medium text-ink", children: name }),
|
|
66
|
+
plan && /* @__PURE__ */ jsx("span", { className: "text-caption text-ink-soft", children: plan })
|
|
67
|
+
] }),
|
|
68
|
+
onOpen && /* @__PURE__ */ jsx(UiIcon, { name: "chevron-right", className: "size-4 text-ink-soft" })
|
|
69
|
+
] });
|
|
70
|
+
if (!onOpen) {
|
|
71
|
+
return /* @__PURE__ */ jsx("div", { "data-slot": "account-menu-card", className: "flex w-full items-center gap-2.5 p-2", children: content });
|
|
72
|
+
}
|
|
73
|
+
return /* @__PURE__ */ jsx(DropdownMenuItem, { "data-slot": "account-menu-card", className: "h-auto gap-2.5 p-2", onClick: onOpen, children: content });
|
|
74
|
+
}
|
|
75
|
+
function ProfileSettingRows({ profile }) {
|
|
76
|
+
const { language, darkTheme, colorTheme, icons } = profile;
|
|
77
|
+
if (!language && !darkTheme && !colorTheme) return null;
|
|
78
|
+
const currentColor = colorTheme?.options.find((o) => o.id === colorTheme.value)?.label;
|
|
79
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
80
|
+
/* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
|
|
81
|
+
language && /* @__PURE__ */ jsxs(DropdownMenuItem, { onClick: () => language.onSelect?.(), children: [
|
|
82
|
+
icons?.language,
|
|
83
|
+
/* @__PURE__ */ jsx("span", { className: "flex-1", children: language.label }),
|
|
84
|
+
/* @__PURE__ */ jsx(Badge, { children: language.value })
|
|
85
|
+
] }),
|
|
86
|
+
darkTheme && /* @__PURE__ */ jsxs(
|
|
87
|
+
DropdownMenuItem,
|
|
88
|
+
{
|
|
89
|
+
role: "menuitemcheckbox",
|
|
90
|
+
"aria-checked": darkTheme.checked,
|
|
91
|
+
onClick: () => darkTheme.onCheckedChange(!darkTheme.checked),
|
|
92
|
+
children: [
|
|
93
|
+
icons?.darkTheme,
|
|
94
|
+
/* @__PURE__ */ jsx("span", { className: "flex-1", children: darkTheme.label }),
|
|
95
|
+
/* @__PURE__ */ jsx(Switch, { checked: darkTheme.checked, tabIndex: -1, "aria-hidden": true, className: "pointer-events-none" })
|
|
96
|
+
]
|
|
97
|
+
}
|
|
98
|
+
),
|
|
99
|
+
colorTheme && /* @__PURE__ */ jsxs(DropdownMenuSub, { children: [
|
|
100
|
+
/* @__PURE__ */ jsxs(DropdownMenuSubTrigger, { children: [
|
|
101
|
+
icons?.colorTheme,
|
|
102
|
+
/* @__PURE__ */ jsx("span", { className: "flex-1", children: colorTheme.label }),
|
|
103
|
+
currentColor && /* @__PURE__ */ jsx("span", { className: "text-body-s text-ink-soft", children: currentColor })
|
|
104
|
+
] }),
|
|
105
|
+
/* @__PURE__ */ jsx(DropdownMenuSubContent, { children: /* @__PURE__ */ jsx(DropdownMenuRadioGroup, { value: colorTheme.value, onValueChange: colorTheme.onValueChange, children: colorTheme.options.map((o) => /* @__PURE__ */ jsx(DropdownMenuRadioItem, { value: o.id, children: o.label }, o.id)) }) })
|
|
106
|
+
] })
|
|
107
|
+
] });
|
|
108
|
+
}
|
|
109
|
+
export {
|
|
110
|
+
ProfileCard,
|
|
111
|
+
ProfileSettingRows,
|
|
112
|
+
ProfileTabs
|
|
113
|
+
};
|
|
114
|
+
//# sourceMappingURL=account-menu-profile.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/components/account-menu-profile.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../lib/cn\";\nimport { Avatar, AvatarFallback } from \"./avatar\";\nimport { Badge } from \"./badge\";\nimport {\n DropdownMenuItem,\n DropdownMenuRadioGroup,\n DropdownMenuRadioItem,\n DropdownMenuSeparator,\n DropdownMenuSub,\n DropdownMenuSubContent,\n DropdownMenuSubTrigger,\n} from \"./dropdown-menu\";\nimport { Switch } from \"./switch\";\nimport { UiIcon } from \"./ui-icons\";\n\nexport interface AccountMenuTab {\n id: string;\n label: React.ReactNode;\n /** Shown instead of the menu body when this tab is current; the first tab is always the menu itself. */\n content?: React.ReactNode;\n}\n\nexport interface AccountMenuProfile {\n /** Under the name on the profile card, e.g. \"Free\". */\n plan?: string;\n /** Makes the card a menu item that runs this and closes the menu, e.g. to open the account settings. */\n onOpenProfile?: () => void;\n tabs?: readonly AccountMenuTab[];\n /** Names the tab row for assistive tech. Default `\"Sections\"`. */\n tabsLabel?: string;\n /** Current tab id (controlled). */\n tab?: string;\n onTabChange?: (id: string) => void;\n /** A row with a short value badge on the right (\"EN\"); picking it runs `onSelect`. */\n language?: { label: string; value: string; onSelect?: () => void };\n /** A row with a switch; the menu closes after a toggle. */\n darkTheme?: { label: string; checked: boolean; onCheckedChange: (checked: boolean) => void };\n /** A submenu of radio options; the current one's label shows on the row. */\n colorTheme?: {\n label: string;\n value: string;\n options: readonly { id: string; label: string }[];\n onValueChange: (id: string) => void;\n };\n /** Glyphs for the rows, so the kit carries no icon set. */\n icons?: { language?: React.ReactNode; darkTheme?: React.ReactNode; colorTheme?: React.ReactNode };\n}\n\n/**\n * The tabs are `menuitemradio` items in a group, not a tablist: a menu may only\n * hold items, and this way the menu's own arrow-key roving reaches them.\n * Selecting one does not close the menu.\n */\nexport function ProfileTabs({\n tabs,\n value,\n onChange,\n label = \"Sections\",\n}: {\n tabs: readonly AccountMenuTab[];\n value: string;\n onChange: (id: string) => void;\n label?: string | undefined;\n}) {\n const move = (e: React.KeyboardEvent<HTMLButtonElement>, index: number) => {\n const step = e.key === \"ArrowRight\" ? 1 : e.key === \"ArrowLeft\" ? -1 : 0;\n if (step === 0) return;\n // Up and Down belong to the menu; Left and Right stay in the row.\n e.stopPropagation();\n e.preventDefault();\n const target = (index + step + tabs.length) % tabs.length;\n const next = tabs[target];\n if (!next) return;\n onChange(next.id);\n e.currentTarget.parentElement?.querySelectorAll<HTMLElement>('[role=\"menuitemradio\"]')[target]?.focus();\n };\n return (\n <div role=\"group\" aria-label={label} data-slot=\"account-menu-tabs\" className=\"flex border-b border-border-subtle\">\n {tabs.map((t, i) => {\n const selected = t.id === value;\n return (\n <button\n key={t.id}\n type=\"button\"\n role=\"menuitemradio\"\n aria-checked={selected}\n tabIndex={-1}\n onClick={() => onChange(t.id)}\n onKeyDown={(e) => move(e, i)}\n className={cn(\n \"-mb-px h-8 flex-1 border-b px-3 text-body font-medium outline-none focus-visible:ring-2 focus-visible:ring-ring\",\n selected ? \"border-primary text-ink\" : \"border-transparent text-ink-mid hover:text-ink\",\n )}\n >\n {t.label}\n </button>\n );\n })}\n </div>\n );\n}\n\nexport function ProfileCard({\n name,\n plan,\n initials,\n onOpen,\n}: {\n name: string;\n plan?: string | undefined;\n initials: string;\n onOpen?: (() => void) | undefined;\n}) {\n const content = (\n <>\n <Avatar className=\"size-14 shrink-0 rounded-full\">\n <AvatarFallback className=\"font-serif text-h2 font-light text-ink\">{initials.slice(0, 1)}</AvatarFallback>\n </Avatar>\n <span className=\"flex min-w-0 flex-1 flex-col gap-0.5\">\n <span className=\"truncate text-body font-medium text-ink\">{name}</span>\n {plan && <span className=\"text-caption text-ink-soft\">{plan}</span>}\n </span>\n {onOpen && <UiIcon name=\"chevron-right\" className=\"size-4 text-ink-soft\" />}\n </>\n );\n if (!onOpen) {\n return (\n <div data-slot=\"account-menu-card\" className=\"flex w-full items-center gap-2.5 p-2\">\n {content}\n </div>\n );\n }\n // A real menu item: it joins the arrow-key order and closes the menu, so a dialog opened from it is not left under the menu.\n return (\n <DropdownMenuItem data-slot=\"account-menu-card\" className=\"h-auto gap-2.5 p-2\" onClick={onOpen}>\n {content}\n </DropdownMenuItem>\n );\n}\n\nexport function ProfileSettingRows({ profile }: { profile: AccountMenuProfile }) {\n const { language, darkTheme, colorTheme, icons } = profile;\n if (!language && !darkTheme && !colorTheme) return null;\n const currentColor = colorTheme?.options.find((o) => o.id === colorTheme.value)?.label;\n return (\n <>\n <DropdownMenuSeparator />\n {language && (\n <DropdownMenuItem onClick={() => language.onSelect?.()}>\n {icons?.language}\n <span className=\"flex-1\">{language.label}</span>\n <Badge>{language.value}</Badge>\n </DropdownMenuItem>\n )}\n {darkTheme && (\n <DropdownMenuItem\n role=\"menuitemcheckbox\"\n aria-checked={darkTheme.checked}\n onClick={() => darkTheme.onCheckedChange(!darkTheme.checked)}\n >\n {icons?.darkTheme}\n <span className=\"flex-1\">{darkTheme.label}</span>\n {/* Presentational: the row is the control, so the switch stays out of the tab order. */}\n <Switch checked={darkTheme.checked} tabIndex={-1} aria-hidden className=\"pointer-events-none\" />\n </DropdownMenuItem>\n )}\n {colorTheme && (\n <DropdownMenuSub>\n <DropdownMenuSubTrigger>\n {icons?.colorTheme}\n <span className=\"flex-1\">{colorTheme.label}</span>\n {currentColor && <span className=\"text-body-s text-ink-soft\">{currentColor}</span>}\n </DropdownMenuSubTrigger>\n <DropdownMenuSubContent>\n <DropdownMenuRadioGroup value={colorTheme.value} onValueChange={colorTheme.onValueChange}>\n {colorTheme.options.map((o) => (\n <DropdownMenuRadioItem key={o.id} value={o.id}>\n {o.label}\n </DropdownMenuRadioItem>\n ))}\n </DropdownMenuRadioGroup>\n </DropdownMenuSubContent>\n </DropdownMenuSub>\n )}\n </>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { cn } from "../lib/cn.js";
|
|
5
5
|
import { Avatar, AvatarFallback } from "./avatar.js";
|
|
@@ -12,6 +12,12 @@ import {
|
|
|
12
12
|
DropdownMenuSeparator,
|
|
13
13
|
DropdownMenuTrigger
|
|
14
14
|
} from "./dropdown-menu.js";
|
|
15
|
+
import { useControllableState } from "../primitives/use-controllable-state.js";
|
|
16
|
+
import {
|
|
17
|
+
ProfileCard,
|
|
18
|
+
ProfileSettingRows,
|
|
19
|
+
ProfileTabs
|
|
20
|
+
} from "./account-menu-profile.js";
|
|
15
21
|
const initials = (account) => {
|
|
16
22
|
const words = account.name?.trim().split(/\s+/).filter(Boolean) ?? [];
|
|
17
23
|
const letters = words.length > 0 ? words.slice(0, 2).map((w) => Array.from(w)[0]) : [Array.from(account.email)[0]];
|
|
@@ -25,9 +31,17 @@ function AccountMenu({
|
|
|
25
31
|
onSignOut,
|
|
26
32
|
linkComponent,
|
|
27
33
|
labels = {},
|
|
34
|
+
profile,
|
|
28
35
|
className
|
|
29
36
|
}) {
|
|
30
37
|
const Link = linkComponent ?? "a";
|
|
38
|
+
const tabs = profile?.tabs ?? [];
|
|
39
|
+
const [tab, setTab] = useControllableState({
|
|
40
|
+
...profile?.tab !== void 0 ? { value: profile.tab } : {},
|
|
41
|
+
defaultValue: tabs[0]?.id ?? "",
|
|
42
|
+
...profile?.onTabChange ? { onChange: profile.onTabChange } : {}
|
|
43
|
+
});
|
|
44
|
+
const otherTab = tabs.length > 0 && tab !== tabs[0]?.id ? tabs.find((t) => t.id === tab) : void 0;
|
|
31
45
|
return /* @__PURE__ */ jsxs(DropdownMenu, { children: [
|
|
32
46
|
/* @__PURE__ */ jsx(
|
|
33
47
|
DropdownMenuTrigger,
|
|
@@ -38,29 +52,42 @@ function AccountMenu({
|
|
|
38
52
|
children: /* @__PURE__ */ jsx(Avatar, { children: /* @__PURE__ */ jsx(AvatarFallback, { className: "text-xs font-medium text-ink", children: initials(account) }) })
|
|
39
53
|
}
|
|
40
54
|
),
|
|
41
|
-
/* @__PURE__ */ jsxs(DropdownMenuContent, { align: "end", className: "w-64", children: [
|
|
42
|
-
/* @__PURE__ */
|
|
43
|
-
|
|
44
|
-
/* @__PURE__ */ jsx(
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
55
|
+
/* @__PURE__ */ jsxs(DropdownMenuContent, { align: "end", className: profile ? "w-75" : "w-64", children: [
|
|
56
|
+
tabs.length > 0 && /* @__PURE__ */ jsx(ProfileTabs, { tabs, value: tab, onChange: setTab, label: profile?.tabsLabel }),
|
|
57
|
+
otherTab ? otherTab.content : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
58
|
+
profile ? /* @__PURE__ */ jsx(
|
|
59
|
+
ProfileCard,
|
|
60
|
+
{
|
|
61
|
+
name: account.name || account.email,
|
|
62
|
+
plan: profile.plan,
|
|
63
|
+
initials: initials(account),
|
|
64
|
+
onOpen: profile.onOpenProfile
|
|
65
|
+
}
|
|
66
|
+
) : /* @__PURE__ */ jsxs(DropdownMenuLabel, { className: "flex flex-col", children: [
|
|
67
|
+
account.name && /* @__PURE__ */ jsx("span", { className: "truncate text-ink", children: account.name }),
|
|
68
|
+
/* @__PURE__ */ jsx("span", { className: "truncate font-normal text-ink-soft", children: account.email })
|
|
69
|
+
] }),
|
|
70
|
+
profile && /* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
|
|
71
|
+
manageHref && /* @__PURE__ */ jsx(DropdownMenuItem, { asChild: true, children: /* @__PURE__ */ jsx("a", { href: manageHref, children: labels.manage ?? "Manage account" }) }),
|
|
72
|
+
groups.map((group) => /* @__PURE__ */ jsxs(React.Fragment, { children: [
|
|
73
|
+
/* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
|
|
74
|
+
/* @__PURE__ */ jsxs(DropdownMenuGroup, { children: [
|
|
75
|
+
group.label && /* @__PURE__ */ jsx(DropdownMenuLabel, { className: "text-xs text-ink-soft", children: group.label }),
|
|
76
|
+
group.items.map((item) => {
|
|
77
|
+
const Item = item.external ? "a" : Link;
|
|
78
|
+
const Icon = item.icon;
|
|
79
|
+
return /* @__PURE__ */ jsx(DropdownMenuItem, { asChild: true, children: /* @__PURE__ */ jsxs(Item, { href: item.href, children: [
|
|
80
|
+
Icon && /* @__PURE__ */ jsx(Icon, { className: "size-4" }),
|
|
81
|
+
item.label
|
|
82
|
+
] }) }, item.id);
|
|
83
|
+
})
|
|
84
|
+
] })
|
|
85
|
+
] }, group.id)),
|
|
86
|
+
profile && /* @__PURE__ */ jsx(ProfileSettingRows, { profile }),
|
|
48
87
|
/* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
|
|
49
|
-
/* @__PURE__ */
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
const Item = item.external ? "a" : Link;
|
|
53
|
-
const Icon = item.icon;
|
|
54
|
-
return /* @__PURE__ */ jsx(DropdownMenuItem, { asChild: true, children: /* @__PURE__ */ jsxs(Item, { href: item.href, children: [
|
|
55
|
-
Icon && /* @__PURE__ */ jsx(Icon, { className: "size-4" }),
|
|
56
|
-
item.label
|
|
57
|
-
] }) }, item.id);
|
|
58
|
-
})
|
|
59
|
-
] })
|
|
60
|
-
] }, group.id)),
|
|
61
|
-
/* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
|
|
62
|
-
/* @__PURE__ */ jsx(DropdownMenuItem, { asChild: true, children: /* @__PURE__ */ jsx("a", { href: switchHref, children: labels.switchAccount ?? "Switch account" }) }),
|
|
63
|
-
/* @__PURE__ */ jsx(DropdownMenuItem, { onClick: onSignOut, children: labels.signOut ?? "Sign out" })
|
|
88
|
+
/* @__PURE__ */ jsx(DropdownMenuItem, { asChild: true, children: /* @__PURE__ */ jsx("a", { href: switchHref, children: labels.switchAccount ?? "Switch account" }) }),
|
|
89
|
+
/* @__PURE__ */ jsx(DropdownMenuItem, { variant: profile ? "destructive" : "neutral", onClick: onSignOut, children: labels.signOut ?? "Sign out" })
|
|
90
|
+
] })
|
|
64
91
|
] })
|
|
65
92
|
] });
|
|
66
93
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/account-menu.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../lib/cn\";\nimport { Avatar, AvatarFallback } from \"./avatar\";\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuGroup,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n} from \"./dropdown-menu\";\nimport type { NavGroup } from \"./nav-model\";\n\nexport interface AccountMenuLabels {\n /** Accessible name of the trigger. Default `\"Account\"`. */\n trigger?: string;\n /** Default `\"Manage account\"`. */\n manage?: string;\n /** Default `\"Switch account\"`. */\n switchAccount?: string;\n /** Default `\"Sign out\"`. */\n signOut?: string;\n}\n\nexport interface AccountMenuProps {\n account: { name?: string | null | undefined; email: string };\n /** The account center, on the identity provider's origin; no item without it. */\n manageHref?: string | undefined;\n /** A full page load to the sign-in that offers the account chooser. */\n switchHref: string;\n /** The service's own entries, between the account center and switching. Draws `label`, `icon`, `external`. */\n groups?: readonly NavGroup[];\n onSignOut: () => void;\n linkComponent?: React.ElementType;\n labels?: AccountMenuLabels;\n className?: string;\n}\n\nconst initials = (account: AccountMenuProps[\"account\"]) => {\n const words = account.name?.trim().split(/\\s+/).filter(Boolean) ?? [];\n const letters = words.length > 0 ? words.slice(0, 2).map(w => Array.from(w)[0]) : [Array.from(account.email)[0]];\n return letters.join(\"\").toUpperCase();\n};\n\n/**\n * Who is signed in, top right. The order is fixed — the account, the account\n * center, the service's groups, switching, signing out — so every service's\n * menu reads the same; a service adds groups and nothing else.\n */\nexport function AccountMenu({\n account,\n manageHref,\n switchHref,\n groups = [],\n onSignOut,\n linkComponent,\n labels = {},\n className,\n}: AccountMenuProps) {\n const Link = linkComponent ?? \"a\";\n return (\n <DropdownMenu>\n <DropdownMenuTrigger\n data-slot=\"account-menu-trigger\"\n aria-label={labels.trigger ?? \"Account\"}\n className={cn(\"rounded-full outline-none focus-visible:ring-2 focus-visible:ring-ring\", className)}\n >\n <Avatar>\n <AvatarFallback className=\"text-xs font-medium text-ink\">{initials(account)}</AvatarFallback>\n </Avatar>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\" className
|
|
1
|
+
{"version":3,"sources":["../../src/components/account-menu.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../lib/cn\";\nimport { Avatar, AvatarFallback } from \"./avatar\";\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuGroup,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n} from \"./dropdown-menu\";\nimport type { NavGroup } from \"./nav-model\";\nimport { useControllableState } from \"../primitives/use-controllable-state\";\nimport {\n ProfileCard,\n ProfileSettingRows,\n ProfileTabs,\n type AccountMenuProfile,\n type AccountMenuTab,\n} from \"./account-menu-profile\";\n\nexport type { AccountMenuProfile, AccountMenuTab };\n\nexport interface AccountMenuLabels {\n /** Accessible name of the trigger. Default `\"Account\"`. */\n trigger?: string;\n /** Default `\"Manage account\"`. */\n manage?: string;\n /** Default `\"Switch account\"`. */\n switchAccount?: string;\n /** Default `\"Sign out\"`. */\n signOut?: string;\n}\n\nexport interface AccountMenuProps {\n account: { name?: string | null | undefined; email: string };\n /** The account center, on the identity provider's origin; no item without it. */\n manageHref?: string | undefined;\n /** A full page load to the sign-in that offers the account chooser. */\n switchHref: string;\n /** The service's own entries, between the account center and switching. Draws `label`, `icon`, `external`. */\n groups?: readonly NavGroup[];\n onSignOut: () => void;\n linkComponent?: React.ElementType;\n labels?: AccountMenuLabels;\n /**\n * The wide profile form: tabs, a profile card, language and theme rows. Absent,\n * the menu is the compact one it always was.\n */\n profile?: AccountMenuProfile;\n className?: string;\n}\n\nconst initials = (account: AccountMenuProps[\"account\"]) => {\n const words = account.name?.trim().split(/\\s+/).filter(Boolean) ?? [];\n const letters = words.length > 0 ? words.slice(0, 2).map(w => Array.from(w)[0]) : [Array.from(account.email)[0]];\n return letters.join(\"\").toUpperCase();\n};\n\n/**\n * Who is signed in, top right. The order is fixed — the account, the account\n * center, the service's groups, switching, signing out — so every service's\n * menu reads the same; a service adds groups and nothing else.\n */\nexport function AccountMenu({\n account,\n manageHref,\n switchHref,\n groups = [],\n onSignOut,\n linkComponent,\n labels = {},\n profile,\n className,\n}: AccountMenuProps) {\n const Link = linkComponent ?? \"a\";\n const tabs = profile?.tabs ?? [];\n const [tab, setTab] = useControllableState<string>({\n ...(profile?.tab !== undefined ? { value: profile.tab } : {}),\n defaultValue: tabs[0]?.id ?? \"\",\n ...(profile?.onTabChange ? { onChange: profile.onTabChange } : {}),\n });\n const otherTab = tabs.length > 0 && tab !== tabs[0]?.id ? tabs.find((t) => t.id === tab) : undefined;\n return (\n <DropdownMenu>\n <DropdownMenuTrigger\n data-slot=\"account-menu-trigger\"\n aria-label={labels.trigger ?? \"Account\"}\n className={cn(\"rounded-full outline-none focus-visible:ring-2 focus-visible:ring-ring\", className)}\n >\n <Avatar>\n <AvatarFallback className=\"text-xs font-medium text-ink\">{initials(account)}</AvatarFallback>\n </Avatar>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\" className={profile ? \"w-75\" : \"w-64\"}>\n {tabs.length > 0 && <ProfileTabs tabs={tabs} value={tab} onChange={setTab} label={profile?.tabsLabel} />}\n {otherTab ? (\n otherTab.content\n ) : (\n <>\n {profile ? (\n <ProfileCard\n name={account.name || account.email}\n plan={profile.plan}\n initials={initials(account)}\n onOpen={profile.onOpenProfile}\n />\n ) : (\n <DropdownMenuLabel className=\"flex flex-col\">\n {account.name && <span className=\"truncate text-ink\">{account.name}</span>}\n <span className=\"truncate font-normal text-ink-soft\">{account.email}</span>\n </DropdownMenuLabel>\n )}\n {profile && <DropdownMenuSeparator />}\n {manageHref && (\n <DropdownMenuItem asChild>\n <a href={manageHref}>{labels.manage ?? \"Manage account\"}</a>\n </DropdownMenuItem>\n )}\n {groups.map(group => (\n <React.Fragment key={group.id}>\n <DropdownMenuSeparator />\n <DropdownMenuGroup>\n {group.label && <DropdownMenuLabel className=\"text-xs text-ink-soft\">{group.label}</DropdownMenuLabel>}\n {group.items.map(item => {\n const Item = item.external ? \"a\" : Link;\n const Icon = item.icon;\n return (\n <DropdownMenuItem key={item.id} asChild>\n <Item href={item.href}>\n {Icon && <Icon className=\"size-4\" />}\n {item.label}\n </Item>\n </DropdownMenuItem>\n );\n })}\n </DropdownMenuGroup>\n </React.Fragment>\n ))}\n {profile && <ProfileSettingRows profile={profile} />}\n <DropdownMenuSeparator />\n <DropdownMenuItem asChild>\n <a href={switchHref}>{labels.switchAccount ?? \"Switch account\"}</a>\n </DropdownMenuItem>\n <DropdownMenuItem variant={profile ? \"destructive\" : \"neutral\"} onClick={onSignOut}>\n {labels.signOut ?? \"Sign out\"}\n </DropdownMenuItem>\n </>\n )}\n </DropdownMenuContent>\n </DropdownMenu>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import {
|
|
4
|
+
ACCOUNT_SETTINGS_DIALOG_CLOSE,
|
|
5
|
+
ACCOUNT_SETTINGS_DIALOG_CONTENT,
|
|
6
|
+
ACCOUNT_SETTINGS_DIALOG_DESCRIPTION,
|
|
7
|
+
ACCOUNT_SETTINGS_DIALOG_ENTRY,
|
|
8
|
+
ACCOUNT_SETTINGS_DIALOG_FOOTER,
|
|
9
|
+
ACCOUNT_SETTINGS_DIALOG_HEADING,
|
|
10
|
+
ACCOUNT_SETTINGS_DIALOG_LIST,
|
|
11
|
+
ACCOUNT_SETTINGS_DIALOG_PANE,
|
|
12
|
+
ACCOUNT_SETTINGS_DIALOG_PANEL,
|
|
13
|
+
ACCOUNT_SETTINGS_DIALOG_SIDEBAR,
|
|
14
|
+
ACCOUNT_SETTINGS_DIALOG_TABS,
|
|
15
|
+
ACCOUNT_SETTINGS_DIALOG_TITLE
|
|
16
|
+
} from "../generated/account-settings-dialog.js";
|
|
17
|
+
import { cn } from "../lib/cn.js";
|
|
18
|
+
import { Dialog, DialogClose, DialogContent, DialogDescription, DialogTitle } from "./dialog.js";
|
|
19
|
+
import { Tabs, TabsContent, TabsList, TabsTrigger } from "./tabs.js";
|
|
20
|
+
import { IconButton } from "./icon-button.js";
|
|
21
|
+
import { UiIcon } from "./ui-icons.js";
|
|
22
|
+
function AccountSettingsDialog({
|
|
23
|
+
open,
|
|
24
|
+
defaultOpen,
|
|
25
|
+
onOpenChange,
|
|
26
|
+
title,
|
|
27
|
+
description,
|
|
28
|
+
sections,
|
|
29
|
+
value,
|
|
30
|
+
defaultValue,
|
|
31
|
+
onValueChange,
|
|
32
|
+
sidebarFooter,
|
|
33
|
+
labels = {},
|
|
34
|
+
className
|
|
35
|
+
}) {
|
|
36
|
+
const first = sections[0]?.id ?? "";
|
|
37
|
+
return /* @__PURE__ */ jsx(
|
|
38
|
+
Dialog,
|
|
39
|
+
{
|
|
40
|
+
...open !== void 0 ? { open } : {},
|
|
41
|
+
...defaultOpen !== void 0 ? { defaultOpen } : {},
|
|
42
|
+
...onOpenChange ? { onOpenChange } : {},
|
|
43
|
+
children: /* @__PURE__ */ jsxs(
|
|
44
|
+
DialogContent,
|
|
45
|
+
{
|
|
46
|
+
showCloseButton: false,
|
|
47
|
+
className: cn(ACCOUNT_SETTINGS_DIALOG_CONTENT, className),
|
|
48
|
+
children: [
|
|
49
|
+
/* @__PURE__ */ jsxs(
|
|
50
|
+
Tabs,
|
|
51
|
+
{
|
|
52
|
+
orientation: "vertical",
|
|
53
|
+
...value !== void 0 ? { value } : { defaultValue: defaultValue ?? first },
|
|
54
|
+
...onValueChange ? { onValueChange } : {},
|
|
55
|
+
className: ACCOUNT_SETTINGS_DIALOG_TABS,
|
|
56
|
+
children: [
|
|
57
|
+
/* @__PURE__ */ jsxs("div", { className: ACCOUNT_SETTINGS_DIALOG_SIDEBAR, children: [
|
|
58
|
+
/* @__PURE__ */ jsx(TabsList, { className: ACCOUNT_SETTINGS_DIALOG_LIST, children: sections.map((s) => /* @__PURE__ */ jsxs(TabsTrigger, { value: s.id, className: ACCOUNT_SETTINGS_DIALOG_ENTRY, children: [
|
|
59
|
+
s.icon,
|
|
60
|
+
s.label
|
|
61
|
+
] }, s.id)) }),
|
|
62
|
+
sidebarFooter != null && /* @__PURE__ */ jsx("div", { className: ACCOUNT_SETTINGS_DIALOG_FOOTER, children: sidebarFooter })
|
|
63
|
+
] }),
|
|
64
|
+
/* @__PURE__ */ jsxs("div", { className: ACCOUNT_SETTINGS_DIALOG_PANE, children: [
|
|
65
|
+
/* @__PURE__ */ jsxs("div", { className: ACCOUNT_SETTINGS_DIALOG_HEADING, children: [
|
|
66
|
+
/* @__PURE__ */ jsx(DialogTitle, { className: ACCOUNT_SETTINGS_DIALOG_TITLE, children: title }),
|
|
67
|
+
description != null && /* @__PURE__ */ jsx(DialogDescription, { className: ACCOUNT_SETTINGS_DIALOG_DESCRIPTION, children: description })
|
|
68
|
+
] }),
|
|
69
|
+
sections.map((s) => /* @__PURE__ */ jsx(TabsContent, { value: s.id, className: ACCOUNT_SETTINGS_DIALOG_PANEL, children: s.content }, s.id))
|
|
70
|
+
] })
|
|
71
|
+
]
|
|
72
|
+
}
|
|
73
|
+
),
|
|
74
|
+
/* @__PURE__ */ jsx(DialogClose, { asChild: true, children: /* @__PURE__ */ jsx(IconButton, { "aria-label": labels.close ?? "Close", className: ACCOUNT_SETTINGS_DIALOG_CLOSE, children: /* @__PURE__ */ jsx(UiIcon, { name: "x" }) }) })
|
|
75
|
+
]
|
|
76
|
+
}
|
|
77
|
+
)
|
|
78
|
+
}
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
export {
|
|
82
|
+
AccountSettingsDialog
|
|
83
|
+
};
|
|
84
|
+
//# sourceMappingURL=account-settings-dialog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/components/account-settings-dialog.tsx"],"sourcesContent":["import * as React from \"react\";\nimport {\n ACCOUNT_SETTINGS_DIALOG_CLOSE,\n ACCOUNT_SETTINGS_DIALOG_CONTENT,\n ACCOUNT_SETTINGS_DIALOG_DESCRIPTION,\n ACCOUNT_SETTINGS_DIALOG_ENTRY,\n ACCOUNT_SETTINGS_DIALOG_FOOTER,\n ACCOUNT_SETTINGS_DIALOG_HEADING,\n ACCOUNT_SETTINGS_DIALOG_LIST,\n ACCOUNT_SETTINGS_DIALOG_PANE,\n ACCOUNT_SETTINGS_DIALOG_PANEL,\n ACCOUNT_SETTINGS_DIALOG_SIDEBAR,\n ACCOUNT_SETTINGS_DIALOG_TABS,\n ACCOUNT_SETTINGS_DIALOG_TITLE,\n} from \"../generated/account-settings-dialog\";\nimport { cn } from \"../lib/cn\";\nimport { Dialog, DialogClose, DialogContent, DialogDescription, DialogTitle } from \"./dialog\";\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from \"./tabs\";\nimport { IconButton } from \"./icon-button\";\nimport { UiIcon } from \"./ui-icons\";\n\nexport interface AccountSettingsSection {\n id: string;\n label: React.ReactNode;\n icon?: React.ReactNode;\n content: React.ReactNode;\n}\n\nexport interface AccountSettingsDialogLabels {\n /** The close button. Default `\"Close\"`. */\n close?: string;\n}\n\nexport interface AccountSettingsDialogProps {\n open?: boolean;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean) => void;\n /** The heading of the content pane. */\n title: React.ReactNode;\n description?: React.ReactNode;\n /** One sidebar entry each; the active one's `content` fills the pane. */\n sections: readonly AccountSettingsSection[];\n value?: string;\n defaultValue?: string;\n onValueChange?: (id: string) => void;\n /** Pinned under the sidebar entries: sign out, language. */\n sidebarFooter?: React.ReactNode;\n labels?: AccountSettingsDialogLabels;\n className?: string;\n}\n\n/**\n * The account modal: a vertical tab rail on the left, the active section on\n * the right. Sections are the caller's, so Profile and Security (or any other)\n * are built from `SettingsSection` and `SettingsRow` inside `content`.\n */\nexport function AccountSettingsDialog({\n open,\n defaultOpen,\n onOpenChange,\n title,\n description,\n sections,\n value,\n defaultValue,\n onValueChange,\n sidebarFooter,\n labels = {},\n className,\n}: AccountSettingsDialogProps) {\n const first = sections[0]?.id ?? \"\";\n return (\n <Dialog\n {...(open !== undefined ? { open } : {})}\n {...(defaultOpen !== undefined ? { defaultOpen } : {})}\n {...(onOpenChange ? { onOpenChange } : {})}\n >\n <DialogContent\n showCloseButton={false}\n className={cn(ACCOUNT_SETTINGS_DIALOG_CONTENT, className)}\n >\n <Tabs\n orientation=\"vertical\"\n {...(value !== undefined ? { value } : { defaultValue: defaultValue ?? first })}\n {...(onValueChange ? { onValueChange } : {})}\n className={ACCOUNT_SETTINGS_DIALOG_TABS}\n >\n <div className={ACCOUNT_SETTINGS_DIALOG_SIDEBAR}>\n <TabsList className={ACCOUNT_SETTINGS_DIALOG_LIST}>\n {sections.map((s) => (\n <TabsTrigger key={s.id} value={s.id} className={ACCOUNT_SETTINGS_DIALOG_ENTRY}>\n {s.icon}\n {s.label}\n </TabsTrigger>\n ))}\n </TabsList>\n {sidebarFooter != null && (\n <div className={ACCOUNT_SETTINGS_DIALOG_FOOTER}>{sidebarFooter}</div>\n )}\n </div>\n <div className={ACCOUNT_SETTINGS_DIALOG_PANE}>\n <div className={ACCOUNT_SETTINGS_DIALOG_HEADING}>\n <DialogTitle className={ACCOUNT_SETTINGS_DIALOG_TITLE}>{title}</DialogTitle>\n {description != null && (\n <DialogDescription className={ACCOUNT_SETTINGS_DIALOG_DESCRIPTION}>{description}</DialogDescription>\n )}\n </div>\n {sections.map((s) => (\n <TabsContent key={s.id} value={s.id} className={ACCOUNT_SETTINGS_DIALOG_PANEL}>\n {s.content}\n </TabsContent>\n ))}\n </div>\n </Tabs>\n <DialogClose asChild>\n <IconButton aria-label={labels.close ?? \"Close\"} className={ACCOUNT_SETTINGS_DIALOG_CLOSE}>\n <UiIcon name=\"x\" />\n </IconButton>\n </DialogClose>\n </DialogContent>\n </Dialog>\n );\n}\n"],"mappings":"AAwFY,cAEI,YAFJ;AAxFZ,YAAY,WAAW;AACvB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,UAAU;AACnB,SAAS,QAAQ,aAAa,eAAe,mBAAmB,mBAAmB;AACnF,SAAS,MAAM,aAAa,UAAU,mBAAmB;AACzD,SAAS,kBAAkB;AAC3B,SAAS,cAAc;AAqChB,SAAS,sBAAsB;AAAA,EACpC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,SAAS,CAAC;AAAA,EACV;AACF,GAA+B;AAC7B,QAAM,QAAQ,SAAS,CAAC,GAAG,MAAM;AACjC,SACE;AAAA,IAAC;AAAA;AAAA,MACE,GAAI,SAAS,SAAY,EAAE,KAAK,IAAI,CAAC;AAAA,MACrC,GAAI,gBAAgB,SAAY,EAAE,YAAY,IAAI,CAAC;AAAA,MACnD,GAAI,eAAe,EAAE,aAAa,IAAI,CAAC;AAAA,MAExC;AAAA,QAAC;AAAA;AAAA,UACC,iBAAiB;AAAA,UACjB,WAAW,GAAG,iCAAiC,SAAS;AAAA,UAExD;AAAA;AAAA,cAAC;AAAA;AAAA,gBACC,aAAY;AAAA,gBACX,GAAI,UAAU,SAAY,EAAE,MAAM,IAAI,EAAE,cAAc,gBAAgB,MAAM;AAAA,gBAC5E,GAAI,gBAAgB,EAAE,cAAc,IAAI,CAAC;AAAA,gBAC1C,WAAW;AAAA,gBAEX;AAAA,uCAAC,SAAI,WAAW,iCACd;AAAA,wCAAC,YAAS,WAAW,8BAClB,mBAAS,IAAI,CAAC,MACb,qBAAC,eAAuB,OAAO,EAAE,IAAI,WAAW,+BAC7C;AAAA,wBAAE;AAAA,sBACF,EAAE;AAAA,yBAFa,EAAE,EAGpB,CACD,GACH;AAAA,oBACC,iBAAiB,QAChB,oBAAC,SAAI,WAAW,gCAAiC,yBAAc;AAAA,qBAEnE;AAAA,kBACA,qBAAC,SAAI,WAAW,8BACd;AAAA,yCAAC,SAAI,WAAW,iCACd;AAAA,0CAAC,eAAY,WAAW,+BAAgC,iBAAM;AAAA,sBAC7D,eAAe,QACd,oBAAC,qBAAkB,WAAW,qCAAsC,uBAAY;AAAA,uBAEpF;AAAA,oBACC,SAAS,IAAI,CAAC,MACb,oBAAC,eAAuB,OAAO,EAAE,IAAI,WAAW,+BAC7C,YAAE,WADa,EAAE,EAEpB,CACD;AAAA,qBACH;AAAA;AAAA;AAAA,YACF;AAAA,YACA,oBAAC,eAAY,SAAO,MAClB,8BAAC,cAAW,cAAY,OAAO,SAAS,SAAS,WAAW,+BAC1D,8BAAC,UAAO,MAAK,KAAI,GACnB,GACF;AAAA;AAAA;AAAA,MACF;AAAA;AAAA,EACF;AAEJ;","names":[]}
|
|
@@ -4,7 +4,7 @@ import * as React from "react";
|
|
|
4
4
|
import { cn } from "../lib/cn.js";
|
|
5
5
|
import { buttonVariants } from "./button.js";
|
|
6
6
|
import { useControllableState } from "../primitives/use-controllable-state.js";
|
|
7
|
-
import { DismissableLayerScope, useDismissableLayer } from "../primitives/dismissable-layer.js";
|
|
7
|
+
import { DismissableLayerScope, useDismissableLayer, useModalScrimStyle } from "../primitives/dismissable-layer.js";
|
|
8
8
|
import { useFocusScope } from "../primitives/focus-scope.js";
|
|
9
9
|
import { usePresence } from "../primitives/presence.js";
|
|
10
10
|
import { mergeRefs } from "../primitives/merge-refs.js";
|
|
@@ -72,9 +72,10 @@ function AlertDialogContent({ className, children, ...props }) {
|
|
|
72
72
|
exclude: [triggerRef],
|
|
73
73
|
modal: true
|
|
74
74
|
});
|
|
75
|
+
const scrimStyle = useModalScrimStyle(dismissRef);
|
|
75
76
|
if (!isPresent) return null;
|
|
76
77
|
return /* @__PURE__ */ jsxs(Portal, { children: [
|
|
77
|
-
/* @__PURE__ */ jsx(AlertDialogOverlay, {}),
|
|
78
|
+
/* @__PURE__ */ jsx(AlertDialogOverlay, { style: scrimStyle }),
|
|
78
79
|
/* @__PURE__ */ jsx(
|
|
79
80
|
"div",
|
|
80
81
|
{
|