@kennycorrea/ui 0.1.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 +153 -0
- package/dist/atoms/Avatar/Avatar.d.ts +7 -0
- package/dist/atoms/Avatar/Avatar.js +19 -0
- package/dist/atoms/Avatar/index.d.ts +1 -0
- package/dist/atoms/Avatar/index.js +1 -0
- package/dist/atoms/Badge/Badge.d.ts +7 -0
- package/dist/atoms/Badge/Badge.js +23 -0
- package/dist/atoms/Badge/index.d.ts +1 -0
- package/dist/atoms/Badge/index.js +1 -0
- package/dist/atoms/Button/Button.d.ts +2 -0
- package/dist/atoms/Button/Button.js +34 -0
- package/dist/atoms/Button/Button.types.d.ts +40 -0
- package/dist/atoms/Button/Button.types.js +1 -0
- package/dist/atoms/Button/index.d.ts +2 -0
- package/dist/atoms/Button/index.js +2 -0
- package/dist/atoms/Card/Card.d.ts +16 -0
- package/dist/atoms/Card/Card.js +22 -0
- package/dist/atoms/Card/index.d.ts +1 -0
- package/dist/atoms/Card/index.js +1 -0
- package/dist/atoms/Checkbox/Checkbox.d.ts +11 -0
- package/dist/atoms/Checkbox/Checkbox.js +14 -0
- package/dist/atoms/Checkbox/index.d.ts +1 -0
- package/dist/atoms/Checkbox/index.js +1 -0
- package/dist/atoms/ContextMenu/ContextMenu.d.ts +31 -0
- package/dist/atoms/ContextMenu/ContextMenu.js +24 -0
- package/dist/atoms/ContextMenu/index.d.ts +1 -0
- package/dist/atoms/ContextMenu/index.js +1 -0
- package/dist/atoms/Dialog/Dialog.d.ts +19 -0
- package/dist/atoms/Dialog/Dialog.js +13 -0
- package/dist/atoms/Dialog/index.d.ts +1 -0
- package/dist/atoms/Dialog/index.js +1 -0
- package/dist/atoms/Icon/Icon.d.ts +48 -0
- package/dist/atoms/Icon/Icon.js +43 -0
- package/dist/atoms/Icon/index.d.ts +1 -0
- package/dist/atoms/Icon/index.js +1 -0
- package/dist/atoms/Input/Input.d.ts +15 -0
- package/dist/atoms/Input/Input.js +11 -0
- package/dist/atoms/Input/index.d.ts +1 -0
- package/dist/atoms/Input/index.js +1 -0
- package/dist/atoms/Label/Label.d.ts +12 -0
- package/dist/atoms/Label/Label.js +14 -0
- package/dist/atoms/Label/index.d.ts +1 -0
- package/dist/atoms/Label/index.js +1 -0
- package/dist/atoms/Popover/Popover.d.ts +16 -0
- package/dist/atoms/Popover/Popover.js +11 -0
- package/dist/atoms/Popover/index.d.ts +1 -0
- package/dist/atoms/Popover/index.js +1 -0
- package/dist/atoms/Progress/Progress.d.ts +9 -0
- package/dist/atoms/Progress/Progress.js +12 -0
- package/dist/atoms/Progress/index.d.ts +1 -0
- package/dist/atoms/Progress/index.js +1 -0
- package/dist/atoms/Select/Select.d.ts +25 -0
- package/dist/atoms/Select/Select.js +16 -0
- package/dist/atoms/Select/index.d.ts +1 -0
- package/dist/atoms/Select/index.js +1 -0
- package/dist/atoms/Separator/Separator.d.ts +5 -0
- package/dist/atoms/Separator/Separator.js +6 -0
- package/dist/atoms/Separator/index.d.ts +1 -0
- package/dist/atoms/Separator/index.js +1 -0
- package/dist/atoms/Spinner/Spinner.d.ts +12 -0
- package/dist/atoms/Spinner/Spinner.js +15 -0
- package/dist/atoms/Spinner/index.d.ts +1 -0
- package/dist/atoms/Spinner/index.js +1 -0
- package/dist/atoms/Switch/Switch.d.ts +4 -0
- package/dist/atoms/Switch/Switch.js +7 -0
- package/dist/atoms/Switch/index.d.ts +1 -0
- package/dist/atoms/Switch/index.js +1 -0
- package/dist/atoms/Textarea/Textarea.d.ts +6 -0
- package/dist/atoms/Textarea/Textarea.js +7 -0
- package/dist/atoms/Textarea/index.d.ts +1 -0
- package/dist/atoms/Textarea/index.js +1 -0
- package/dist/atoms/Tooltip/Tooltip.d.ts +26 -0
- package/dist/atoms/Tooltip/Tooltip.js +25 -0
- package/dist/atoms/Tooltip/index.d.ts +1 -0
- package/dist/atoms/Tooltip/index.js +1 -0
- package/dist/atoms/index.d.ts +18 -0
- package/dist/atoms/index.js +18 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/strings.d.ts +39 -0
- package/dist/strings.js +26 -0
- package/dist/utils/cn.d.ts +3 -0
- package/dist/utils/cn.js +6 -0
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.js +2 -0
- package/dist/utils/theme.d.ts +33 -0
- package/dist/utils/theme.js +72 -0
- package/package.json +55 -0
- package/tokens.css +240 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { TextField } from '@radix-ui/themes';
|
|
3
|
+
import { forwardRef } from 'react';
|
|
4
|
+
import { cn } from '../../utils/cn';
|
|
5
|
+
const SIZES = { sm: '1', md: '2', lg: '3' };
|
|
6
|
+
export const Input = forwardRef(function Input({ size = 'md', invalid = false, className, ...props }, ref) {
|
|
7
|
+
return (_jsx(TextField.Root, { ref: ref, size: SIZES[size],
|
|
8
|
+
// Themes has no invalid variant; the red tint and the ARIA state are ours, and they
|
|
9
|
+
// have to agree — a field that only looks wrong is no use to a screen reader.
|
|
10
|
+
color: invalid ? 'red' : undefined, "aria-invalid": invalid || undefined, className: cn('w-full', className), ...props }));
|
|
11
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Input';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Input';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type ComponentPropsWithoutRef } from 'react';
|
|
2
|
+
export interface LabelProps extends Omit<ComponentPropsWithoutRef<'label'>, 'color'> {
|
|
3
|
+
required?: boolean;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Themes' `Text` rendered as a label, so field labels sit on the same type scale as
|
|
7
|
+
* everything else.
|
|
8
|
+
*
|
|
9
|
+
* The asterisk is decoration: `required` is already on the input, and a screen reader
|
|
10
|
+
* announcing "New email star" reads worse than the field simply being required.
|
|
11
|
+
*/
|
|
12
|
+
export declare const Label: import("react").ForwardRefExoticComponent<LabelProps & import("react").RefAttributes<HTMLLabelElement>>;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Text } from '@radix-ui/themes';
|
|
3
|
+
import { forwardRef } from 'react';
|
|
4
|
+
import { cn } from '../../utils/cn';
|
|
5
|
+
/**
|
|
6
|
+
* Themes' `Text` rendered as a label, so field labels sit on the same type scale as
|
|
7
|
+
* everything else.
|
|
8
|
+
*
|
|
9
|
+
* The asterisk is decoration: `required` is already on the input, and a screen reader
|
|
10
|
+
* announcing "New email star" reads worse than the field simply being required.
|
|
11
|
+
*/
|
|
12
|
+
export const Label = forwardRef(function Label({ className, required = false, children, ...props }, ref) {
|
|
13
|
+
return (_jsxs(Text, { as: "label", size: "2", weight: "medium", ref: ref, className: cn('text-brand-900', className), ...props, children: [children, required ? (_jsx("span", { "aria-hidden": "true", className: "ml-0.5 text-danger-ink", children: "*" })) : null] }));
|
|
14
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Label';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Label';
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
export interface PopoverProps {
|
|
3
|
+
/** The control that opens it. Rendered as-is, so it keeps its own styling. */
|
|
4
|
+
trigger: ReactNode;
|
|
5
|
+
children: ReactNode;
|
|
6
|
+
open?: boolean;
|
|
7
|
+
onOpenChange?: (open: boolean) => void;
|
|
8
|
+
align?: 'start' | 'center' | 'end';
|
|
9
|
+
className?: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* A popover rather than a dropdown menu: the contents are things to read — the bell feed —
|
|
13
|
+
* not commands to run, and a menu's roving focus would make arrow keys skip past text
|
|
14
|
+
* rather than scroll it.
|
|
15
|
+
*/
|
|
16
|
+
export declare function Popover({ trigger, children, open, onOpenChange, align, className, }: PopoverProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Popover as ThemedPopover } from '@radix-ui/themes';
|
|
3
|
+
import { cn } from '../../utils/cn';
|
|
4
|
+
/**
|
|
5
|
+
* A popover rather than a dropdown menu: the contents are things to read — the bell feed —
|
|
6
|
+
* not commands to run, and a menu's roving focus would make arrow keys skip past text
|
|
7
|
+
* rather than scroll it.
|
|
8
|
+
*/
|
|
9
|
+
export function Popover({ trigger, children, open, onOpenChange, align = 'end', className, }) {
|
|
10
|
+
return (_jsxs(ThemedPopover.Root, { open: open, onOpenChange: onOpenChange, children: [_jsx(ThemedPopover.Trigger, { children: trigger }), _jsx(ThemedPopover.Content, { align: align, className: cn('w-80 max-w-[calc(100vw-1.5rem)] p-0', className), children: children })] }));
|
|
11
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Popover';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Popover';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export interface ProgressProps {
|
|
2
|
+
/** 0–1. Clamped, because a turnover ratio can genuinely exceed its ceiling. */
|
|
3
|
+
value: number;
|
|
4
|
+
label: string;
|
|
5
|
+
/** Carries meaning here: green, amber or red by how close the ceiling is. */
|
|
6
|
+
color?: 'jade' | 'amber' | 'red';
|
|
7
|
+
className?: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function Progress({ value, label, color, className }: ProgressProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Progress as ThemedProgress } from '@radix-ui/themes';
|
|
3
|
+
import { cn } from '../../utils/cn';
|
|
4
|
+
export function Progress({ value, label, color = 'jade', className }) {
|
|
5
|
+
return (_jsx(ThemedProgress
|
|
6
|
+
// Rounded for the announcement, clamped for both: turnover can genuinely pass its
|
|
7
|
+
// ceiling, and "110 out of 100" is not a sentence worth reading out.
|
|
8
|
+
, {
|
|
9
|
+
// Rounded for the announcement, clamped for both: turnover can genuinely pass its
|
|
10
|
+
// ceiling, and "110 out of 100" is not a sentence worth reading out.
|
|
11
|
+
value: Math.round(Math.min(100, Math.max(0, value * 100))), max: 100, color: color, size: "2", "aria-label": label, className: cn(className) }));
|
|
12
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Progress';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Progress';
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export interface SelectOption {
|
|
2
|
+
value: string;
|
|
3
|
+
label: string;
|
|
4
|
+
}
|
|
5
|
+
export interface SelectProps {
|
|
6
|
+
value?: string;
|
|
7
|
+
onValueChange?: (value: string) => void;
|
|
8
|
+
options: SelectOption[];
|
|
9
|
+
placeholder?: string;
|
|
10
|
+
id?: string;
|
|
11
|
+
name?: string;
|
|
12
|
+
disabled?: boolean;
|
|
13
|
+
invalid?: boolean;
|
|
14
|
+
required?: boolean;
|
|
15
|
+
'aria-describedby'?: string;
|
|
16
|
+
className?: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Themes' Select, which is its own styling of the same Radix primitive this used to wrap by
|
|
20
|
+
* hand — the chevron, the popper, the checked item and the scroll buttons all come with it.
|
|
21
|
+
*
|
|
22
|
+
* The option list stays a prop rather than children: every caller here builds it from data,
|
|
23
|
+
* and passing `<Select.Item>` elements around would make that a map at each site.
|
|
24
|
+
*/
|
|
25
|
+
export declare const Select: import("react").ForwardRefExoticComponent<SelectProps & import("react").RefAttributes<HTMLButtonElement>>;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Select as ThemedSelect } from '@radix-ui/themes';
|
|
3
|
+
import { forwardRef } from 'react';
|
|
4
|
+
import { useUiStrings } from '../../strings';
|
|
5
|
+
import { cn } from '../../utils/cn';
|
|
6
|
+
/**
|
|
7
|
+
* Themes' Select, which is its own styling of the same Radix primitive this used to wrap by
|
|
8
|
+
* hand — the chevron, the popper, the checked item and the scroll buttons all come with it.
|
|
9
|
+
*
|
|
10
|
+
* The option list stays a prop rather than children: every caller here builds it from data,
|
|
11
|
+
* and passing `<Select.Item>` elements around would make that a map at each site.
|
|
12
|
+
*/
|
|
13
|
+
export const Select = forwardRef(function Select({ value, onValueChange, options, placeholder, invalid = false, disabled, required, id, name, className, ...props }, ref) {
|
|
14
|
+
const strings = useUiStrings();
|
|
15
|
+
return (_jsxs(ThemedSelect.Root, { value: value, onValueChange: onValueChange, disabled: disabled, required: required, name: name, children: [_jsx(ThemedSelect.Trigger, { ref: ref, id: id, placeholder: placeholder ?? strings.select, color: invalid ? 'red' : undefined, "aria-invalid": invalid || undefined, className: cn('w-full', className), ...props }), _jsx(ThemedSelect.Content, { position: "popper", children: options.map((option) => (_jsx(ThemedSelect.Item, { value: option.value, children: option.label }, option.value))) })] }));
|
|
16
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Select';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Select';
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Separator as ThemedSeparator } from '@radix-ui/themes';
|
|
3
|
+
import { cn } from '../../utils/cn';
|
|
4
|
+
export function Separator({ orientation = 'horizontal', className }) {
|
|
5
|
+
return _jsx(ThemedSeparator, { orientation: orientation, size: "4", className: cn(className) });
|
|
6
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Separator';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Separator';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface SpinnerProps {
|
|
2
|
+
className?: string;
|
|
3
|
+
label?: string;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Themes' spinner, wrapped so it still announces itself.
|
|
7
|
+
*
|
|
8
|
+
* Theirs is decoration — three animated bars with no role — which is right when it sits
|
|
9
|
+
* inside a button that already says "Saving". Standing alone, as it does on a loading
|
|
10
|
+
* screen, something has to tell a screen reader that a wait is in progress.
|
|
11
|
+
*/
|
|
12
|
+
export declare function Spinner({ className, label }: SpinnerProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Spinner as ThemedSpinner } from '@radix-ui/themes';
|
|
3
|
+
import { useUiStrings } from '../../strings';
|
|
4
|
+
import { cn } from '../../utils/cn';
|
|
5
|
+
/**
|
|
6
|
+
* Themes' spinner, wrapped so it still announces itself.
|
|
7
|
+
*
|
|
8
|
+
* Theirs is decoration — three animated bars with no role — which is right when it sits
|
|
9
|
+
* inside a button that already says "Saving". Standing alone, as it does on a loading
|
|
10
|
+
* screen, something has to tell a screen reader that a wait is in progress.
|
|
11
|
+
*/
|
|
12
|
+
export function Spinner({ className, label }) {
|
|
13
|
+
const strings = useUiStrings();
|
|
14
|
+
return (_jsx("span", { role: "status", "aria-label": label ?? strings.loading, className: cn('inline-flex', className), children: _jsx(ThemedSpinner, { size: "2" }) }));
|
|
15
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Spinner';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Spinner';
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { Switch as ThemedSwitch } from '@radix-ui/themes';
|
|
2
|
+
import { type ComponentPropsWithoutRef } from 'react';
|
|
3
|
+
export type SwitchProps = Omit<ComponentPropsWithoutRef<typeof ThemedSwitch>, 'size'>;
|
|
4
|
+
export declare const Switch: import("react").ForwardRefExoticComponent<SwitchProps & import("react").RefAttributes<HTMLButtonElement>>;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Switch as ThemedSwitch } from '@radix-ui/themes';
|
|
3
|
+
import { forwardRef } from 'react';
|
|
4
|
+
import { cn } from '../../utils/cn';
|
|
5
|
+
export const Switch = forwardRef(function Switch({ className, ...props }, ref) {
|
|
6
|
+
return _jsx(ThemedSwitch, { ref: ref, size: "2", className: cn(className), ...props });
|
|
7
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Switch';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Switch';
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { TextArea } from '@radix-ui/themes';
|
|
2
|
+
import { type ComponentPropsWithoutRef } from 'react';
|
|
3
|
+
export interface TextareaProps extends Omit<ComponentPropsWithoutRef<typeof TextArea>, 'size' | 'color'> {
|
|
4
|
+
invalid?: boolean;
|
|
5
|
+
}
|
|
6
|
+
export declare const Textarea: import("react").ForwardRefExoticComponent<TextareaProps & import("react").RefAttributes<HTMLTextAreaElement>>;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { TextArea } from '@radix-ui/themes';
|
|
3
|
+
import { forwardRef } from 'react';
|
|
4
|
+
import { cn } from '../../utils/cn';
|
|
5
|
+
export const Textarea = forwardRef(function Textarea({ invalid = false, className, rows = 3, ...props }, ref) {
|
|
6
|
+
return (_jsx(TextArea, { ref: ref, rows: rows, size: "2", color: invalid ? 'red' : undefined, "aria-invalid": invalid || undefined, className: cn('w-full', className), ...props }));
|
|
7
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Textarea';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Textarea';
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
export interface TooltipProps {
|
|
3
|
+
/** Nothing renders when this is absent, so a caller can switch it off. */
|
|
4
|
+
label?: string;
|
|
5
|
+
children: ReactNode;
|
|
6
|
+
side?: 'top' | 'right' | 'bottom' | 'left';
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* A hover label, in place of the browser's own `title` attribute.
|
|
10
|
+
*
|
|
11
|
+
* `title` looks free but is not: it never appears for keyboard or touch users, it cannot be
|
|
12
|
+
* styled, and screen readers treat it inconsistently enough to be unreliable as a label.
|
|
13
|
+
* This shows on focus as well as hover and wires `aria-describedby`, which is what the
|
|
14
|
+
* collapsed sidebar needs — a rail of unlabelled icons is unusable without it.
|
|
15
|
+
*/
|
|
16
|
+
export declare function Tooltip({ label, children, side }: TooltipProps): import("react").JSX.Element;
|
|
17
|
+
/**
|
|
18
|
+
* Kept as a no-op wrapper.
|
|
19
|
+
*
|
|
20
|
+
* Themes mounts its own tooltip provider inside `<Theme>`, so there is nothing left to
|
|
21
|
+
* provide — but the layout reads better with the boundary still visible, and removing it
|
|
22
|
+
* would touch a file for no behavioural reason.
|
|
23
|
+
*/
|
|
24
|
+
export declare function TooltipProvider({ children }: {
|
|
25
|
+
children: ReactNode;
|
|
26
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Tooltip as ThemedTooltip } from '@radix-ui/themes';
|
|
3
|
+
/**
|
|
4
|
+
* A hover label, in place of the browser's own `title` attribute.
|
|
5
|
+
*
|
|
6
|
+
* `title` looks free but is not: it never appears for keyboard or touch users, it cannot be
|
|
7
|
+
* styled, and screen readers treat it inconsistently enough to be unreliable as a label.
|
|
8
|
+
* This shows on focus as well as hover and wires `aria-describedby`, which is what the
|
|
9
|
+
* collapsed sidebar needs — a rail of unlabelled icons is unusable without it.
|
|
10
|
+
*/
|
|
11
|
+
export function Tooltip({ label, children, side = 'right' }) {
|
|
12
|
+
if (!label)
|
|
13
|
+
return _jsx(_Fragment, { children: children });
|
|
14
|
+
return (_jsx(ThemedTooltip, { content: label, side: side, children: children }));
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Kept as a no-op wrapper.
|
|
18
|
+
*
|
|
19
|
+
* Themes mounts its own tooltip provider inside `<Theme>`, so there is nothing left to
|
|
20
|
+
* provide — but the layout reads better with the boundary still visible, and removing it
|
|
21
|
+
* would touch a file for no behavioural reason.
|
|
22
|
+
*/
|
|
23
|
+
export function TooltipProvider({ children }) {
|
|
24
|
+
return _jsx(_Fragment, { children: children });
|
|
25
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Tooltip';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Tooltip';
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export * from './Avatar';
|
|
2
|
+
export * from './Badge';
|
|
3
|
+
export * from './Button';
|
|
4
|
+
export * from './Card';
|
|
5
|
+
export * from './Checkbox';
|
|
6
|
+
export * from './ContextMenu';
|
|
7
|
+
export * from './Dialog';
|
|
8
|
+
export * from './Icon';
|
|
9
|
+
export * from './Input';
|
|
10
|
+
export * from './Label';
|
|
11
|
+
export * from './Popover';
|
|
12
|
+
export * from './Progress';
|
|
13
|
+
export * from './Select';
|
|
14
|
+
export * from './Separator';
|
|
15
|
+
export * from './Spinner';
|
|
16
|
+
export * from './Switch';
|
|
17
|
+
export * from './Textarea';
|
|
18
|
+
export * from './Tooltip';
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export * from './Avatar';
|
|
2
|
+
export * from './Badge';
|
|
3
|
+
export * from './Button';
|
|
4
|
+
export * from './Card';
|
|
5
|
+
export * from './Checkbox';
|
|
6
|
+
export * from './ContextMenu';
|
|
7
|
+
export * from './Dialog';
|
|
8
|
+
export * from './Icon';
|
|
9
|
+
export * from './Input';
|
|
10
|
+
export * from './Label';
|
|
11
|
+
export * from './Popover';
|
|
12
|
+
export * from './Progress';
|
|
13
|
+
export * from './Select';
|
|
14
|
+
export * from './Separator';
|
|
15
|
+
export * from './Spinner';
|
|
16
|
+
export * from './Switch';
|
|
17
|
+
export * from './Textarea';
|
|
18
|
+
export * from './Tooltip';
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* The handful of strings the atoms say on their own behalf.
|
|
4
|
+
*
|
|
5
|
+
* **The dictionaries stay in the apps, deliberately** — that is a decision in the product
|
|
6
|
+
* brief, and it is the right one: a shared dictionary would make every string in three
|
|
7
|
+
* products one product's business. But two atoms do have something to say without being told.
|
|
8
|
+
* A standing `Spinner` has to announce that a wait is in progress, and a `Select` with no
|
|
9
|
+
* placeholder has to say something in the trigger.
|
|
10
|
+
*
|
|
11
|
+
* So the package declares what it needs and the host supplies it. English defaults, not
|
|
12
|
+
* Portuguese: an app that forgets to wrap gets something legible rather than something wrong,
|
|
13
|
+
* and Portuguese here would be a translatable string living in the shared package, which is
|
|
14
|
+
* exactly what this avoids.
|
|
15
|
+
*/
|
|
16
|
+
export interface UiStrings {
|
|
17
|
+
/** Announced by a standing spinner. */
|
|
18
|
+
loading: string;
|
|
19
|
+
/** The placeholder in a Select that was given none. */
|
|
20
|
+
select: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Feeds the atoms their strings, once, near the root.
|
|
24
|
+
*
|
|
25
|
+
* Every app wraps its tree in this and passes its own dictionary's words through. It takes a
|
|
26
|
+
* partial so an app that only cares about one of them says only that.
|
|
27
|
+
*/
|
|
28
|
+
export declare function UiStringsProvider({ strings, children, }: {
|
|
29
|
+
strings: Partial<UiStrings>;
|
|
30
|
+
children: ReactNode;
|
|
31
|
+
}): import("react").JSX.Element;
|
|
32
|
+
/**
|
|
33
|
+
* No error when there is no provider, unlike the apps' own `useCopy`.
|
|
34
|
+
*
|
|
35
|
+
* A missing provider there is a bug — the dictionary is the app. Here it is a host that did
|
|
36
|
+
* not need to override anything, which is a legitimate way to use this package and must not
|
|
37
|
+
* throw in a component as ordinary as a spinner.
|
|
38
|
+
*/
|
|
39
|
+
export declare function useUiStrings(): UiStrings;
|
package/dist/strings.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { createContext, useContext } from 'react';
|
|
3
|
+
const DEFAULTS = {
|
|
4
|
+
loading: 'Loading',
|
|
5
|
+
select: 'Choose…',
|
|
6
|
+
};
|
|
7
|
+
const StringsContext = createContext(DEFAULTS);
|
|
8
|
+
/**
|
|
9
|
+
* Feeds the atoms their strings, once, near the root.
|
|
10
|
+
*
|
|
11
|
+
* Every app wraps its tree in this and passes its own dictionary's words through. It takes a
|
|
12
|
+
* partial so an app that only cares about one of them says only that.
|
|
13
|
+
*/
|
|
14
|
+
export function UiStringsProvider({ strings, children, }) {
|
|
15
|
+
return (_jsx(StringsContext.Provider, { value: { ...DEFAULTS, ...strings }, children: children }));
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* No error when there is no provider, unlike the apps' own `useCopy`.
|
|
19
|
+
*
|
|
20
|
+
* A missing provider there is a bug — the dictionary is the app. Here it is a host that did
|
|
21
|
+
* not need to override anything, which is a legitimate way to use this package and must not
|
|
22
|
+
* throw in a component as ordinary as a spinner.
|
|
23
|
+
*/
|
|
24
|
+
export function useUiStrings() {
|
|
25
|
+
return useContext(StringsContext);
|
|
26
|
+
}
|
package/dist/utils/cn.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export declare const THEMES: readonly ["system", "light", "dark"];
|
|
2
|
+
export type Theme = (typeof THEMES)[number];
|
|
3
|
+
/**
|
|
4
|
+
* Applies a theme by stamping the root element, or clearing it for "system".
|
|
5
|
+
*
|
|
6
|
+
* Nothing but this function writes the attribute, and nothing but the stylesheet reads it.
|
|
7
|
+
* "System" deliberately stamps *nothing*: the CSS falls through to `prefers-color-scheme`
|
|
8
|
+
* on its own, so following the operating system needs no listener here and keeps working
|
|
9
|
+
* when the user changes it mid-session.
|
|
10
|
+
*/
|
|
11
|
+
export declare function applyTheme(theme: Theme): void;
|
|
12
|
+
export declare function readTheme(): Theme;
|
|
13
|
+
export declare function storeTheme(theme: Theme): void;
|
|
14
|
+
/**
|
|
15
|
+
* The accents a business can dress an app in.
|
|
16
|
+
*
|
|
17
|
+
* Six of Radix's twenty-six, not all of them: every one costs a colour scale in the bundle —
|
|
18
|
+
* see the note in each app's `main.tsx` — and a page of near-identical greens is a worse
|
|
19
|
+
* choice than six that are obviously different from across a room. `jade` is first because it
|
|
20
|
+
* is Upfront's own, and the default for anyone who never opens the picker.
|
|
21
|
+
*
|
|
22
|
+
* **Only the accent moves.** Success stays jade, warning amber, danger red — those are
|
|
23
|
+
* meanings rather than decoration, and a business whose "paid" badge turned orange because
|
|
24
|
+
* they liked orange would have lost something. It does mean picking red or jade makes the
|
|
25
|
+
* accent and one semantic colour look alike, which is a fair trade for letting people choose.
|
|
26
|
+
*
|
|
27
|
+
* **The labels are not here.** They are words, and words belong to an app's own dictionary —
|
|
28
|
+
* the same rule that keeps `UiStrings` down to two entries.
|
|
29
|
+
*/
|
|
30
|
+
export declare const ACCENTS: readonly ["jade", "blue", "purple", "red", "orange", "yellow"];
|
|
31
|
+
export type Accent = (typeof ACCENTS)[number];
|
|
32
|
+
export declare function readAccent(): Accent;
|
|
33
|
+
export declare function storeAccent(accent: Accent): void;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
export const THEMES = ['system', 'light', 'dark'];
|
|
2
|
+
const STORAGE_KEY = 'upfront.theme';
|
|
3
|
+
/**
|
|
4
|
+
* Applies a theme by stamping the root element, or clearing it for "system".
|
|
5
|
+
*
|
|
6
|
+
* Nothing but this function writes the attribute, and nothing but the stylesheet reads it.
|
|
7
|
+
* "System" deliberately stamps *nothing*: the CSS falls through to `prefers-color-scheme`
|
|
8
|
+
* on its own, so following the operating system needs no listener here and keeps working
|
|
9
|
+
* when the user changes it mid-session.
|
|
10
|
+
*/
|
|
11
|
+
export function applyTheme(theme) {
|
|
12
|
+
const root = document.documentElement;
|
|
13
|
+
if (theme === 'system')
|
|
14
|
+
root.removeAttribute('data-theme');
|
|
15
|
+
else
|
|
16
|
+
root.setAttribute('data-theme', theme);
|
|
17
|
+
}
|
|
18
|
+
export function readTheme() {
|
|
19
|
+
try {
|
|
20
|
+
const stored = localStorage.getItem(STORAGE_KEY);
|
|
21
|
+
return THEMES.includes(stored) ? stored : 'system';
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
// Private windows and blocked site data both throw. A theme preference is not worth a
|
|
25
|
+
// crash, so the system default stands.
|
|
26
|
+
return 'system';
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export function storeTheme(theme) {
|
|
30
|
+
try {
|
|
31
|
+
localStorage.setItem(STORAGE_KEY, theme);
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
// See readTheme. It applies for this session either way.
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* The accents a business can dress an app in.
|
|
39
|
+
*
|
|
40
|
+
* Six of Radix's twenty-six, not all of them: every one costs a colour scale in the bundle —
|
|
41
|
+
* see the note in each app's `main.tsx` — and a page of near-identical greens is a worse
|
|
42
|
+
* choice than six that are obviously different from across a room. `jade` is first because it
|
|
43
|
+
* is Upfront's own, and the default for anyone who never opens the picker.
|
|
44
|
+
*
|
|
45
|
+
* **Only the accent moves.** Success stays jade, warning amber, danger red — those are
|
|
46
|
+
* meanings rather than decoration, and a business whose "paid" badge turned orange because
|
|
47
|
+
* they liked orange would have lost something. It does mean picking red or jade makes the
|
|
48
|
+
* accent and one semantic colour look alike, which is a fair trade for letting people choose.
|
|
49
|
+
*
|
|
50
|
+
* **The labels are not here.** They are words, and words belong to an app's own dictionary —
|
|
51
|
+
* the same rule that keeps `UiStrings` down to two entries.
|
|
52
|
+
*/
|
|
53
|
+
export const ACCENTS = ['jade', 'blue', 'purple', 'red', 'orange', 'yellow'];
|
|
54
|
+
const ACCENT_KEY = 'upfront.accent';
|
|
55
|
+
export function readAccent() {
|
|
56
|
+
try {
|
|
57
|
+
const stored = localStorage.getItem(ACCENT_KEY);
|
|
58
|
+
return ACCENTS.includes(stored) ? stored : 'jade';
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
// Same as readTheme: a private window throws, and a colour is not worth a crash.
|
|
62
|
+
return 'jade';
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
export function storeAccent(accent) {
|
|
66
|
+
try {
|
|
67
|
+
localStorage.setItem(ACCENT_KEY, accent);
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
// It applies for this session either way.
|
|
71
|
+
}
|
|
72
|
+
}
|