@salemcobalt/ui 0.2.0-rc.20 → 0.2.0-rc.22
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 +38 -37
- package/dist/{codemirror-D3Z4MYFA.js → codemirror-6REE3TUN.js} +8 -1
- package/dist/{codemirror-D3Z4MYFA.js.map → codemirror-6REE3TUN.js.map} +1 -1
- package/dist/editor.d.ts +12 -3
- package/dist/editor.js +27 -2
- package/dist/editor.js.map +1 -1
- package/dist/index.d.ts +39 -2
- package/dist/index.js +63 -0
- package/dist/index.js.map +1 -1
- package/dist/styles.css +110 -1
- package/package.json +3 -3
package/dist/editor.d.ts
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
|
-
import { ReactNode } from 'react';
|
|
2
|
+
import { ReactNode, Ref } from 'react';
|
|
3
3
|
|
|
4
4
|
type TextEditorLanguage = 'yaml' | 'json' | 'markdown' | 'text';
|
|
5
5
|
|
|
6
|
+
/** What `ref` gives you: focus and line selection, on the Textarea before CodeMirror loads and on CodeMirror after. */
|
|
7
|
+
interface TextEditorHandle {
|
|
8
|
+
/** Focuses the editor. */
|
|
9
|
+
focus(): void;
|
|
10
|
+
/** Selects line `line` (1-based, clamped to the text), scrolls it into view and focuses the editor. */
|
|
11
|
+
selectLine(line: number): void;
|
|
12
|
+
}
|
|
6
13
|
interface TextEditorProps {
|
|
7
14
|
/** Required. The visible label. */
|
|
8
15
|
label: ReactNode;
|
|
@@ -34,13 +41,15 @@ interface TextEditorProps {
|
|
|
34
41
|
id?: string | undefined;
|
|
35
42
|
/** Class on the wrapper. */
|
|
36
43
|
className?: string | undefined;
|
|
44
|
+
/** Focus and select a line from outside, such as "Show line 12". */
|
|
45
|
+
ref?: Ref<TextEditorHandle> | undefined;
|
|
37
46
|
}
|
|
38
47
|
/**
|
|
39
48
|
* A code and text editor on CodeMirror 6, with inline word diffs against `base`. Client-only: the server and the
|
|
40
49
|
* first client render show a code `<textarea>` in the same field chrome, and CodeMirror replaces it once its chunk
|
|
41
50
|
* has loaded, keeping focus and the selection.
|
|
42
51
|
*/
|
|
43
|
-
declare function TextEditor({ label, description, error, value, onValueChange, base, language, liveDiff, disabled, readOnly, rows, id: idProp, className, }: TextEditorProps): react.JSX.Element;
|
|
52
|
+
declare function TextEditor({ label, description, error, value, onValueChange, base, language, liveDiff, disabled, readOnly, rows, id: idProp, className, ref, }: TextEditorProps): react.JSX.Element;
|
|
44
53
|
|
|
45
54
|
export { TextEditor };
|
|
46
|
-
export type { TextEditorLanguage, TextEditorProps };
|
|
55
|
+
export type { TextEditorHandle, TextEditorLanguage, TextEditorProps };
|
package/dist/editor.js
CHANGED
|
@@ -5,6 +5,7 @@ import { Field } from "@base-ui/react/field";
|
|
|
5
5
|
import {
|
|
6
6
|
useEffect,
|
|
7
7
|
useId,
|
|
8
|
+
useImperativeHandle,
|
|
8
9
|
useLayoutEffect,
|
|
9
10
|
useRef,
|
|
10
11
|
useState
|
|
@@ -17,8 +18,14 @@ function cx(...parts) {
|
|
|
17
18
|
|
|
18
19
|
// src/components/TextEditor/TextEditor.tsx
|
|
19
20
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
20
|
-
var load = () => import("./codemirror-
|
|
21
|
+
var load = () => import("./codemirror-6REE3TUN.js");
|
|
21
22
|
var isProse = (language) => language === "markdown" || language === "text";
|
|
23
|
+
function lineRange(text, line) {
|
|
24
|
+
const lines = text.split("\n");
|
|
25
|
+
const n = Math.min(Math.max(Math.trunc(line), 1), lines.length);
|
|
26
|
+
const start = lines.slice(0, n - 1).reduce((at, l) => at + l.length + 1, 0);
|
|
27
|
+
return [start, start + lines[n - 1].length];
|
|
28
|
+
}
|
|
22
29
|
function TextEditor({
|
|
23
30
|
label,
|
|
24
31
|
description,
|
|
@@ -32,7 +39,8 @@ function TextEditor({
|
|
|
32
39
|
readOnly = false,
|
|
33
40
|
rows = 8,
|
|
34
41
|
id: idProp,
|
|
35
|
-
className
|
|
42
|
+
className,
|
|
43
|
+
ref
|
|
36
44
|
}) {
|
|
37
45
|
const [module, setModule] = useState(null);
|
|
38
46
|
const textareaRef = useRef(null);
|
|
@@ -94,6 +102,23 @@ function TextEditor({
|
|
|
94
102
|
useLayoutEffect(() => {
|
|
95
103
|
handleRef.current?.update(options);
|
|
96
104
|
});
|
|
105
|
+
useImperativeHandle(
|
|
106
|
+
ref,
|
|
107
|
+
() => ({
|
|
108
|
+
focus() {
|
|
109
|
+
if (handleRef.current) return handleRef.current.focus();
|
|
110
|
+
if (!optionsRef.current.disabled) textareaRef.current?.focus();
|
|
111
|
+
},
|
|
112
|
+
selectLine(line) {
|
|
113
|
+
if (handleRef.current) return handleRef.current.selectLine(line);
|
|
114
|
+
const ta = textareaRef.current;
|
|
115
|
+
if (!ta || optionsRef.current.disabled) return;
|
|
116
|
+
ta.focus();
|
|
117
|
+
ta.setSelectionRange(...lineRange(ta.value, line));
|
|
118
|
+
}
|
|
119
|
+
}),
|
|
120
|
+
[]
|
|
121
|
+
);
|
|
97
122
|
const prose = isProse(language);
|
|
98
123
|
return /* @__PURE__ */ jsxs(
|
|
99
124
|
Field.Root,
|
package/dist/editor.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/TextEditor/TextEditor.tsx","../src/utils/cx.ts"],"sourcesContent":["import { Field } from '@base-ui/react/field';\nimport {\n useEffect,\n useId,\n useLayoutEffect,\n useRef,\n useState,\n type CSSProperties,\n type ReactNode,\n} from 'react';\nimport { cx } from '../../utils/cx';\nimport type * as CodeMirror from './codemirror';\nimport type { EditorHandle, EditorOptions, TextEditorLanguage } from './codemirror';\n\nexport type { TextEditorLanguage };\n\nexport interface TextEditorProps {\n /** Required. The visible label. */\n label: ReactNode;\n /** Help text under the editor, wired with `aria-describedby`. */\n description?: ReactNode | undefined;\n /** The error message. When given the editor is invalid and the message is announced. */\n error?: ReactNode | undefined;\n /** The text. Controlled: feed `onValueChange`'s value back. */\n value: string;\n onValueChange: (value: string) => void;\n /** The text to compare against, such as the saved version. With a base, changes show inline; without, no diff. */\n base?: string | undefined;\n /**\n * Syntax highlighting, loaded per language. `markdown` and `text` (default) keep the phone's autocorrect,\n * capitals and spellcheck; `yaml` and `json` turn them off.\n */\n language?: TextEditorLanguage | undefined;\n /**\n * Keep showing changes while the editor has focus, updated 300ms after the last keystroke. Default false: the\n * diff hides while you type, so nothing moves under the caret, and shows again on blur.\n */\n liveDiff?: boolean | undefined;\n disabled?: boolean | undefined;\n /** Focusable and selectable, not editable. The phone's keyboard stays down. */\n readOnly?: boolean | undefined;\n /** Minimum visible lines; the editor grows with its content. Default 8. */\n rows?: number | undefined;\n /** The editable element's id (the Textarea's before CodeMirror loads). */\n id?: string | undefined;\n /** Class on the wrapper. */\n className?: string | undefined;\n}\n\ntype CodeMirrorModule = typeof CodeMirror;\n// The only path to CodeMirror: a dynamic import, so it lands in its own chunk and never in the server render.\nconst load = (): Promise<CodeMirrorModule> => import('./codemirror');\n\nconst isProse = (language: TextEditorLanguage) => language === 'markdown' || language === 'text';\n\n/**\n * A code and text editor on CodeMirror 6, with inline word diffs against `base`. Client-only: the server and the\n * first client render show a code `<textarea>` in the same field chrome, and CodeMirror replaces it once its chunk\n * has loaded, keeping focus and the selection.\n */\nexport function TextEditor({\n label,\n description,\n error,\n value,\n onValueChange,\n base,\n language = 'text',\n liveDiff = false,\n disabled = false,\n readOnly = false,\n rows = 8,\n id: idProp,\n className,\n}: TextEditorProps) {\n const [module, setModule] = useState<CodeMirrorModule | null>(null);\n const textareaRef = useRef<HTMLTextAreaElement>(null);\n const hostRef = useRef<HTMLDivElement>(null);\n const handleRef = useRef<EditorHandle | null>(null);\n // The Textarea's focus and selection at the moment of the swap.\n const handoffRef = useRef<[number, number] | null>(null);\n const onChangeRef = useRef(onValueChange);\n useLayoutEffect(() => {\n onChangeRef.current = onValueChange;\n });\n const generatedId = useId();\n const id = idProp ?? generatedId;\n const labelId = `${id}-label`;\n const descriptionId = `${id}-description`;\n const errorId = `${id}-error`;\n const invalid = error != null;\n const describedBy = [description != null && descriptionId, invalid && errorId].filter(Boolean).join(' ');\n const options: EditorOptions = {\n value,\n base,\n liveDiff,\n language,\n disabled,\n readOnly,\n invalid,\n attributes: {\n id,\n 'aria-labelledby': labelId,\n ...(describedBy ? { 'aria-describedby': describedBy } : {}),\n },\n };\n const optionsRef = useRef(options);\n useLayoutEffect(() => {\n optionsRef.current = options;\n });\n\n useEffect(() => {\n let live = true;\n void load().then((m) => {\n if (!live) return;\n const ta = textareaRef.current;\n if (ta && document.activeElement === ta) handoffRef.current = [ta.selectionStart, ta.selectionEnd];\n setModule(() => m);\n });\n return () => {\n live = false;\n };\n }, []);\n\n useLayoutEffect(() => {\n if (!module || !hostRef.current) return;\n const handle = module.mountEditor(hostRef.current, optionsRef.current, (v) => onChangeRef.current(v));\n handleRef.current = handle;\n if (handoffRef.current) handle.focus(...handoffRef.current);\n handoffRef.current = null;\n return () => {\n handle.destroy();\n handleRef.current = null;\n };\n }, [module]);\n\n useLayoutEffect(() => {\n handleRef.current?.update(options);\n });\n\n const prose = isProse(language);\n return (\n <Field.Root\n className={cx('sc-field', className)}\n disabled={disabled}\n invalid={invalid}\n data-size=\"md\"\n data-editor={module ? 'codemirror' : 'fallback'}\n >\n {/* A <label> can't point at a contenteditable, so once CodeMirror is in, a click focuses it by hand. */}\n <Field.Label\n id={labelId}\n htmlFor={id}\n className=\"sc-field__label\"\n onClick={() => handleRef.current?.focus()}\n >\n {label}\n </Field.Label>\n {module ? (\n <div ref={hostRef} style={{ '--_rows': rows } as CSSProperties} />\n ) : (\n <Field.Control\n id={id}\n render={\n <textarea\n ref={textareaRef}\n // Grows to its content like the editor (text-editor.css), so the swap moves the layout only by the diff.\n rows={Math.max(rows, value.split('\\n').length)}\n style={{ '--_rows': rows } as CSSProperties}\n readOnly={readOnly}\n spellCheck={prose}\n autoCorrect={prose ? 'on' : 'off'}\n autoCapitalize={prose ? 'sentences' : 'off'}\n />\n }\n className=\"sc-input sc-textarea\"\n data-variant=\"code\"\n data-resize=\"none\"\n value={value}\n onValueChange={(v) => onValueChange(v)}\n />\n )}\n {description != null ? (\n <Field.Description id={descriptionId} className=\"sc-field__description\">\n {description}\n </Field.Description>\n ) : null}\n {invalid ? (\n <Field.Error id={errorId} className=\"sc-field__error\" match>\n {error}\n </Field.Error>\n ) : null}\n </Field.Root>\n );\n}\n","/** Joins class names, dropping falsy entries. */\nexport function cx(...parts: Array<string | false | null | undefined>): string {\n return parts.filter(Boolean).join(' ');\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/components/TextEditor/TextEditor.tsx","../src/utils/cx.ts"],"sourcesContent":["import { Field } from '@base-ui/react/field';\nimport {\n useEffect,\n useId,\n useImperativeHandle,\n useLayoutEffect,\n useRef,\n useState,\n type CSSProperties,\n type ReactNode,\n type Ref,\n} from 'react';\nimport { cx } from '../../utils/cx';\nimport type * as CodeMirror from './codemirror';\nimport type { EditorHandle, EditorOptions, TextEditorLanguage } from './codemirror';\n\nexport type { TextEditorLanguage };\n\n/** What `ref` gives you: focus and line selection, on the Textarea before CodeMirror loads and on CodeMirror after. */\nexport interface TextEditorHandle {\n /** Focuses the editor. */\n focus(): void;\n /** Selects line `line` (1-based, clamped to the text), scrolls it into view and focuses the editor. */\n selectLine(line: number): void;\n}\n\nexport interface TextEditorProps {\n /** Required. The visible label. */\n label: ReactNode;\n /** Help text under the editor, wired with `aria-describedby`. */\n description?: ReactNode | undefined;\n /** The error message. When given the editor is invalid and the message is announced. */\n error?: ReactNode | undefined;\n /** The text. Controlled: feed `onValueChange`'s value back. */\n value: string;\n onValueChange: (value: string) => void;\n /** The text to compare against, such as the saved version. With a base, changes show inline; without, no diff. */\n base?: string | undefined;\n /**\n * Syntax highlighting, loaded per language. `markdown` and `text` (default) keep the phone's autocorrect,\n * capitals and spellcheck; `yaml` and `json` turn them off.\n */\n language?: TextEditorLanguage | undefined;\n /**\n * Keep showing changes while the editor has focus, updated 300ms after the last keystroke. Default false: the\n * diff hides while you type, so nothing moves under the caret, and shows again on blur.\n */\n liveDiff?: boolean | undefined;\n disabled?: boolean | undefined;\n /** Focusable and selectable, not editable. The phone's keyboard stays down. */\n readOnly?: boolean | undefined;\n /** Minimum visible lines; the editor grows with its content. Default 8. */\n rows?: number | undefined;\n /** The editable element's id (the Textarea's before CodeMirror loads). */\n id?: string | undefined;\n /** Class on the wrapper. */\n className?: string | undefined;\n /** Focus and select a line from outside, such as \"Show line 12\". */\n ref?: Ref<TextEditorHandle> | undefined;\n}\n\ntype CodeMirrorModule = typeof CodeMirror;\n// The only path to CodeMirror: a dynamic import, so it lands in its own chunk and never in the server render.\nconst load = (): Promise<CodeMirrorModule> => import('./codemirror');\n\nconst isProse = (language: TextEditorLanguage) => language === 'markdown' || language === 'text';\n\n/** Line `line`'s start and end offsets in `text`, without its line break; clamped like CodeMirror's. */\nfunction lineRange(text: string, line: number): [number, number] {\n const lines = text.split('\\n');\n const n = Math.min(Math.max(Math.trunc(line), 1), lines.length);\n const start = lines.slice(0, n - 1).reduce((at, l) => at + l.length + 1, 0);\n return [start, start + lines[n - 1]!.length];\n}\n\n/**\n * A code and text editor on CodeMirror 6, with inline word diffs against `base`. Client-only: the server and the\n * first client render show a code `<textarea>` in the same field chrome, and CodeMirror replaces it once its chunk\n * has loaded, keeping focus and the selection.\n */\nexport function TextEditor({\n label,\n description,\n error,\n value,\n onValueChange,\n base,\n language = 'text',\n liveDiff = false,\n disabled = false,\n readOnly = false,\n rows = 8,\n id: idProp,\n className,\n ref,\n}: TextEditorProps) {\n const [module, setModule] = useState<CodeMirrorModule | null>(null);\n const textareaRef = useRef<HTMLTextAreaElement>(null);\n const hostRef = useRef<HTMLDivElement>(null);\n const handleRef = useRef<EditorHandle | null>(null);\n // The Textarea's focus and selection at the moment of the swap.\n const handoffRef = useRef<[number, number] | null>(null);\n const onChangeRef = useRef(onValueChange);\n useLayoutEffect(() => {\n onChangeRef.current = onValueChange;\n });\n const generatedId = useId();\n const id = idProp ?? generatedId;\n const labelId = `${id}-label`;\n const descriptionId = `${id}-description`;\n const errorId = `${id}-error`;\n const invalid = error != null;\n const describedBy = [description != null && descriptionId, invalid && errorId].filter(Boolean).join(' ');\n const options: EditorOptions = {\n value,\n base,\n liveDiff,\n language,\n disabled,\n readOnly,\n invalid,\n attributes: {\n id,\n 'aria-labelledby': labelId,\n ...(describedBy ? { 'aria-describedby': describedBy } : {}),\n },\n };\n const optionsRef = useRef(options);\n useLayoutEffect(() => {\n optionsRef.current = options;\n });\n\n useEffect(() => {\n let live = true;\n void load().then((m) => {\n if (!live) return;\n const ta = textareaRef.current;\n if (ta && document.activeElement === ta) handoffRef.current = [ta.selectionStart, ta.selectionEnd];\n setModule(() => m);\n });\n return () => {\n live = false;\n };\n }, []);\n\n useLayoutEffect(() => {\n if (!module || !hostRef.current) return;\n const handle = module.mountEditor(hostRef.current, optionsRef.current, (v) => onChangeRef.current(v));\n handleRef.current = handle;\n if (handoffRef.current) handle.focus(...handoffRef.current);\n handoffRef.current = null;\n return () => {\n handle.destroy();\n handleRef.current = null;\n };\n }, [module]);\n\n useLayoutEffect(() => {\n handleRef.current?.update(options);\n });\n\n useImperativeHandle(\n ref,\n () => ({\n focus() {\n if (handleRef.current) return handleRef.current.focus();\n if (!optionsRef.current.disabled) textareaRef.current?.focus();\n },\n selectLine(line) {\n if (handleRef.current) return handleRef.current.selectLine(line);\n const ta = textareaRef.current;\n if (!ta || optionsRef.current.disabled) return;\n ta.focus();\n ta.setSelectionRange(...lineRange(ta.value, line));\n },\n }),\n [],\n );\n\n const prose = isProse(language);\n return (\n <Field.Root\n className={cx('sc-field', className)}\n disabled={disabled}\n invalid={invalid}\n data-size=\"md\"\n data-editor={module ? 'codemirror' : 'fallback'}\n >\n {/* A <label> can't point at a contenteditable, so once CodeMirror is in, a click focuses it by hand. */}\n <Field.Label\n id={labelId}\n htmlFor={id}\n className=\"sc-field__label\"\n onClick={() => handleRef.current?.focus()}\n >\n {label}\n </Field.Label>\n {module ? (\n <div ref={hostRef} style={{ '--_rows': rows } as CSSProperties} />\n ) : (\n <Field.Control\n id={id}\n render={\n <textarea\n ref={textareaRef}\n // Grows to its content like the editor (text-editor.css), so the swap moves the layout only by the diff.\n rows={Math.max(rows, value.split('\\n').length)}\n style={{ '--_rows': rows } as CSSProperties}\n readOnly={readOnly}\n spellCheck={prose}\n autoCorrect={prose ? 'on' : 'off'}\n autoCapitalize={prose ? 'sentences' : 'off'}\n />\n }\n className=\"sc-input sc-textarea\"\n data-variant=\"code\"\n data-resize=\"none\"\n value={value}\n onValueChange={(v) => onValueChange(v)}\n />\n )}\n {description != null ? (\n <Field.Description id={descriptionId} className=\"sc-field__description\">\n {description}\n </Field.Description>\n ) : null}\n {invalid ? (\n <Field.Error id={errorId} className=\"sc-field__error\" match>\n {error}\n </Field.Error>\n ) : null}\n </Field.Root>\n );\n}\n","/** Joins class names, dropping falsy entries. */\nexport function cx(...parts: Array<string | false | null | undefined>): string {\n return parts.filter(Boolean).join(' ');\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.d.ts
CHANGED
|
@@ -3299,7 +3299,44 @@ interface QuoteProps extends ComponentPropsWithRef<'figure'> {
|
|
|
3299
3299
|
*/
|
|
3300
3300
|
declare function Quote({ label, className, children, ...rest }: QuoteProps): react.JSX.Element;
|
|
3301
3301
|
|
|
3302
|
+
type AvatarSize = 'xs' | 'sm' | 'md' | 'lg';
|
|
3303
|
+
/**
|
|
3304
|
+
* One or two capital letters for a person: the first letters of the first and last words of a name, or, for an email
|
|
3305
|
+
* address, of its local part split on `.`, `_`, `+`, `-` and digits. Empty when there is nothing to take a letter from.
|
|
3306
|
+
*/
|
|
3307
|
+
declare function avatarInitials(name: string | null | undefined): string;
|
|
3308
|
+
interface AvatarLookProps {
|
|
3309
|
+
/** The person's name, or their email address when there is no name. The initials come from it. (On AvatarButton it is not the form `name` attribute.) */
|
|
3310
|
+
name?: string | null | undefined;
|
|
3311
|
+
/** Overrides the initials taken from `name`. Shown as given, up to two characters. */
|
|
3312
|
+
initials?: string | undefined;
|
|
3313
|
+
/** A picture. The initials show until it loads, and stay if it fails. */
|
|
3314
|
+
src?: string | undefined;
|
|
3315
|
+
/** `xs` 24px (beside text), `sm` 32, `md` 40 (default) and `lg` 48 follow the control heights, compact density included. */
|
|
3316
|
+
size?: AvatarSize | undefined;
|
|
3317
|
+
}
|
|
3318
|
+
interface AvatarProps extends AvatarLookProps, Omit<ComponentPropsWithRef<'span'>, 'children'> {
|
|
3319
|
+
/** Gives the avatar an accessible name (`role="img"`), e.g. the person's name where it is not written next to it. Without it the avatar is decorative and hidden from assistive tech. */
|
|
3320
|
+
label?: string | undefined;
|
|
3321
|
+
}
|
|
3322
|
+
/**
|
|
3323
|
+
* A person: their picture, else their initials on a brand tint inside a 1px ring (the ring is what keeps the circle
|
|
3324
|
+
* visible on every surface). Decorative unless `label` is given. For a clickable avatar use `AvatarButton`.
|
|
3325
|
+
*/
|
|
3326
|
+
declare function Avatar({ name, initials, src, size, label, className, ...rest }: AvatarProps): react.JSX.Element;
|
|
3327
|
+
interface AvatarButtonProps extends AvatarLookProps, Omit<useRender.ComponentProps<'button'>, 'children' | 'aria-label' | 'name'> {
|
|
3328
|
+
/** Required. The button's accessible name, e.g. `Account: Robin Test`; the avatar inside is decorative. */
|
|
3329
|
+
label: string;
|
|
3330
|
+
}
|
|
3331
|
+
/**
|
|
3332
|
+
* A round button that is an Avatar, for an account menu or a profile link. The ring thickens on hover and while its
|
|
3333
|
+
* popup is open; the focus ring is the system's; on touch the hit area is at least 44px. Compose it as a trigger:
|
|
3334
|
+
* `<Menu.Trigger render={<AvatarButton label="Account: Robin Test" name="Robin Test" size="sm" />} />`, or make it a
|
|
3335
|
+
* link with `render={<a href="/profile" />}`.
|
|
3336
|
+
*/
|
|
3337
|
+
declare function AvatarButton({ label, name, initials, src, size, type, className, render, ref, ...rest }: AvatarButtonProps): react.ReactElement<unknown, string | react.JSXElementConstructor<any>>;
|
|
3338
|
+
|
|
3302
3339
|
declare const version = "0.1.1";
|
|
3303
3340
|
|
|
3304
|
-
export { ArchiveIcon, ArrowDownIcon, ArrowLeftIcon, ArrowRightIcon, ArrowUpIcon, Badge, Banner, BellIcon, BriefcaseIcon, BroomIcon, Button, CalendarIcon, Card, CheckIcon, Checkbox, ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, ChevronUpIcon, Chip, CircleCheckIcon, CircleXIcon, ClockIcon, CopyIcon, Dialog, Disclosure, DocumentIcon, DownloadIcon, EditIcon, EmptyState, ExternalLinkIcon, EyeIcon, FileInput, FilePreviewDialog, FilterIcon, FlagIcon, FullScreenFlow, Heading, Icon, IconButton, InboxIcon, InfoIcon, Inline, Input, Kbd, LinkIcon, MailIcon, MapPinIcon, Menu, MenuIcon, MinusIcon, MoonIcon, MoreHorizontalIcon, MoreVerticalIcon, PauseIcon, PlayIcon, PlusIcon, Popover, PriorityNav, Progress$1 as Progress, Quote, ReasonList, RefreshIcon, Score, SearchIcon, Select, SelectionBar, SettingsIcon, Skeleton, Spinner, Stack, StarIcon, Stat, SunIcon, Switch, Table, Tabs, Text, Textarea, ThumbsDownIcon, ThumbsUpIcon, Toast, Toolbar, Tooltip, TrashIcon, UndoIcon, UserIcon, WarningIcon, WordDiff, XIcon, iconNames, useRowSelection, useToast, version };
|
|
3305
|
-
export type { BadgeProps, BadgeSize, BadgeTone, BadgeVariant, BannerProps, BannerTone, BroomIconProps, ButtonProps, ButtonSize, ButtonVariant, CardElement, CardPadding, CardProps, CardVariant, CheckboxProps, ChipProps, ChipSize, ChipTone, ControlSize, DialogActionsProps, DialogBodyProps, DialogCloseProps, DialogCloseRequestReason, DialogContentProps, DialogDescriptionProps, DialogRootProps, DialogSize, DialogTitleProps, DialogVariant, DisclosureProps, EmptyStateElement, EmptyStateProps, EmptyStateSize, FieldProps, FileInputProps, FilePreviewDialogProps, FlowChapter, FlowCloseRequestReason, FlowDirection, FullScreenFlowBodyProps, FullScreenFlowFooterProps, FullScreenFlowHeaderProps, FullScreenFlowProgressProps, FullScreenFlowRailGroupProps, FullScreenFlowRailItemProps, FullScreenFlowRailProps, FullScreenFlowRootProps, FullScreenFlowStepProps, FullScreenFlowViewportProps, HeadingElement, HeadingLevel, HeadingProps, HeadingSize, IconButtonDotTone, IconButtonProps, IconGlyphProps, IconName, IconProps, IconSize, InlineAlign, InlineElement, InlineJustify, InlineProps, InputProps, KbdGroupProps, KbdProps, MenuAlign, MenuCheckboxItemProps, MenuContentProps, MenuGroupProps, MenuItemProps, MenuItemTone, MenuLinkItemProps, MenuRadioGroupProps, MenuRadioItemProps, MenuRootProps, MenuSeparatorProps, MenuSide, MenuTriggerProps, PopoverAlign, PopoverCloseProps, PopoverContentProps, PopoverDescriptionProps, PopoverRootProps, PopoverSide, PopoverTitleProps, PopoverTriggerProps, PriorityNavItem, PriorityNavLinkProps, PriorityNavProps, ProgressProps, ProgressSize, ProgressTone, ProgressVariant, QuoteProps, ReasonListItemProps, ReasonListProps, ReasonListSize, ReasonTone, RowSelection, RowSelectionHeaderState, ScoreProps, ScoreSize, ScoreTone, ScoreVariant, SelectGroup, SelectOption, SelectProps, SelectionBarProps, SkeletonProps, SkeletonVariant, SpacingSize, SpinnerProps, StackAlign, StackElement, StackJustify, StackProps, StatDeltaTone, StatElement, StatProps, StatSize, StatTrend, StatVariant, SwitchProps, TableAlign, TableBodyProps, TableCaptionProps, TableCellProps, TableDensity, TableFootProps, TableHeadProps, TableHeaderProps, TableRootProps, TableRowProps, TableSelectionCellProps, TableSelectionHeaderProps, TableSortDirection, TabsListProps, TabsListVariant, TabsPanelProps, TabsRootProps, TabsTabProps, TextAlign, TextElement, TextFamily, TextProps, TextSize, TextTone, TextWeight, TextareaProps, TextareaVariant, ToastApi, ToastOptions, ToastProviderProps, ToastTone, ToolbarButtonProps, ToolbarGroupProps, ToolbarLinkProps, ToolbarRootProps, ToolbarSeparatorProps, ToolbarSpacerProps, TooltipAlign, TooltipProps, TooltipSide, WordDiffElement, WordDiffKind, WordDiffPart, WordDiffProps, WordDiffSize };
|
|
3341
|
+
export { ArchiveIcon, ArrowDownIcon, ArrowLeftIcon, ArrowRightIcon, ArrowUpIcon, Avatar, AvatarButton, Badge, Banner, BellIcon, BriefcaseIcon, BroomIcon, Button, CalendarIcon, Card, CheckIcon, Checkbox, ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, ChevronUpIcon, Chip, CircleCheckIcon, CircleXIcon, ClockIcon, CopyIcon, Dialog, Disclosure, DocumentIcon, DownloadIcon, EditIcon, EmptyState, ExternalLinkIcon, EyeIcon, FileInput, FilePreviewDialog, FilterIcon, FlagIcon, FullScreenFlow, Heading, Icon, IconButton, InboxIcon, InfoIcon, Inline, Input, Kbd, LinkIcon, MailIcon, MapPinIcon, Menu, MenuIcon, MinusIcon, MoonIcon, MoreHorizontalIcon, MoreVerticalIcon, PauseIcon, PlayIcon, PlusIcon, Popover, PriorityNav, Progress$1 as Progress, Quote, ReasonList, RefreshIcon, Score, SearchIcon, Select, SelectionBar, SettingsIcon, Skeleton, Spinner, Stack, StarIcon, Stat, SunIcon, Switch, Table, Tabs, Text, Textarea, ThumbsDownIcon, ThumbsUpIcon, Toast, Toolbar, Tooltip, TrashIcon, UndoIcon, UserIcon, WarningIcon, WordDiff, XIcon, avatarInitials, iconNames, useRowSelection, useToast, version };
|
|
3342
|
+
export type { AvatarButtonProps, AvatarProps, AvatarSize, BadgeProps, BadgeSize, BadgeTone, BadgeVariant, BannerProps, BannerTone, BroomIconProps, ButtonProps, ButtonSize, ButtonVariant, CardElement, CardPadding, CardProps, CardVariant, CheckboxProps, ChipProps, ChipSize, ChipTone, ControlSize, DialogActionsProps, DialogBodyProps, DialogCloseProps, DialogCloseRequestReason, DialogContentProps, DialogDescriptionProps, DialogRootProps, DialogSize, DialogTitleProps, DialogVariant, DisclosureProps, EmptyStateElement, EmptyStateProps, EmptyStateSize, FieldProps, FileInputProps, FilePreviewDialogProps, FlowChapter, FlowCloseRequestReason, FlowDirection, FullScreenFlowBodyProps, FullScreenFlowFooterProps, FullScreenFlowHeaderProps, FullScreenFlowProgressProps, FullScreenFlowRailGroupProps, FullScreenFlowRailItemProps, FullScreenFlowRailProps, FullScreenFlowRootProps, FullScreenFlowStepProps, FullScreenFlowViewportProps, HeadingElement, HeadingLevel, HeadingProps, HeadingSize, IconButtonDotTone, IconButtonProps, IconGlyphProps, IconName, IconProps, IconSize, InlineAlign, InlineElement, InlineJustify, InlineProps, InputProps, KbdGroupProps, KbdProps, MenuAlign, MenuCheckboxItemProps, MenuContentProps, MenuGroupProps, MenuItemProps, MenuItemTone, MenuLinkItemProps, MenuRadioGroupProps, MenuRadioItemProps, MenuRootProps, MenuSeparatorProps, MenuSide, MenuTriggerProps, PopoverAlign, PopoverCloseProps, PopoverContentProps, PopoverDescriptionProps, PopoverRootProps, PopoverSide, PopoverTitleProps, PopoverTriggerProps, PriorityNavItem, PriorityNavLinkProps, PriorityNavProps, ProgressProps, ProgressSize, ProgressTone, ProgressVariant, QuoteProps, ReasonListItemProps, ReasonListProps, ReasonListSize, ReasonTone, RowSelection, RowSelectionHeaderState, ScoreProps, ScoreSize, ScoreTone, ScoreVariant, SelectGroup, SelectOption, SelectProps, SelectionBarProps, SkeletonProps, SkeletonVariant, SpacingSize, SpinnerProps, StackAlign, StackElement, StackJustify, StackProps, StatDeltaTone, StatElement, StatProps, StatSize, StatTrend, StatVariant, SwitchProps, TableAlign, TableBodyProps, TableCaptionProps, TableCellProps, TableDensity, TableFootProps, TableHeadProps, TableHeaderProps, TableRootProps, TableRowProps, TableSelectionCellProps, TableSelectionHeaderProps, TableSortDirection, TabsListProps, TabsListVariant, TabsPanelProps, TabsRootProps, TabsTabProps, TextAlign, TextElement, TextFamily, TextProps, TextSize, TextTone, TextWeight, TextareaProps, TextareaVariant, ToastApi, ToastOptions, ToastProviderProps, ToastTone, ToolbarButtonProps, ToolbarGroupProps, ToolbarLinkProps, ToolbarRootProps, ToolbarSeparatorProps, ToolbarSpacerProps, TooltipAlign, TooltipProps, TooltipSide, WordDiffElement, WordDiffKind, WordDiffPart, WordDiffProps, WordDiffSize };
|
package/dist/index.js
CHANGED
|
@@ -3640,6 +3640,66 @@ function Quote({ label, className, children, ...rest }) {
|
|
|
3640
3640
|
] });
|
|
3641
3641
|
}
|
|
3642
3642
|
|
|
3643
|
+
// src/components/Avatar/Avatar.tsx
|
|
3644
|
+
import { Avatar as BaseAvatar } from "@base-ui/react/avatar";
|
|
3645
|
+
import { useRender as useRender3 } from "@base-ui/react/use-render";
|
|
3646
|
+
import { jsx as jsx39, jsxs as jsxs33 } from "react/jsx-runtime";
|
|
3647
|
+
function avatarInitials(name) {
|
|
3648
|
+
const value = name?.trim() ?? "";
|
|
3649
|
+
const words = (value.includes("@") ? (value.split("@")[0] ?? "").split(/[._+\-\d]+/) : value.split(/\s+/)).filter(Boolean);
|
|
3650
|
+
const first = (w) => w ? Array.from(w)[0] ?? "" : "";
|
|
3651
|
+
const letters = words.length > 1 ? first(words[0]) + first(words[words.length - 1]) : first(words[0]);
|
|
3652
|
+
return letters.toUpperCase();
|
|
3653
|
+
}
|
|
3654
|
+
function Letters({ name, initials }) {
|
|
3655
|
+
const letters = initials?.trim() ? Array.from(initials.trim()).slice(0, 2).join("") : avatarInitials(name);
|
|
3656
|
+
return letters ? /* @__PURE__ */ jsx39("span", { className: "sc-avatar__initials", children: letters }) : /* @__PURE__ */ jsx39(UserIcon, { className: "sc-avatar__icon", size: "em" });
|
|
3657
|
+
}
|
|
3658
|
+
function Avatar({ name, initials, src, size = "md", label, className, ...rest }) {
|
|
3659
|
+
return /* @__PURE__ */ jsxs33(
|
|
3660
|
+
BaseAvatar.Root,
|
|
3661
|
+
{
|
|
3662
|
+
className: cx("sc-avatar", className),
|
|
3663
|
+
"data-size": size,
|
|
3664
|
+
role: label ? "img" : void 0,
|
|
3665
|
+
"aria-label": label,
|
|
3666
|
+
"aria-hidden": label ? void 0 : true,
|
|
3667
|
+
...rest,
|
|
3668
|
+
children: [
|
|
3669
|
+
src ? /* @__PURE__ */ jsx39(BaseAvatar.Image, { className: "sc-avatar__image", src, alt: "" }) : null,
|
|
3670
|
+
/* @__PURE__ */ jsx39(BaseAvatar.Fallback, { className: "sc-avatar__fallback", children: /* @__PURE__ */ jsx39(Letters, { name, initials }) })
|
|
3671
|
+
]
|
|
3672
|
+
}
|
|
3673
|
+
);
|
|
3674
|
+
}
|
|
3675
|
+
function AvatarButton({
|
|
3676
|
+
label,
|
|
3677
|
+
name,
|
|
3678
|
+
initials,
|
|
3679
|
+
src,
|
|
3680
|
+
size = "md",
|
|
3681
|
+
type,
|
|
3682
|
+
className,
|
|
3683
|
+
render,
|
|
3684
|
+
ref,
|
|
3685
|
+
...rest
|
|
3686
|
+
}) {
|
|
3687
|
+
return useRender3({
|
|
3688
|
+
render,
|
|
3689
|
+
ref,
|
|
3690
|
+
defaultTagName: "button",
|
|
3691
|
+
props: {
|
|
3692
|
+
// `type` only means something on a real button; a rendered link keeps its own attributes.
|
|
3693
|
+
type: render ? type : type ?? "button",
|
|
3694
|
+
className: cx("sc-avatar-button", className),
|
|
3695
|
+
"data-size": size,
|
|
3696
|
+
"aria-label": label,
|
|
3697
|
+
...rest,
|
|
3698
|
+
children: /* @__PURE__ */ jsx39(Avatar, { name, initials, src, size })
|
|
3699
|
+
}
|
|
3700
|
+
});
|
|
3701
|
+
}
|
|
3702
|
+
|
|
3643
3703
|
// src/index.ts
|
|
3644
3704
|
var version = "0.1.1";
|
|
3645
3705
|
export {
|
|
@@ -3648,6 +3708,8 @@ export {
|
|
|
3648
3708
|
ArrowLeftIcon,
|
|
3649
3709
|
ArrowRightIcon,
|
|
3650
3710
|
ArrowUpIcon,
|
|
3711
|
+
Avatar,
|
|
3712
|
+
AvatarButton,
|
|
3651
3713
|
Badge,
|
|
3652
3714
|
Banner,
|
|
3653
3715
|
BellIcon,
|
|
@@ -3733,6 +3795,7 @@ export {
|
|
|
3733
3795
|
WarningIcon,
|
|
3734
3796
|
WordDiff,
|
|
3735
3797
|
XIcon,
|
|
3798
|
+
avatarInitials,
|
|
3736
3799
|
iconNames,
|
|
3737
3800
|
useRowSelection,
|
|
3738
3801
|
useToast,
|