@sole-ui/react 0.0.0-stage → 0.2.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 +98 -2
- package/dist/components/shell/app-frame.d.ts +14 -0
- package/dist/components/shell/app-frame.js +8 -0
- package/dist/components/shell/command-palette.d.ts +32 -0
- package/dist/components/shell/command-palette.js +30 -0
- package/dist/components/shell/index.d.ts +4 -0
- package/dist/components/shell/index.js +4 -0
- package/dist/components/shell/page.d.ts +25 -0
- package/dist/components/shell/page.js +22 -0
- package/dist/components/shell/sidebar.d.ts +81 -0
- package/dist/components/shell/sidebar.js +113 -0
- package/dist/components/sole/avatar.d.ts +19 -0
- package/dist/components/sole/avatar.js +37 -0
- package/dist/components/sole/banner.d.ts +24 -0
- package/dist/components/sole/banner.js +27 -0
- package/dist/components/sole/column-header.d.ts +15 -0
- package/dist/components/sole/column-header.js +15 -0
- package/dist/components/sole/copy-field.d.ts +10 -0
- package/dist/components/sole/copy-field.js +32 -0
- package/dist/components/sole/count-badge.d.ts +8 -0
- package/dist/components/sole/count-badge.js +8 -0
- package/dist/components/sole/field.d.ts +24 -0
- package/dist/components/sole/field.js +27 -0
- package/dist/components/sole/filters.d.ts +33 -0
- package/dist/components/sole/filters.js +48 -0
- package/dist/components/sole/index.d.ts +17 -0
- package/dist/components/sole/index.js +17 -0
- package/dist/components/sole/kbd.d.ts +7 -0
- package/dist/components/sole/kbd.js +14 -0
- package/dist/components/sole/label-tag.d.ts +18 -0
- package/dist/components/sole/label-tag.js +17 -0
- package/dist/components/sole/layout.d.ts +54 -0
- package/dist/components/sole/layout.js +48 -0
- package/dist/components/sole/logo.d.ts +13 -0
- package/dist/components/sole/logo.js +17 -0
- package/dist/components/sole/nav.d.ts +22 -0
- package/dist/components/sole/nav.js +32 -0
- package/dist/components/sole/person-chip.d.ts +8 -0
- package/dist/components/sole/person-chip.js +19 -0
- package/dist/components/sole/person-header.d.ts +21 -0
- package/dist/components/sole/person-header.js +22 -0
- package/dist/components/sole/pill.d.ts +9 -0
- package/dist/components/sole/pill.js +14 -0
- package/dist/components/sole/states.d.ts +48 -0
- package/dist/components/sole/states.js +53 -0
- package/dist/components/sole/table.d.ts +23 -0
- package/dist/components/sole/table.js +32 -0
- package/dist/components/ui/alert-dialog.d.ts +27 -0
- package/dist/components/ui/alert-dialog.js +33 -0
- package/dist/components/ui/badge.d.ts +11 -0
- package/dist/components/ui/badge.js +30 -0
- package/dist/components/ui/button.d.ts +13 -0
- package/dist/components/ui/button.js +49 -0
- package/dist/components/ui/calendar.d.ts +9 -0
- package/dist/components/ui/calendar.js +79 -0
- package/dist/components/ui/checkbox.d.ts +4 -0
- package/dist/components/ui/checkbox.js +11 -0
- package/dist/components/ui/command.d.ts +90 -0
- package/dist/components/ui/command.js +36 -0
- package/dist/components/ui/dialog.d.ts +18 -0
- package/dist/components/ui/dialog.js +33 -0
- package/dist/components/ui/dropdown-menu.d.ts +25 -0
- package/dist/components/ui/dropdown-menu.js +42 -0
- package/dist/components/ui/index.d.ts +23 -0
- package/dist/components/ui/index.js +23 -0
- package/dist/components/ui/input.d.ts +3 -0
- package/dist/components/ui/input.js +12 -0
- package/dist/components/ui/label.d.ts +4 -0
- package/dist/components/ui/label.js +10 -0
- package/dist/components/ui/popover.d.ts +10 -0
- package/dist/components/ui/popover.js +22 -0
- package/dist/components/ui/scroll-area.d.ts +5 -0
- package/dist/components/ui/scroll-area.js +14 -0
- package/dist/components/ui/select.d.ts +15 -0
- package/dist/components/ui/select.js +30 -0
- package/dist/components/ui/separator.d.ts +4 -0
- package/dist/components/ui/separator.js +10 -0
- package/dist/components/ui/sheet.d.ts +14 -0
- package/dist/components/ui/sheet.js +35 -0
- package/dist/components/ui/skeleton.d.ts +3 -0
- package/dist/components/ui/skeleton.js +10 -0
- package/dist/components/ui/sonner.d.ts +5 -0
- package/dist/components/ui/sonner.js +63 -0
- package/dist/components/ui/switch.d.ts +6 -0
- package/dist/components/ui/switch.js +10 -0
- package/dist/components/ui/tabs.d.ts +7 -0
- package/dist/components/ui/tabs.js +24 -0
- package/dist/components/ui/textarea.d.ts +3 -0
- package/dist/components/ui/textarea.js +9 -0
- package/dist/components/ui/toggle-group.d.ts +14 -0
- package/dist/components/ui/toggle-group.js +24 -0
- package/dist/components/ui/toggle.d.ts +12 -0
- package/dist/components/ui/toggle.js +28 -0
- package/dist/components/ui/tooltip.d.ts +7 -0
- package/dist/components/ui/tooltip.js +18 -0
- package/dist/hq/activity-line.d.ts +21 -0
- package/dist/hq/activity-line.js +26 -0
- package/dist/hq/board-card.d.ts +12 -0
- package/dist/hq/board-card.js +21 -0
- package/dist/hq/board-chip.d.ts +12 -0
- package/dist/hq/board-chip.js +20 -0
- package/dist/hq/checklist-item.d.ts +14 -0
- package/dist/hq/checklist-item.js +13 -0
- package/dist/hq/comment.d.ts +26 -0
- package/dist/hq/comment.js +15 -0
- package/dist/hq/due-chip.d.ts +11 -0
- package/dist/hq/due-chip.js +25 -0
- package/dist/hq/group-header.d.ts +27 -0
- package/dist/hq/group-header.js +22 -0
- package/dist/hq/inbox-row.d.ts +22 -0
- package/dist/hq/inbox-row.js +13 -0
- package/dist/hq/index.d.ts +21 -0
- package/dist/hq/index.js +21 -0
- package/dist/hq/link-row.d.ts +13 -0
- package/dist/hq/link-row.js +13 -0
- package/dist/hq/mention-chip.d.ts +23 -0
- package/dist/hq/mention-chip.js +28 -0
- package/dist/hq/page-row.d.ts +26 -0
- package/dist/hq/page-row.js +30 -0
- package/dist/hq/priority-icon.d.ts +9 -0
- package/dist/hq/priority-icon.js +23 -0
- package/dist/hq/property-type-icon.d.ts +7 -0
- package/dist/hq/property-type-icon.js +22 -0
- package/dist/hq/recent-item.d.ts +11 -0
- package/dist/hq/recent-item.js +12 -0
- package/dist/hq/review-badge.d.ts +37 -0
- package/dist/hq/review-badge.js +77 -0
- package/dist/hq/state-icon.d.ts +12 -0
- package/dist/hq/state-icon.js +41 -0
- package/dist/hq/state-pill.d.ts +9 -0
- package/dist/hq/state-pill.js +18 -0
- package/dist/hq/sticky.d.ts +13 -0
- package/dist/hq/sticky.js +22 -0
- package/dist/hq/template-card.d.ts +10 -0
- package/dist/hq/template-card.js +9 -0
- package/dist/hq/types.d.ts +49 -0
- package/dist/hq/types.js +7 -0
- package/dist/hq/work-item-row.d.ts +16 -0
- package/dist/hq/work-item-row.js +23 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +12 -0
- package/dist/lib/cn.d.ts +2 -0
- package/dist/lib/cn.js +15 -0
- package/dist/lib/colour.d.ts +8 -0
- package/dist/lib/colour.js +28 -0
- package/dist/lib/dates.d.ts +62 -0
- package/dist/lib/dates.js +182 -0
- package/dist/lib/hue.d.ts +3 -0
- package/dist/lib/hue.js +1 -0
- package/dist/lib/person.d.ts +16 -0
- package/dist/lib/person.js +17 -0
- package/dist/lib/platform.d.ts +4 -0
- package/dist/lib/platform.js +12 -0
- package/dist/lib/sole-provider.d.ts +35 -0
- package/dist/lib/sole-provider.js +52 -0
- package/dist/styles.css +64 -0
- package/dist/tokens.css +231 -0
- package/dist/tokens.generated.d.ts +219 -0
- package/dist/tokens.generated.js +679 -0
- package/dist/tokens.json +677 -0
- package/package.json +82 -4
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { Check, Copy } from 'lucide-react';
|
|
5
|
+
import { cn } from '../../lib/cn.js';
|
|
6
|
+
import { Button } from '../ui/button.js';
|
|
7
|
+
export const CopyField = React.forwardRef(function CopyField({ value, secret = false, label, onCopied, className, ...props }, ref) {
|
|
8
|
+
const id = React.useId();
|
|
9
|
+
const [copied, setCopied] = React.useState(false);
|
|
10
|
+
React.useEffect(() => {
|
|
11
|
+
if (!copied)
|
|
12
|
+
return;
|
|
13
|
+
const t = setTimeout(() => setCopied(false), 2000);
|
|
14
|
+
return () => clearTimeout(t);
|
|
15
|
+
}, [copied]);
|
|
16
|
+
const copy = async () => {
|
|
17
|
+
try {
|
|
18
|
+
await navigator.clipboard.writeText(value);
|
|
19
|
+
setCopied(true);
|
|
20
|
+
onCopied?.();
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
// The clipboard can be refused (an insecure context, a denied permission). The
|
|
24
|
+
// value stays selectable, so select it for a manual copy.
|
|
25
|
+
const el = document.getElementById(id);
|
|
26
|
+
if (el)
|
|
27
|
+
window.getSelection()?.selectAllChildren(el);
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
return (_jsxs("div", { ref: ref, "data-copy-field": secret ? 'secret' : 'plain', className: cn('flex h-10 min-w-0 items-center gap-2 rounded-lg border border-input bg-field pr-[5px] pl-3', className), ...props, children: [_jsx("span", { id: id, className: cn('min-w-0 flex-1 truncate text-foreground select-all', secret ? 'font-mono text-[15px] leading-5' : 'text-body'), children: value }), _jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: copy, className: "gap-2 bg-background px-[11px] shadow-none", "aria-label": `Copy ${label.toLocaleLowerCase('en-AU')}`, children: [copied ? _jsx(Check, { "aria-hidden": "true", className: "size-4 text-success" }) : _jsx(Copy, { "aria-hidden": "true", className: "size-4" }), copied ? 'Copied' : 'Copy'] }), _jsx("span", { role: "status", className: "sr-only", children: copied ? `${label} copied` : '' })] }));
|
|
31
|
+
});
|
|
32
|
+
CopyField.displayName = 'CopyField';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
export interface CountBadgeProps extends React.ComponentPropsWithoutRef<'span'> {
|
|
3
|
+
children: React.ReactNode;
|
|
4
|
+
variant?: 'tint' | 'muted';
|
|
5
|
+
/** The accessible text, e.g. "2 unread mentions". */
|
|
6
|
+
label?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare const CountBadge: React.ForwardRefExoticComponent<CountBadgeProps & React.RefAttributes<HTMLSpanElement>>;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { cn } from '../../lib/cn.js';
|
|
5
|
+
export const CountBadge = React.forwardRef(function CountBadge({ children, variant = 'tint', label, className, ...props }, ref) {
|
|
6
|
+
return (_jsx("span", { ref: ref, "aria-label": label, title: label, className: cn('tnum inline-flex h-[18px] min-w-[18px] shrink-0 items-center justify-center rounded-full px-[5px] text-[11px] leading-none font-semibold', variant === 'tint' && 'bg-accent text-accent-foreground', variant === 'muted' && 'bg-muted text-subtle-foreground', className), ...props, children: children }));
|
|
7
|
+
});
|
|
8
|
+
CountBadge.displayName = 'CountBadge';
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
export interface FieldProps extends Omit<React.ComponentPropsWithoutRef<'div'>, 'children'> {
|
|
3
|
+
label: React.ReactNode;
|
|
4
|
+
hint?: React.ReactNode;
|
|
5
|
+
/** "Could not ..." sentence; replaces the hint and marks the control invalid. */
|
|
6
|
+
error?: React.ReactNode;
|
|
7
|
+
/** One control: an Input, Textarea, FieldSelectButton or the like. */
|
|
8
|
+
children: React.ReactElement<{
|
|
9
|
+
id?: string;
|
|
10
|
+
'aria-describedby'?: string;
|
|
11
|
+
'aria-invalid'?: boolean;
|
|
12
|
+
}>;
|
|
13
|
+
/** The control's id, when the caller needs to know it. Otherwise the control's own, or a generated one. */
|
|
14
|
+
id?: string;
|
|
15
|
+
}
|
|
16
|
+
export declare const Field: React.ForwardRefExoticComponent<FieldProps & React.RefAttributes<HTMLDivElement>>;
|
|
17
|
+
export interface ReadOnlyFieldProps extends Omit<React.ComponentPropsWithoutRef<'div'>, 'children'> {
|
|
18
|
+
label: React.ReactNode;
|
|
19
|
+
value: React.ReactNode;
|
|
20
|
+
/** Why it cannot change: "Set by your Google account". */
|
|
21
|
+
note?: React.ReactNode;
|
|
22
|
+
}
|
|
23
|
+
/** Field/Read only (80.3 Email): the label, then the value and why it cannot change. */
|
|
24
|
+
export declare const ReadOnlyField: React.ForwardRefExoticComponent<ReadOnlyFieldProps & React.RefAttributes<HTMLDivElement>>;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { cn } from '../../lib/cn.js';
|
|
5
|
+
import { InlineError } from './banner.js';
|
|
6
|
+
export const Field = React.forwardRef(function Field({ label, hint, error, children, className, id: idProp, ...props }, ref) {
|
|
7
|
+
const auto = React.useId();
|
|
8
|
+
const own = React.isValidElement(children) ? children.props : undefined;
|
|
9
|
+
const id = idProp ?? own?.id ?? auto;
|
|
10
|
+
const messageId = `${id}-message`;
|
|
11
|
+
// Keeps what the control already points at (a password's rule list, say), adds the message.
|
|
12
|
+
const describedBy = [own?.['aria-describedby'], hint || error ? messageId : undefined].filter(Boolean).join(' ') || undefined;
|
|
13
|
+
const control = React.isValidElement(children)
|
|
14
|
+
? React.cloneElement(children, {
|
|
15
|
+
id,
|
|
16
|
+
'aria-describedby': describedBy,
|
|
17
|
+
'aria-invalid': error ? true : own?.['aria-invalid'],
|
|
18
|
+
})
|
|
19
|
+
: children;
|
|
20
|
+
return (_jsxs("div", { ref: ref, className: cn('flex min-w-0 flex-col gap-1.5', className), ...props, children: [_jsx("label", { htmlFor: id, className: "text-label text-foreground", children: label }), control, error ? (_jsx(InlineError, { id: messageId, children: error })) : (hint && (_jsx("p", { id: messageId, className: "text-label font-normal text-subtle-foreground", children: hint })))] }));
|
|
21
|
+
});
|
|
22
|
+
Field.displayName = 'Field';
|
|
23
|
+
/** Field/Read only (80.3 Email): the label, then the value and why it cannot change. */
|
|
24
|
+
export const ReadOnlyField = React.forwardRef(function ReadOnlyField({ label, value, note, className, ...props }, ref) {
|
|
25
|
+
return (_jsxs("div", { ref: ref, className: cn('min-w-0', className), ...props, children: [_jsx("p", { className: "text-label text-foreground", children: label }), _jsxs("p", { className: "mt-1 text-body text-foreground", children: [value, note && (_jsxs("span", { className: "text-subtle-foreground", children: [_jsx("span", { "aria-hidden": "true", className: "px-2.5", children: "\u00B7" }), note] }))] })] }));
|
|
26
|
+
});
|
|
27
|
+
ReadOnlyField.displayName = 'ReadOnlyField';
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Filter chip: 26px, an outline, a 14px icon and 12/500 text. Active is the accent fill
|
|
4
|
+
* with a blue border and blue text, and an x that clears it (50.2 "Assignee: Ava").
|
|
5
|
+
* It is the trigger for its picker, so it forwards its ref and props to the button.
|
|
6
|
+
*/
|
|
7
|
+
export declare const FilterChip: React.ForwardRefExoticComponent<Omit<Omit<React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, "ref">, "children"> & {
|
|
8
|
+
icon?: React.ReactNode;
|
|
9
|
+
label: React.ReactNode;
|
|
10
|
+
active?: boolean;
|
|
11
|
+
onClear?: () => void;
|
|
12
|
+
clearLabel?: string;
|
|
13
|
+
} & React.RefAttributes<HTMLButtonElement>>;
|
|
14
|
+
/** Chip/Removable (90.7 allowed domains): 26px neutral chip with an x. */
|
|
15
|
+
export declare const RemovableChip: React.ForwardRefExoticComponent<Omit<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>, "ref">, "children"> & {
|
|
16
|
+
children: React.ReactNode;
|
|
17
|
+
onRemove: () => void;
|
|
18
|
+
removeLabel: string;
|
|
19
|
+
} & React.RefAttributes<HTMLSpanElement>>;
|
|
20
|
+
export type BoardView = 'list' | 'kanban';
|
|
21
|
+
/** View switch (50.1, 50.3): List and Kanban on a muted track; the current one is white. */
|
|
22
|
+
export declare const ViewSwitch: React.ForwardRefExoticComponent<Omit<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref">, "aria-label" | "children" | "onChange" | "role"> & {
|
|
23
|
+
value: BoardView;
|
|
24
|
+
onChange: (view: BoardView) => void;
|
|
25
|
+
} & React.RefAttributes<HTMLDivElement>>;
|
|
26
|
+
/**
|
|
27
|
+
* Field/Search (60.1 "Search pages", 80.1): a 32px field with a search icon and the "/"
|
|
28
|
+
* hint. The "/" key is the screen's to wire (focus()), through the forwarded ref.
|
|
29
|
+
*/
|
|
30
|
+
export declare const SearchField: React.ForwardRefExoticComponent<Omit<Omit<React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>, "ref">, "type"> & {
|
|
31
|
+
shortcut?: string | null;
|
|
32
|
+
wrapperClassName?: string;
|
|
33
|
+
} & React.RefAttributes<HTMLInputElement>>;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { List, Search, SquareKanban, X } from 'lucide-react';
|
|
5
|
+
import { cn } from '../../lib/cn.js';
|
|
6
|
+
import { Kbd } from './kbd.js';
|
|
7
|
+
// Filters and switches (02-components.png 02.3; 50.1 to 50.3, 60.1, 90.7).
|
|
8
|
+
/**
|
|
9
|
+
* Filter chip: 26px, an outline, a 14px icon and 12/500 text. Active is the accent fill
|
|
10
|
+
* with a blue border and blue text, and an x that clears it (50.2 "Assignee: Ava").
|
|
11
|
+
* It is the trigger for its picker, so it forwards its ref and props to the button.
|
|
12
|
+
*/
|
|
13
|
+
export const FilterChip = React.forwardRef(function FilterChip({ icon, label, active = false, onClear, clearLabel, className, ...props }, ref) {
|
|
14
|
+
return (_jsxs("span", { "data-filter-chip": active ? 'active' : 'default', className: cn('inline-flex h-[26px] shrink-0 items-center rounded-md border text-label transition-colors duration-[120ms]', active ? 'border-brand bg-accent text-accent-foreground' : 'border-border bg-card text-secondary-foreground hover:bg-row-hover', className), children: [_jsxs("button", { ref: ref, type: "button", "aria-pressed": active, className: cn('inline-flex h-full min-w-0 items-center gap-1.5 rounded-md pl-2 outline-none focus-visible:shadow-[var(--focus-ring)] [&_svg]:size-3.5 [&_svg]:shrink-0', active && onClear ? 'pr-1' : 'pr-2.5', active ? '[&_svg]:text-accent-foreground' : '[&_svg]:text-muted-foreground'), ...props, children: [icon, _jsx("span", { className: "truncate", children: label })] }), active && onClear && (_jsx("button", { type: "button", onClick: onClear, "aria-label": clearLabel ?? `Clear ${typeof label === 'string' ? label : 'this filter'}`, className: "mr-[3px] inline-flex size-4 items-center justify-center rounded-sm text-accent-foreground outline-none hover:bg-brand/10 focus-visible:shadow-[var(--focus-ring)]", children: _jsx(X, { className: "size-3", strokeWidth: 2, "aria-hidden": "true" }) }))] }));
|
|
15
|
+
});
|
|
16
|
+
FilterChip.displayName = 'FilterChip';
|
|
17
|
+
/** Chip/Removable (90.7 allowed domains): 26px neutral chip with an x. */
|
|
18
|
+
export const RemovableChip = React.forwardRef(function RemovableChip({ children, onRemove, removeLabel, className, ...props }, ref) {
|
|
19
|
+
return (_jsxs("span", { ref: ref, className: cn('inline-flex h-6 shrink-0 items-center gap-1 rounded-md bg-muted pr-[3px] pl-2.5 text-label text-foreground', className), ...props, children: [_jsx("span", { className: "truncate", children: children }), _jsx("button", { type: "button", onClick: onRemove, "aria-label": removeLabel, className: "inline-flex size-[18px] items-center justify-center rounded-sm text-muted-foreground outline-none hover:bg-sidebar-hover hover:text-foreground focus-visible:shadow-[var(--focus-ring)]", children: _jsx(X, { className: "size-3", strokeWidth: 2, "aria-hidden": "true" }) })] }));
|
|
20
|
+
});
|
|
21
|
+
RemovableChip.displayName = 'RemovableChip';
|
|
22
|
+
/** View switch (50.1, 50.3): List and Kanban on a muted track; the current one is white. */
|
|
23
|
+
export const ViewSwitch = React.forwardRef(function ViewSwitch({ value, onChange, className, ...props }, ref) {
|
|
24
|
+
const items = [
|
|
25
|
+
{ value: 'list', label: 'List', Icon: List },
|
|
26
|
+
{ value: 'kanban', label: 'Kanban', Icon: SquareKanban },
|
|
27
|
+
];
|
|
28
|
+
return (_jsx("div", { ref: ref, ...props, role: "radiogroup", "aria-label": "View", className: cn('inline-flex h-[30px] shrink-0 items-center rounded-lg bg-muted p-0.5', className), children: items.map(({ value: v, label, Icon }) => {
|
|
29
|
+
const on = v === value;
|
|
30
|
+
return (_jsxs("button", { type: "button", "data-view": v, role: "radio", "aria-checked": on, onClick: () => !on && onChange(v), onKeyDown: (e) => {
|
|
31
|
+
if (e.key === 'ArrowRight' || e.key === 'ArrowLeft' || e.key === 'ArrowDown' || e.key === 'ArrowUp') {
|
|
32
|
+
e.preventDefault();
|
|
33
|
+
const next = value === 'list' ? 'kanban' : 'list';
|
|
34
|
+
e.currentTarget.parentElement?.querySelector(`[data-view="${next}"]`)?.focus();
|
|
35
|
+
onChange(next);
|
|
36
|
+
}
|
|
37
|
+
}, tabIndex: on ? 0 : -1, className: cn('inline-flex h-[26px] items-center gap-1.5 rounded-md px-2.5 text-label outline-none transition-colors duration-[120ms] focus-visible:shadow-[var(--focus-ring)] [&_svg]:size-3.5', on ? 'bg-card text-foreground shadow-sm [&_svg]:text-foreground' : 'text-muted-foreground hover:text-foreground [&_svg]:text-muted-foreground'), children: [_jsx(Icon, { "aria-hidden": "true", strokeWidth: 1.8 }), label] }, v));
|
|
38
|
+
}) }));
|
|
39
|
+
});
|
|
40
|
+
ViewSwitch.displayName = 'ViewSwitch';
|
|
41
|
+
/**
|
|
42
|
+
* Field/Search (60.1 "Search pages", 80.1): a 32px field with a search icon and the "/"
|
|
43
|
+
* hint. The "/" key is the screen's to wire (focus()), through the forwarded ref.
|
|
44
|
+
*/
|
|
45
|
+
export const SearchField = React.forwardRef(function SearchField({ shortcut = '/', wrapperClassName, className, ...props }, ref) {
|
|
46
|
+
return (_jsxs("label", { className: cn('flex h-8 min-w-0 cursor-text items-center gap-2 rounded-lg border border-input bg-field pr-2 pl-2.5 transition-[color,box-shadow] duration-[120ms] focus-within:border-ring focus-within:shadow-[var(--focus-ring)]', wrapperClassName), children: [_jsx(Search, { "aria-hidden": "true", strokeWidth: 1.8, className: "size-3.5 shrink-0 text-subtle-foreground" }), _jsx("input", { ref: ref, type: "search", className: cn('h-full min-w-0 flex-1 bg-transparent text-body text-foreground outline-none placeholder:text-subtle-foreground [&::-webkit-search-cancel-button]:hidden', className), ...props }), shortcut && _jsx(Kbd, { keys: [shortcut], className: "bg-muted" })] }));
|
|
47
|
+
});
|
|
48
|
+
SearchField.displayName = 'SearchField';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export * from './avatar.tsx';
|
|
2
|
+
export * from './banner.tsx';
|
|
3
|
+
export * from './column-header.tsx';
|
|
4
|
+
export * from './copy-field.tsx';
|
|
5
|
+
export * from './count-badge.tsx';
|
|
6
|
+
export * from './field.tsx';
|
|
7
|
+
export * from './filters.tsx';
|
|
8
|
+
export * from './kbd.tsx';
|
|
9
|
+
export * from './label-tag.tsx';
|
|
10
|
+
export * from './layout.tsx';
|
|
11
|
+
export * from './logo.tsx';
|
|
12
|
+
export * from './nav.tsx';
|
|
13
|
+
export * from './person-chip.tsx';
|
|
14
|
+
export * from './person-header.tsx';
|
|
15
|
+
export * from './pill.tsx';
|
|
16
|
+
export * from './states.tsx';
|
|
17
|
+
export * from './table.tsx';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export * from './avatar.js';
|
|
2
|
+
export * from './banner.js';
|
|
3
|
+
export * from './column-header.js';
|
|
4
|
+
export * from './copy-field.js';
|
|
5
|
+
export * from './count-badge.js';
|
|
6
|
+
export * from './field.js';
|
|
7
|
+
export * from './filters.js';
|
|
8
|
+
export * from './kbd.js';
|
|
9
|
+
export * from './label-tag.js';
|
|
10
|
+
export * from './layout.js';
|
|
11
|
+
export * from './logo.js';
|
|
12
|
+
export * from './nav.js';
|
|
13
|
+
export * from './person-chip.js';
|
|
14
|
+
export * from './person-header.js';
|
|
15
|
+
export * from './pill.js';
|
|
16
|
+
export * from './states.js';
|
|
17
|
+
export * from './table.js';
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
export interface KbdProps extends Omit<React.ComponentPropsWithoutRef<'kbd'>, 'children'> {
|
|
3
|
+
keys: readonly string[];
|
|
4
|
+
}
|
|
5
|
+
export declare const Kbd: React.ForwardRefExoticComponent<KbdProps & React.RefAttributes<HTMLElement>>;
|
|
6
|
+
/** The same hint as words: "Cmd+K", or "Cmd K" with a space, as tooltips show it. */
|
|
7
|
+
export declare function keysText(keys: readonly string[], separator?: string): string;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { cn } from '../../lib/cn.js';
|
|
5
|
+
import { modKeyName } from '../../lib/platform.js';
|
|
6
|
+
export const Kbd = React.forwardRef(function Kbd({ keys, className, ...props }, ref) {
|
|
7
|
+
const text = keys.map((k) => (k === 'mod' ? modKeyName() : k)).join(' ');
|
|
8
|
+
return (_jsx("kbd", { ref: ref, className: cn('inline-flex h-[18px] min-w-[18px] shrink-0 items-center justify-center rounded-sm border border-border bg-muted px-1 font-sans text-[11px] leading-none font-medium text-muted-foreground', className), ...props, children: text }));
|
|
9
|
+
});
|
|
10
|
+
Kbd.displayName = 'Kbd';
|
|
11
|
+
/** The same hint as words: "Cmd+K", or "Cmd K" with a space, as tooltips show it. */
|
|
12
|
+
export function keysText(keys, separator = '+') {
|
|
13
|
+
return keys.map((k) => (k === 'mod' ? modKeyName() : k)).join(separator);
|
|
14
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import type { Hue } from '../../lib/hue.ts';
|
|
3
|
+
export interface LabelTagProps extends Omit<React.ComponentPropsWithoutRef<'span'>, 'children'> {
|
|
4
|
+
name: string;
|
|
5
|
+
hue: Hue;
|
|
6
|
+
}
|
|
7
|
+
export declare const LabelTag: React.ForwardRefExoticComponent<LabelTagProps & React.RefAttributes<HTMLSpanElement>>;
|
|
8
|
+
export interface LabelTagsProps extends Omit<React.ComponentPropsWithoutRef<'span'>, 'children'> {
|
|
9
|
+
labels: Array<{
|
|
10
|
+
id: string;
|
|
11
|
+
name: string;
|
|
12
|
+
hue: Hue;
|
|
13
|
+
}>;
|
|
14
|
+
/** How many labels to show before "+n". */
|
|
15
|
+
max?: number;
|
|
16
|
+
}
|
|
17
|
+
/** Up to `max` labels, then "+n" in the same shape. */
|
|
18
|
+
export declare const LabelTags: React.ForwardRefExoticComponent<LabelTagsProps & React.RefAttributes<HTMLSpanElement>>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { cn } from '../../lib/cn.js';
|
|
5
|
+
export const LabelTag = React.forwardRef(function LabelTag({ name, hue, className, ...props }, ref) {
|
|
6
|
+
return (_jsxs("span", { ref: ref, className: cn(`hue-${hue}`, 'inline-flex h-5 max-w-40 shrink-0 items-center gap-[5px] rounded-md border border-tag-border bg-tag-bg px-[7px] text-[11.5px] leading-none font-medium text-tag-fg', className), ...props, children: [_jsx("span", { "aria-hidden": "true", className: "size-1 shrink-0 rounded-full bg-tag-dot" }), _jsx("span", { className: "truncate", children: name })] }));
|
|
7
|
+
});
|
|
8
|
+
LabelTag.displayName = 'LabelTag';
|
|
9
|
+
/** Up to `max` labels, then "+n" in the same shape. */
|
|
10
|
+
export const LabelTags = React.forwardRef(function LabelTags({ labels, max = 2, className, ...props }, ref) {
|
|
11
|
+
if (!labels.length)
|
|
12
|
+
return null;
|
|
13
|
+
const shown = labels.slice(0, max);
|
|
14
|
+
const extra = labels.length - shown.length;
|
|
15
|
+
return (_jsxs("span", { ref: ref, className: cn('inline-flex min-w-0 items-center gap-1', className), ...props, children: [shown.map((l) => (_jsx(LabelTag, { name: l.name, hue: l.hue }, l.id))), extra > 0 && (_jsxs("span", { title: labels.slice(max).map((l) => l.name).join(', '), className: "inline-flex h-5 shrink-0 items-center rounded-md border border-border px-1.5 text-[11.5px] font-medium text-muted-foreground", children: ["+", extra] }))] }));
|
|
16
|
+
});
|
|
17
|
+
LabelTags.displayName = 'LabelTags';
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
/** Page header (60.1, 80.1, 90.7): a 20/28 title, one 13px line under it, actions on the right. */
|
|
3
|
+
export declare const PageHeader: React.ForwardRefExoticComponent<Omit<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>, "ref">, "children" | "title"> & {
|
|
4
|
+
title: React.ReactNode;
|
|
5
|
+
description?: React.ReactNode;
|
|
6
|
+
actions?: React.ReactNode;
|
|
7
|
+
as?: 'h1' | 'h2';
|
|
8
|
+
} & React.RefAttributes<HTMLElement>>;
|
|
9
|
+
/** Section label (30.1 "RECENTLY VIEWED", 60.5 "COMMENTS 2"): 11/600 uppercase, a count, an action on the right. */
|
|
10
|
+
export declare const SectionLabel: React.ForwardRefExoticComponent<Omit<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref">, "children"> & {
|
|
11
|
+
children: React.ReactNode;
|
|
12
|
+
count?: number;
|
|
13
|
+
/** A link-style button: "New sticky", "Show resolved". */
|
|
14
|
+
action?: React.ReactNode;
|
|
15
|
+
as?: 'h2' | 'h3' | 'div';
|
|
16
|
+
} & React.RefAttributes<HTMLDivElement>>;
|
|
17
|
+
/** The quiet link-style action a SectionLabel carries: 12/500 in the link colour. */
|
|
18
|
+
export declare const SectionAction: React.ForwardRefExoticComponent<Omit<React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, "ref"> & React.RefAttributes<HTMLButtonElement>>;
|
|
19
|
+
/** Card/Section (90.7, 90.8, 50.11): a bordered 12px card, a 14/600 title, a 13px line, then the body. */
|
|
20
|
+
export declare const SectionCard: React.ForwardRefExoticComponent<Omit<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>, "ref">, "title"> & {
|
|
21
|
+
title: React.ReactNode;
|
|
22
|
+
description?: React.ReactNode;
|
|
23
|
+
actions?: React.ReactNode;
|
|
24
|
+
} & React.RefAttributes<HTMLElement>>;
|
|
25
|
+
/** List card (30.1 My work, 80.2 Open work): a 12px card with a 42px header, rows below. */
|
|
26
|
+
export declare const ListCard: React.ForwardRefExoticComponent<Omit<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>, "ref">, "title"> & {
|
|
27
|
+
title: React.ReactNode;
|
|
28
|
+
/** "9 open", or a number. */
|
|
29
|
+
count?: React.ReactNode;
|
|
30
|
+
/** On the right of the header: "Assigned to you, not done". */
|
|
31
|
+
meta?: React.ReactNode;
|
|
32
|
+
footer?: React.ReactNode;
|
|
33
|
+
children: React.ReactNode;
|
|
34
|
+
} & React.RefAttributes<HTMLElement>>;
|
|
35
|
+
/** Property row (50.6 side panel): a 96px 13px label, then the value, 36px tall. */
|
|
36
|
+
export declare const PropertyRow: React.ForwardRefExoticComponent<Omit<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref">, "children" | "title"> & {
|
|
37
|
+
label: React.ReactNode;
|
|
38
|
+
/** The whole label, shown on hover when it is cut to the column. */
|
|
39
|
+
title?: string;
|
|
40
|
+
/** Before the label, inside the same 96px column: 14px, decorative. */
|
|
41
|
+
icon?: React.ReactNode;
|
|
42
|
+
children: React.ReactNode;
|
|
43
|
+
} & React.RefAttributes<HTMLDivElement>>;
|
|
44
|
+
/**
|
|
45
|
+
* Table header (90.1 Access, 80.1 People): 32px, the muted fill, 12/500 labels. Give it
|
|
46
|
+
* the same grid template as the rows, e.g. `className="grid-cols-[1fr_140px_180px_160px_40px]"`.
|
|
47
|
+
* A column is its label, or `{ id, label }` for one with no words (the row menu).
|
|
48
|
+
*/
|
|
49
|
+
export declare const TableHeader: React.ForwardRefExoticComponent<Omit<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref">, "children"> & {
|
|
50
|
+
columns: ReadonlyArray<string | {
|
|
51
|
+
id: string;
|
|
52
|
+
label?: React.ReactNode;
|
|
53
|
+
}>;
|
|
54
|
+
} & React.RefAttributes<HTMLDivElement>>;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { cn } from '../../lib/cn.js';
|
|
5
|
+
// Page parts (02-components.png 02.5, 02.9): the page header, the uppercase section
|
|
6
|
+
// label, the section card, the list card, property rows and table headers.
|
|
7
|
+
/** Page header (60.1, 80.1, 90.7): a 20/28 title, one 13px line under it, actions on the right. */
|
|
8
|
+
export const PageHeader = React.forwardRef(function PageHeader({ title, description, actions, as: Heading = 'h1', className, ...props }, ref) {
|
|
9
|
+
return (_jsxs("header", { ref: ref, className: cn('flex min-w-0 items-start gap-4', className), ...props, children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsx(Heading, { className: "text-page text-foreground", children: title }), description && _jsx("p", { className: "text-body text-subtle-foreground", children: description })] }), actions && _jsx("div", { className: "flex shrink-0 items-center gap-2", children: actions })] }));
|
|
10
|
+
});
|
|
11
|
+
PageHeader.displayName = 'PageHeader';
|
|
12
|
+
/** Section label (30.1 "RECENTLY VIEWED", 60.5 "COMMENTS 2"): 11/600 uppercase, a count, an action on the right. */
|
|
13
|
+
export const SectionLabel = React.forwardRef(function SectionLabel({ children, count, action, as: Heading = 'h2', className, ...props }, ref) {
|
|
14
|
+
return (_jsxs("div", { ref: ref, className: cn('flex h-6 min-w-0 items-center gap-2', className), ...props, children: [_jsx(Heading, { className: "text-micro text-subtle-foreground", children: children }), count !== undefined && _jsx("span", { className: "tnum text-label font-normal text-subtle-foreground", children: count }), action && _jsx("div", { className: "ml-auto flex shrink-0 items-center", children: action })] }));
|
|
15
|
+
});
|
|
16
|
+
SectionLabel.displayName = 'SectionLabel';
|
|
17
|
+
/** The quiet link-style action a SectionLabel carries: 12/500 in the link colour. */
|
|
18
|
+
export const SectionAction = React.forwardRef(function SectionAction({ className, ...props }, ref) {
|
|
19
|
+
return (_jsx("button", { ref: ref, type: "button", className: cn('rounded-sm text-label text-link outline-none hover:underline focus-visible:shadow-[var(--focus-ring)] disabled:opacity-50', className), ...props }));
|
|
20
|
+
});
|
|
21
|
+
SectionAction.displayName = 'SectionAction';
|
|
22
|
+
/** Card/Section (90.7, 90.8, 50.11): a bordered 12px card, a 14/600 title, a 13px line, then the body. */
|
|
23
|
+
export const SectionCard = React.forwardRef(function SectionCard({ title, description, actions, children, className, ...props }, ref) {
|
|
24
|
+
return (_jsxs("section", { ref: ref, className: cn('rounded-xl border border-border bg-card px-5 pt-[18px] pb-5', className), ...props, children: [_jsxs("div", { className: "flex min-w-0 items-start gap-4", children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("h2", { className: "text-strong text-foreground", children: title }), description && _jsx("p", { className: "mt-0.5 text-body text-muted-foreground", children: description })] }), actions && _jsx("div", { className: "flex shrink-0 items-center gap-2", children: actions })] }), children && _jsx("div", { className: "mt-4", children: children })] }));
|
|
25
|
+
});
|
|
26
|
+
SectionCard.displayName = 'SectionCard';
|
|
27
|
+
/** List card (30.1 My work, 80.2 Open work): a 12px card with a 42px header, rows below. */
|
|
28
|
+
export const ListCard = React.forwardRef(function ListCard({ title, count, meta, children, footer, className, ...props }, ref) {
|
|
29
|
+
return (_jsxs("section", { ref: ref, className: cn('overflow-hidden rounded-xl border border-border bg-card', className), ...props, children: [_jsxs("header", { className: "flex h-[43px] min-w-0 items-center gap-2 border-b border-border px-4", children: [_jsx("h2", { className: "text-strong text-foreground", children: title }), count !== undefined && _jsx("span", { className: "tnum text-label font-normal text-subtle-foreground", children: count }), meta && _jsx("span", { className: "ml-auto truncate text-label font-normal text-subtle-foreground", children: meta })] }), _jsx("div", { className: "[&>*:last-child]:border-b-0", children: children }), footer && _jsx("div", { className: "border-t border-hairline px-4 py-2.5", children: footer })] }));
|
|
30
|
+
});
|
|
31
|
+
ListCard.displayName = 'ListCard';
|
|
32
|
+
/** Property row (50.6 side panel): a 96px 13px label, then the value, 36px tall. */
|
|
33
|
+
export const PropertyRow = React.forwardRef(function PropertyRow({ label, title, icon, children, className, ...props }, ref) {
|
|
34
|
+
return (_jsxs("div", { ref: ref, className: cn('flex min-h-9 min-w-0 items-center', className), ...props, children: [_jsx("div", { title: title, className: cn('w-24 shrink-0 pr-2 text-body text-subtle-foreground', icon ? 'flex items-center gap-1.5' : 'truncate'), children: icon ? (_jsxs(_Fragment, { children: [_jsx("span", { "aria-hidden": "true", className: "flex shrink-0 items-center [&_svg]:size-3.5 [&_svg]:shrink-0", children: icon }), _jsx("span", { className: "min-w-0 truncate", children: label })] })) : (label) }), _jsx("div", { className: "flex min-w-0 flex-1 items-center gap-1.5", children: children })] }));
|
|
35
|
+
});
|
|
36
|
+
PropertyRow.displayName = 'PropertyRow';
|
|
37
|
+
/**
|
|
38
|
+
* Table header (90.1 Access, 80.1 People): 32px, the muted fill, 12/500 labels. Give it
|
|
39
|
+
* the same grid template as the rows, e.g. `className="grid-cols-[1fr_140px_180px_160px_40px]"`.
|
|
40
|
+
* A column is its label, or `{ id, label }` for one with no words (the row menu).
|
|
41
|
+
*/
|
|
42
|
+
export const TableHeader = React.forwardRef(function TableHeader({ columns, className, ...props }, ref) {
|
|
43
|
+
return (_jsx("div", { ref: ref, ...props, role: "row", className: cn('grid h-8 items-center gap-4 bg-muted px-4 text-label text-subtle-foreground', className), children: columns.map((c) => {
|
|
44
|
+
const col = typeof c === 'string' ? { id: c, label: c } : c;
|
|
45
|
+
return (_jsx("div", { role: "columnheader", className: "min-w-0 truncate", children: col.label }, col.id));
|
|
46
|
+
}) }));
|
|
47
|
+
});
|
|
48
|
+
TableHeader.displayName = 'TableHeader';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
export interface SoleMarkProps extends Omit<React.ComponentPropsWithoutRef<'img'>, 'width' | 'height'> {
|
|
3
|
+
/** Width and height in px. */
|
|
4
|
+
size?: number;
|
|
5
|
+
}
|
|
6
|
+
export declare const SoleMark: React.ForwardRefExoticComponent<SoleMarkProps & React.RefAttributes<HTMLImageElement>>;
|
|
7
|
+
export interface SoleLockupProps extends Omit<React.ComponentPropsWithoutRef<'span'>, 'children'> {
|
|
8
|
+
/** The mark's size in px; the wordmark follows it. */
|
|
9
|
+
size?: number;
|
|
10
|
+
/** The product's name beside the mark. */
|
|
11
|
+
name?: string;
|
|
12
|
+
}
|
|
13
|
+
export declare const SoleLockup: React.ForwardRefExoticComponent<SoleLockupProps & React.RefAttributes<HTMLSpanElement>>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { cn } from '../../lib/cn.js';
|
|
5
|
+
// The Sole mark and the lockup (brand-and-ux 1.2). Indigo lives in the mark only; the
|
|
6
|
+
// interface accent is blue (D31). The mark is a PNG until a vector exists. HQ serves it
|
|
7
|
+
// from /mark.png; the package carries the same 202px image inline, so a consumer needs no
|
|
8
|
+
// file in its public folder. Pass `src` to use a file instead.
|
|
9
|
+
const MARK_SRC = 'data:image/png;base64,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';
|
|
10
|
+
export const SoleMark = React.forwardRef(function SoleMark({ size = 20, src = MARK_SRC, className, ...props }, ref) {
|
|
11
|
+
return _jsx("img", { ref: ref, src: src, alt: "", width: size, height: size, draggable: false, className: cn('shrink-0 select-none', className), ...props });
|
|
12
|
+
});
|
|
13
|
+
SoleMark.displayName = 'SoleMark';
|
|
14
|
+
export const SoleLockup = React.forwardRef(function SoleLockup({ size = 20, name = 'Sole HQ', className, style, ...props }, ref) {
|
|
15
|
+
return (_jsxs("span", { ref: ref, className: cn('inline-flex items-center', className), style: { gap: Math.round(size * 0.3), ...style }, ...props, children: [_jsx(SoleMark, { size: size }), _jsx("span", { className: cn('font-semibold text-foreground', size >= 24 ? 'text-title' : 'text-strong tracking-[-0.01em]'), children: name })] }));
|
|
16
|
+
});
|
|
17
|
+
SoleLockup.displayName = 'SoleLockup';
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* One item. With `href` it is a link (the provider's link component) and `active` marks the
|
|
4
|
+
* current page with `aria-current`; the caller decides, so a matching address never marks
|
|
5
|
+
* an item as current that is drawn as not. Without `href` it is a button that sets state
|
|
6
|
+
* (the wiki rail's filters) and `active` is `aria-pressed`.
|
|
7
|
+
*/
|
|
8
|
+
export declare const SubNavItem: React.ForwardRefExoticComponent<{
|
|
9
|
+
icon?: React.ReactNode;
|
|
10
|
+
label: React.ReactNode;
|
|
11
|
+
count?: number | string;
|
|
12
|
+
/** An address in the app: the item is a link. */
|
|
13
|
+
href?: string;
|
|
14
|
+
active?: boolean;
|
|
15
|
+
onClick?: () => void;
|
|
16
|
+
className?: string;
|
|
17
|
+
} & React.RefAttributes<HTMLElement>>;
|
|
18
|
+
/** A column of sub-nav items with an optional 11px heading ("SPACES", "FOLDERS"). */
|
|
19
|
+
export declare const SubNav: React.ForwardRefExoticComponent<Omit<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>, "ref">, "children"> & {
|
|
20
|
+
label?: React.ReactNode;
|
|
21
|
+
children: React.ReactNode;
|
|
22
|
+
} & React.RefAttributes<HTMLElement>>;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { cn } from '../../lib/cn.js';
|
|
5
|
+
import { SoleLink } from '../../lib/sole-provider.js';
|
|
6
|
+
// Sub-navigation item (02.5; the wiki rail in 60.1, Settings and Admin nav in 80.3 and
|
|
7
|
+
// 90.1): 32px, a 16px icon, 13/500 text, a count on the right. Active is the accent fill
|
|
8
|
+
// with blue text, the same as the sidebar.
|
|
9
|
+
const base = 'group/subnav flex h-8 w-full min-w-0 items-center gap-2 rounded-md px-2.5 text-left text-body font-medium text-sidebar-foreground outline-none transition-colors duration-[120ms] [&_svg]:size-4 [&_svg]:shrink-0 [&_svg]:text-muted-foreground hover:bg-sidebar-hover focus-visible:shadow-[var(--focus-ring)]';
|
|
10
|
+
const activeClass = 'bg-accent text-accent-foreground hover:bg-accent [&_svg]:text-accent-foreground';
|
|
11
|
+
function Inner({ icon, label, count }) {
|
|
12
|
+
return (_jsxs(_Fragment, { children: [icon, _jsx("span", { className: "min-w-0 flex-1 truncate", children: label }), count !== undefined && _jsx("span", { className: "tnum shrink-0 text-label font-normal text-subtle-foreground", children: count })] }));
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* One item. With `href` it is a link (the provider's link component) and `active` marks the
|
|
16
|
+
* current page with `aria-current`; the caller decides, so a matching address never marks
|
|
17
|
+
* an item as current that is drawn as not. Without `href` it is a button that sets state
|
|
18
|
+
* (the wiki rail's filters) and `active` is `aria-pressed`.
|
|
19
|
+
*/
|
|
20
|
+
export const SubNavItem = React.forwardRef(function SubNavItem({ icon, label, count, href, active, onClick, className }, ref) {
|
|
21
|
+
// One ref type for both elements; each branch narrows it to the element it renders.
|
|
22
|
+
if (href) {
|
|
23
|
+
return (_jsx(SoleLink, { ref: ref, href: href, onClick: onClick, className: cn(base, active && activeClass, className), "aria-current": active ? 'page' : undefined, children: _jsx(Inner, { icon: icon, label: label, count: count }) }));
|
|
24
|
+
}
|
|
25
|
+
return (_jsx("button", { ref: ref, type: "button", onClick: onClick, "aria-pressed": active, className: cn(base, active && activeClass, className), children: _jsx(Inner, { icon: icon, label: label, count: count }) }));
|
|
26
|
+
});
|
|
27
|
+
SubNavItem.displayName = 'SubNavItem';
|
|
28
|
+
/** A column of sub-nav items with an optional 11px heading ("SPACES", "FOLDERS"). */
|
|
29
|
+
export const SubNav = React.forwardRef(function SubNav({ label, children, className, ...props }, ref) {
|
|
30
|
+
return (_jsxs("nav", { ref: ref, "aria-label": typeof label === 'string' ? label : undefined, className: cn('flex flex-col gap-0.5', className), ...props, children: [label && _jsx("div", { className: "flex h-7 items-end px-2.5 pb-1.5 text-micro text-subtle-foreground", children: label }), children] }));
|
|
31
|
+
});
|
|
32
|
+
SubNav.displayName = 'SubNav';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { type SolePerson } from '../../lib/person.ts';
|
|
3
|
+
export interface PersonChipProps extends Omit<React.ComponentPropsWithoutRef<'span'>, 'children'> {
|
|
4
|
+
person: SolePerson | null | undefined;
|
|
5
|
+
/** Set false for a chip that must not link, e.g. inside another link. */
|
|
6
|
+
link?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export declare const PersonChip: React.ForwardRefExoticComponent<PersonChipProps & React.RefAttributes<HTMLElement>>;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { cn } from '../../lib/cn.js';
|
|
5
|
+
import { FORMER_MEMBER, personName } from '../../lib/person.js';
|
|
6
|
+
import { SoleLink, useSole } from '../../lib/sole-provider.js';
|
|
7
|
+
import { Avatar } from './avatar.js';
|
|
8
|
+
export const PersonChip = React.forwardRef(function PersonChip({ person, link = true, className, ...props }, ref) {
|
|
9
|
+
const { personHref } = useSole();
|
|
10
|
+
const name = person ? personName(person) : FORMER_MEMBER;
|
|
11
|
+
const inner = (_jsxs(_Fragment, { children: [_jsx(Avatar, { person: person, size: 20 }), _jsx("span", { className: cn('truncate', !person && 'text-subtle-foreground'), children: name })] }));
|
|
12
|
+
const base = 'inline-flex max-w-full min-w-0 items-center gap-1.5 rounded-md px-1 py-0.5 text-body font-medium';
|
|
13
|
+
const href = link && person && !person.isBot ? personHref(person) : null;
|
|
14
|
+
if (!href) {
|
|
15
|
+
return (_jsx("span", { ref: ref, className: cn(base, className), ...props, children: inner }));
|
|
16
|
+
}
|
|
17
|
+
return (_jsx(SoleLink, { ref: ref, href: href, className: cn(base, 'transition-colors duration-[120ms] hover:bg-row-hover focus-visible:shadow-[var(--focus-ring)] focus-visible:outline-none', className), ...props, children: inner }));
|
|
18
|
+
});
|
|
19
|
+
PersonChip.displayName = 'PersonChip';
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { type SolePerson } from '../../lib/person.ts';
|
|
3
|
+
export interface PersonHeaderProps extends Omit<React.ComponentPropsWithoutRef<'header'>, 'children'> {
|
|
4
|
+
person: SolePerson;
|
|
5
|
+
admin?: boolean;
|
|
6
|
+
/** "Payroll lead", the email, "Perth, 2 hours behind Sydney". Empty parts are dropped. */
|
|
7
|
+
meta?: ReadonlyArray<string | null | undefined | false>;
|
|
8
|
+
actions?: React.ReactNode;
|
|
9
|
+
}
|
|
10
|
+
export declare const PersonHeader: React.ForwardRefExoticComponent<PersonHeaderProps & React.RefAttributes<HTMLElement>>;
|
|
11
|
+
export interface AvatarRowProps extends Omit<React.ComponentPropsWithoutRef<'div'>, 'title' | 'children'> {
|
|
12
|
+
person: SolePerson;
|
|
13
|
+
title: React.ReactNode;
|
|
14
|
+
body?: React.ReactNode;
|
|
15
|
+
actions?: React.ReactNode;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Avatar row (80.3 Profile): the 64px avatar with a 13/500 line and a 12px line beside it, and
|
|
19
|
+
* the buttons that change the picture (Change, Remove) on the right.
|
|
20
|
+
*/
|
|
21
|
+
export declare const AvatarRow: React.ForwardRefExoticComponent<AvatarRowProps & React.RefAttributes<HTMLDivElement>>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { cn } from '../../lib/cn.js';
|
|
5
|
+
import { personName } from '../../lib/person.js';
|
|
6
|
+
import { Avatar } from './avatar.js';
|
|
7
|
+
import { Pill } from './pill.js';
|
|
8
|
+
const NO_META = [];
|
|
9
|
+
export const PersonHeader = React.forwardRef(function PersonHeader({ person, admin = false, meta = NO_META, actions, className, ...props }, ref) {
|
|
10
|
+
// Each part once, so it can be its own key.
|
|
11
|
+
const parts = [...new Set(meta.filter((m) => typeof m === 'string' && m !== ''))];
|
|
12
|
+
return (_jsxs("header", { ref: ref, className: cn('flex min-w-0 items-center gap-4', className), ...props, children: [_jsx(Avatar, { person: person, size: 64 }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [_jsx("h1", { className: "truncate text-page text-foreground", children: personName(person) }), admin && _jsx(Pill, { tone: "neutral", children: "Admin" })] }), parts.length > 0 && (_jsx("p", { className: "mt-px truncate text-body text-subtle-foreground", children: parts.map((part, i) => (_jsxs("span", { children: [i > 0 && (_jsx("span", { "aria-hidden": "true", className: "px-[7px]", children: "\u00B7" })), part] }, part))) }))] }), actions && _jsx("div", { className: "flex shrink-0 items-center gap-2", children: actions })] }));
|
|
13
|
+
});
|
|
14
|
+
PersonHeader.displayName = 'PersonHeader';
|
|
15
|
+
/**
|
|
16
|
+
* Avatar row (80.3 Profile): the 64px avatar with a 13/500 line and a 12px line beside it, and
|
|
17
|
+
* the buttons that change the picture (Change, Remove) on the right.
|
|
18
|
+
*/
|
|
19
|
+
export const AvatarRow = React.forwardRef(function AvatarRow({ person, title, body, actions, className, ...props }, ref) {
|
|
20
|
+
return (_jsxs("div", { ref: ref, className: cn('flex min-w-0 items-center gap-3.5', className), ...props, children: [_jsx(Avatar, { person: person, size: 64 }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("p", { className: "text-body font-medium text-foreground", children: title }), body && _jsx("p", { className: "text-label font-normal text-subtle-foreground", children: body })] }), actions && _jsx("div", { className: "flex shrink-0 items-center gap-2", children: actions })] }));
|
|
21
|
+
});
|
|
22
|
+
AvatarRow.displayName = 'AvatarRow';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
export type PillTone = 'success' | 'warning' | 'neutral' | 'info';
|
|
3
|
+
export interface PillProps extends React.ComponentPropsWithoutRef<'span'> {
|
|
4
|
+
tone?: PillTone;
|
|
5
|
+
/** A 12px icon before the text. */
|
|
6
|
+
icon?: React.ReactNode;
|
|
7
|
+
children: React.ReactNode;
|
|
8
|
+
}
|
|
9
|
+
export declare const Pill: React.ForwardRefExoticComponent<PillProps & React.RefAttributes<HTMLSpanElement>>;
|