@sesamehr/react-design-system 3.4.0 → 3.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Button/index.d.ts +1 -1
- package/dist/Display/AiTag/AiTag.d.ts +33 -0
- package/dist/Display/AiTag/index.d.ts +21 -0
- package/dist/Display/Avatar/index.d.ts +2 -2
- package/dist/Display/Badge/index.d.ts +1 -1
- package/dist/Display/BrandLogo/BrandLogo.d.ts +53 -0
- package/dist/Display/BrandLogo/brandLogoArt.d.ts +29 -0
- package/dist/Display/BrandLogo/index.d.ts +17 -0
- package/dist/Display/FileCard/FileCard.d.ts +102 -41
- package/dist/Display/FileCard/index.d.ts +31 -0
- package/dist/Display/ImageAttachment/ImageAttachment.d.ts +58 -0
- package/dist/Display/ImageAttachment/index.d.ts +2 -0
- package/dist/Display/LinkPreview/LinkPreview.d.ts +65 -0
- package/dist/Display/LinkPreview/index.d.ts +1 -0
- package/dist/Display/LocationAttachment/LocationAttachment.d.ts +71 -0
- package/dist/Display/LocationAttachment/index.d.ts +1 -0
- package/dist/Display/VideoAttachment/VideoAttachment.d.ts +78 -0
- package/dist/Display/VideoAttachment/index.d.ts +1 -0
- package/dist/Display/VoiceMessage/VoiceMessage.d.ts +103 -0
- package/dist/Display/VoiceMessage/index.d.ts +2 -0
- package/dist/Display/VoiceMessage/toWaveformPeaks.d.ts +58 -0
- package/dist/Display/VoiceMessage/useVoiceWaveform.d.ts +90 -0
- package/dist/Feedback/Dialog/Dialog.d.ts +13 -1
- package/dist/Feedback/Dialog/DialogOverlay.d.ts +12 -1
- package/dist/Feedback/Drawer/Drawer.d.ts +68 -0
- package/dist/Feedback/Drawer/DrawerBody.d.ts +18 -0
- package/dist/Feedback/Drawer/DrawerContent.d.ts +36 -0
- package/dist/Feedback/Drawer/DrawerDescription.d.ts +10 -0
- package/dist/Feedback/Drawer/DrawerExpand.d.ts +23 -0
- package/dist/Feedback/Drawer/DrawerTitle.d.ts +13 -0
- package/dist/Feedback/Drawer/index.d.ts +7 -0
- package/dist/Feedback/Drawer/useDrawer.d.ts +30 -0
- package/dist/Feedback/Progress/index.d.ts +1 -1
- package/dist/Forms/Inputs/AiSearch/AiSearch.d.ts +67 -0
- package/dist/Forms/Inputs/AiSearch/AiSearchAsk.d.ts +34 -0
- package/dist/Forms/Inputs/AiSearch/AiSearchPanel.d.ts +50 -0
- package/dist/Forms/Inputs/AiSearch/AiSearchSend.d.ts +30 -0
- package/dist/Forms/Inputs/AiSearch/AiSearchShortcut.d.ts +23 -0
- package/dist/Forms/Inputs/AiSearch/AiSearchSuggestion.d.ts +27 -0
- package/dist/Forms/Inputs/AiSearch/index.d.ts +6 -0
- package/dist/Forms/Inputs/AiSearch/useAiSearch.d.ts +37 -0
- package/dist/Forms/Inputs/TimePicker/TimePicker.d.ts +64 -0
- package/dist/Forms/Inputs/TimePicker/TimePickerField.d.ts +35 -0
- package/dist/Forms/Inputs/TimePicker/TimePickerList.d.ts +23 -0
- package/dist/Forms/Inputs/TimePicker/TimePickerWheel.d.ts +26 -0
- package/dist/Forms/Inputs/TimePicker/TimePickerWheelColumn.d.ts +23 -0
- package/dist/Forms/Inputs/TimePicker/index.d.ts +6 -0
- package/dist/Forms/Inputs/TimePicker/time.d.ts +41 -0
- package/dist/Forms/Inputs/TimePicker/useTimeField.d.ts +62 -0
- package/dist/Forms/Inputs/TimePicker/useTimeList.d.ts +53 -0
- package/dist/Forms/Inputs/TimePicker/useTimePicker.d.ts +35 -0
- package/dist/Forms/Inputs/TimePicker/useWheelColumn.d.ts +71 -0
- package/dist/Forms/Inputs/index.d.ts +1 -0
- package/dist/Navigation/Tabs/Tabs.d.ts +18 -0
- package/dist/Navigation/Tabs/TabsContent.d.ts +7 -0
- package/dist/Navigation/Tabs/TabsList.d.ts +10 -0
- package/dist/Navigation/Tabs/TabsTrigger.d.ts +20 -0
- package/dist/Navigation/Tabs/index.d.ts +4 -0
- package/dist/internal/AttachmentMedia/AttachmentMedia.d.ts +38 -0
- package/dist/internal/AttachmentMedia/index.d.ts +36 -0
- package/dist/internal/index.d.ts +1 -0
- package/dist/main.d.ts +12 -2
- package/dist/react-design-system.css +1 -1
- package/dist/react-design-system.js +12581 -10218
- package/dist/react-design-system.umd.cjs +108 -74
- package/package.json +3 -2
- package/src/assets/styles/theme-v3.css +99 -2
- package/src/assets/styles/theme-v3.test.ts +5 -5
package/dist/Button/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { VariantProps } from 'class-variance-authority';
|
|
2
2
|
export { Button, type ButtonProps } from './Button.tsx';
|
|
3
3
|
export declare const buttonVariants: (props?: ({
|
|
4
|
-
variant?: "link" | "default" | "destructive" | "success" | "outline" | "secondary" | "ghost" | null | undefined;
|
|
4
|
+
variant?: "link" | "default" | "destructive" | "success" | "outline" | "secondary" | "ghost" | "ai" | null | undefined;
|
|
5
5
|
size?: "icon" | "2xs" | "xs" | "sm" | "md" | "lg" | null | undefined;
|
|
6
6
|
} & import('class-variance-authority/types').ClassProp) | undefined) => string;
|
|
7
7
|
export type ButtonVariants = VariantProps<typeof buttonVariants>;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { AiTagVariants } from './index.ts';
|
|
2
|
+
/**
|
|
3
|
+
* `color` has to be omitted before it is redeclared: React's `HTMLAttributes`
|
|
4
|
+
* carries HTML's deprecated presentational `color` attribute as `string`, and
|
|
5
|
+
* the variant union will not widen to it.
|
|
6
|
+
*/
|
|
7
|
+
export interface AiTagProps extends Omit<React.HTMLAttributes<HTMLSpanElement>, 'color'> {
|
|
8
|
+
/**
|
|
9
|
+
* Whether this is the AI marker itself (`ai`, the two-hue gradient) or the
|
|
10
|
+
* quiet grey version for a surface that is already about AI.
|
|
11
|
+
*/
|
|
12
|
+
color?: AiTagVariants['color'];
|
|
13
|
+
/** A line around nothing, or a filled surface. */
|
|
14
|
+
variant?: AiTagVariants['variant'];
|
|
15
|
+
/** 20px with 12px type, or 28px with 14px type. */
|
|
16
|
+
size?: AiTagVariants['size'];
|
|
17
|
+
dataTestid: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Says that what it sits next to came from Sesame AI.
|
|
21
|
+
*
|
|
22
|
+
* Built on `OxTag`'s shape rather than on a primitive, which is also what
|
|
23
|
+
* shadcn would have given: its `Badge` is a `<span>` with a variant map and no
|
|
24
|
+
* behaviour, because there is no behaviour here to get wrong. The AI marker is
|
|
25
|
+
* not interactive, takes no click and is not a tab stop — if pressing it should
|
|
26
|
+
* do something, that is an `OxChip`.
|
|
27
|
+
*
|
|
28
|
+
* The glyph goes in `children` carrying `data-slot="icon"`, the way `OxTag`
|
|
29
|
+
* takes one, and is not shipped inside with a prop to hide it. Figma spells the
|
|
30
|
+
* same axis as a `Show icon` boolean, and writing the icon or not writing it is
|
|
31
|
+
* the one spelling of that axis which needs no prop and cannot be got wrong.
|
|
32
|
+
*/
|
|
33
|
+
export declare const AiTag: import('react').ForwardRefExoticComponent<AiTagProps & import('react').RefAttributes<HTMLSpanElement>>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { VariantProps } from 'class-variance-authority';
|
|
2
|
+
export { AiTag, type AiTagProps } from './AiTag.tsx';
|
|
3
|
+
/**
|
|
4
|
+
* The marker that says a piece of content came from Sesame AI.
|
|
5
|
+
*
|
|
6
|
+
* Two axes, and they are not the same axis twice. `color` is *whether this is
|
|
7
|
+
* the AI marker at all*: `ai` paints the two-hue gradient nothing else in the
|
|
8
|
+
* system uses, `muted` is the same pill in grey for when the surrounding UI is
|
|
9
|
+
* already about AI and a second gradient would only shout. `variant` is how
|
|
10
|
+
* loudly either one is painted — a line around nothing, or a filled surface.
|
|
11
|
+
*
|
|
12
|
+
* The design calls the second axis `Style`; it is `variant` here because a prop
|
|
13
|
+
* named `style` would eat the caller's inline styles. `OxTag` renamed it for
|
|
14
|
+
* the same reason.
|
|
15
|
+
*/
|
|
16
|
+
export declare const aiTagVariants: (props?: ({
|
|
17
|
+
color?: "ai" | "muted" | null | undefined;
|
|
18
|
+
variant?: "filled" | "outline" | null | undefined;
|
|
19
|
+
size?: "sm" | "md" | null | undefined;
|
|
20
|
+
} & import('class-variance-authority/types').ClassProp) | undefined) => string;
|
|
21
|
+
export type AiTagVariants = VariantProps<typeof aiTagVariants>;
|
|
@@ -23,12 +23,12 @@ export { useAvatarBullet } from './hooks/useAvatarBullet.ts';
|
|
|
23
23
|
*/
|
|
24
24
|
export declare const avatarSquareVariants: (props?: ({
|
|
25
25
|
size?: "xs" | "sm" | "md" | "lg" | "xl" | null | undefined;
|
|
26
|
-
tone?: "
|
|
26
|
+
tone?: "brand" | "neutral" | null | undefined;
|
|
27
27
|
disabled?: boolean | null | undefined;
|
|
28
28
|
} & import('class-variance-authority/types').ClassProp) | undefined) => string;
|
|
29
29
|
export declare const avatarRoundedVariants: (props?: ({
|
|
30
30
|
size?: "xs" | "sm" | "md" | "lg" | "xl" | null | undefined;
|
|
31
|
-
tone?: "
|
|
31
|
+
tone?: "brand" | "neutral" | null | undefined;
|
|
32
32
|
disabled?: boolean | null | undefined;
|
|
33
33
|
} & import('class-variance-authority/types').ClassProp) | undefined) => string;
|
|
34
34
|
export type AvatarSquareVariants = VariantProps<typeof avatarSquareVariants>;
|
|
@@ -12,6 +12,6 @@ export { Badge, type BadgeProps } from './Badge.tsx';
|
|
|
12
12
|
*/
|
|
13
13
|
export declare const badgeVariants: (props?: ({
|
|
14
14
|
size?: "sm" | "md" | "lg" | null | undefined;
|
|
15
|
-
color?: "
|
|
15
|
+
color?: "brand" | "error" | "neutral" | null | undefined;
|
|
16
16
|
} & import('class-variance-authority/types').ClassProp) | undefined) => string;
|
|
17
17
|
export type BadgeVariants = VariantProps<typeof badgeVariants>;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { BrandLogoVariants } from './index.ts';
|
|
2
|
+
/**
|
|
3
|
+
* `color` has to be omitted before it is redeclared: React's `SVGAttributes`
|
|
4
|
+
* carries SVG's presentational `color` attribute as `string`, and the variant
|
|
5
|
+
* union will not widen to it.
|
|
6
|
+
*/
|
|
7
|
+
export interface BrandLogoProps extends Omit<React.SVGAttributes<SVGSVGElement>, 'color' | 'type'> {
|
|
8
|
+
/**
|
|
9
|
+
* The symbol with the wordmark beside it, or the symbol on its own — which
|
|
10
|
+
* is what a collapsed sidebar and a favicon-sized slot want.
|
|
11
|
+
*/
|
|
12
|
+
type?: BrandLogoVariants['type'];
|
|
13
|
+
/**
|
|
14
|
+
* The ink: `dark` for a light background, `light` for a dark one. Not a
|
|
15
|
+
* dark-mode switch — see the note below.
|
|
16
|
+
*/
|
|
17
|
+
color?: BrandLogoVariants['color'];
|
|
18
|
+
/** 32px tall, or 88px. The width follows the art. */
|
|
19
|
+
size?: BrandLogoVariants['size'];
|
|
20
|
+
dataTestid: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* The Sesame logo.
|
|
24
|
+
*
|
|
25
|
+
* **It uses primitive colours on purpose, and this is the note that stops
|
|
26
|
+
* someone "fixing" it.** `dark` is `brand-500` and `light` is `white`, read
|
|
27
|
+
* straight off the primitive ramp rather than through `--brand` or
|
|
28
|
+
* `--foreground`. A semantic token is a promise that the value may be
|
|
29
|
+
* re-pointed — by a theme, by dark mode, by a consumer overriding
|
|
30
|
+
* `--ox-color-brand-500`'s semantic layer — and a logo is the one thing in the
|
|
31
|
+
* system that must not move when any of that happens. It is the brand, not a
|
|
32
|
+
* surface painted in brand colours. `@sesame/orxata-icons` is the only other
|
|
33
|
+
* sanctioned exception, for the same reason.
|
|
34
|
+
*
|
|
35
|
+
* So: the two colours here are *not* a dark-mode pair. `color` says what the
|
|
36
|
+
* logo is sitting on, not what the theme is — `light` is the white logo for a
|
|
37
|
+
* navy header, and it stays white in light mode.
|
|
38
|
+
*
|
|
39
|
+
* No primitive underneath, and nothing to borrow: shadcn has no logo and
|
|
40
|
+
* neither does Radix. There is no state, no keyboard and no coordination, so
|
|
41
|
+
* there is nothing for a headless primitive to hold — it is one `<svg>`.
|
|
42
|
+
*
|
|
43
|
+
* `role="img"` with no name by default, which is deliberate. A logo in a
|
|
44
|
+
* sidebar header beside the word "Sesame" is decoration and naming it twice is
|
|
45
|
+
* noise; a logo that *is* the link home needs the name on the link, not on the
|
|
46
|
+
* art. Where it is genuinely the only thing identifying the page, `children`
|
|
47
|
+
* becomes an SVG `<title>`:
|
|
48
|
+
*
|
|
49
|
+
* ```tsx
|
|
50
|
+
* <OxBrandLogo dataTestid="logo">{t('brand.name')}</OxBrandLogo>
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
export declare const BrandLogo: import('react').ForwardRefExoticComponent<BrandLogoProps & import('react').RefAttributes<SVGSVGElement>>;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Sesame logo, as the v3 board draws it.
|
|
3
|
+
*
|
|
4
|
+
* Path data rather than an asset file, and it lives beside the component rather
|
|
5
|
+
* than in `@sesame/orxata-icons`, for two reasons. The icons package renders an
|
|
6
|
+
* asset as `innerHTML` of an `<i>` and its one Sesame brand file is still the
|
|
7
|
+
* v2 mark — green accent, navy hexagon — so using it would ship the old logo;
|
|
8
|
+
* and adding v3 art there would tie this component to an icons release. Here it
|
|
9
|
+
* is versioned with the component that draws it.
|
|
10
|
+
*
|
|
11
|
+
* Every path is `fill="currentColor"` at render time. The board paints all of
|
|
12
|
+
* them with one colour per variant, so one `color` is all the markup needs —
|
|
13
|
+
* see the component for why that is worth saying out loud.
|
|
14
|
+
*
|
|
15
|
+
* Composed from the vector groups Figma exports, each placed with the offset
|
|
16
|
+
* the group has inside its variant frame. Regenerated rather than edited: the
|
|
17
|
+
* numbers are measurements, and a hand-nudged logo is a wrong logo.
|
|
18
|
+
*/
|
|
19
|
+
export interface BrandLogoArt {
|
|
20
|
+
viewBox: string;
|
|
21
|
+
groups: {
|
|
22
|
+
transform: string;
|
|
23
|
+
paths: string[];
|
|
24
|
+
}[];
|
|
25
|
+
}
|
|
26
|
+
/** Symbol plus the "sesame" wordmark: 113.6 x 32, which is 3.55:1. */
|
|
27
|
+
export declare const BRAND_LOGO_FULL: BrandLogoArt;
|
|
28
|
+
/** The symbol on its own, square. */
|
|
29
|
+
export declare const BRAND_LOGO_MARK: BrandLogoArt;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { VariantProps } from 'class-variance-authority';
|
|
2
|
+
export { BrandLogo, type BrandLogoProps } from './BrandLogo.tsx';
|
|
3
|
+
export { type BrandLogoArt } from './brandLogoArt.ts';
|
|
4
|
+
/**
|
|
5
|
+
* The Sesame logo.
|
|
6
|
+
*
|
|
7
|
+
* `type` picks the art and `color` picks the ink. There is no `variant` axis,
|
|
8
|
+
* which is where this differs from every other component in the package — see
|
|
9
|
+
* the note in `BrandLogo.tsx` about the board's `Style` axis, which draws the
|
|
10
|
+
* same two colours either way.
|
|
11
|
+
*/
|
|
12
|
+
export declare const brandLogoVariants: (props?: ({
|
|
13
|
+
type?: "mark" | "full" | null | undefined;
|
|
14
|
+
color?: "dark" | "light" | null | undefined;
|
|
15
|
+
size?: "md" | "lg" | null | undefined;
|
|
16
|
+
} & import('class-variance-authority/types').ClassProp) | undefined) => string;
|
|
17
|
+
export type BrandLogoVariants = VariantProps<typeof brandLogoVariants>;
|
|
@@ -1,30 +1,72 @@
|
|
|
1
|
+
import { FileCardVariants } from './index.ts';
|
|
1
2
|
/**
|
|
2
|
-
* One attached file as a row
|
|
3
|
+
* One attached file as a row: a type badge, the file's name, a second line of
|
|
4
|
+
* detail, somewhere for the caller to put what you can do to it, and an
|
|
5
|
+
* optional bar across the bottom while it moves.
|
|
3
6
|
*
|
|
4
|
-
*
|
|
5
|
-
* the white surface are already a component, and the only reason this file has
|
|
6
|
-
* a stylesheet at all is the row inside it. `flat` is the card the design
|
|
7
|
-
* draws: border, no lift — `Card`'s `flat` keeps the outline and drops only the
|
|
8
|
-
* shadow and the glass.
|
|
7
|
+
* Mirrors `@sesame/orxata-core` FileCard.
|
|
9
8
|
*
|
|
10
|
-
* ###
|
|
9
|
+
* ### Figma draws this on two pages and they are one component
|
|
11
10
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* naming it after one of those would be a lie the day someone passes another.
|
|
11
|
+
* `File card` is the file you already have — display and download. `File item`
|
|
12
|
+
* is the row inside `File upload` while it arrives, with `State = Loading |
|
|
13
|
+
* Uploaded | Hover | Error`. They are the same row: the same `_File / Type`
|
|
14
|
+
* badge, the same name over the same meta line, the same box. What differs is
|
|
15
|
+
* which control sits on the right — a download in one, a retry or a remove in
|
|
16
|
+
* the other — and that is already a slot.
|
|
19
17
|
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* none of
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
18
|
+
* So there is no `OxFileItem` and there is not going to be one. The test for
|
|
19
|
+
* splitting a component here is logic, state or context interaction, and the
|
|
20
|
+
* upload side has none of the three: the progress is a number that becomes a
|
|
21
|
+
* width, the error is a class, and everything else that changes is slot
|
|
22
|
+
* content. A second component would wrap this one, add classes and forward a
|
|
23
|
+
* slot, which is the shape the architecture rules name as premature
|
|
24
|
+
* abstraction.
|
|
25
|
+
*
|
|
26
|
+
* ### The text is slots, which is a reversal
|
|
27
|
+
*
|
|
28
|
+
* The name and the second line used to be **strings** here, argued for on the
|
|
29
|
+
* grounds that sameness is the product and a node is where that guarantee
|
|
30
|
+
* dies. It was the wrong trade. Content in props is the pattern
|
|
31
|
+
* composition-over-configuration exists to stop, and the guarantee it was
|
|
32
|
+
* buying is held by the stylesheet anyway — the two lines are sized and
|
|
33
|
+
* coloured here whatever goes in them. Keeping them as strings also spent two
|
|
34
|
+
* of five on content at the exact moment the board needed three for `type`,
|
|
35
|
+
* `surface` and `state`.
|
|
36
|
+
*
|
|
37
|
+
* `children` is the name; `meta` is the second line, and it is called that
|
|
38
|
+
* rather than `size` because what belongs there is the application's business —
|
|
39
|
+
* `PDF · 230 KB` on the board, but `Uploading… 60 %` on the upload one and
|
|
40
|
+
* `Uploaded by Ana` in a list.
|
|
41
|
+
*
|
|
42
|
+
* ### `type` is the colour; the badge's content is still yours
|
|
43
|
+
*
|
|
44
|
+
* Mapping a file to a glyph is application knowledge — there are hundreds of
|
|
45
|
+
* extensions and the set is open — which is why the badge has a slot. The
|
|
46
|
+
* board closes the *colour* at five named types and binds a token to each,
|
|
47
|
+
* which is a design decision and therefore ours. Which of the five `.odt`,
|
|
48
|
+
* `.numbers` or `.pages` belongs to is not.
|
|
49
|
+
*
|
|
50
|
+
* So the two split cleanly: `type` picks the ramp, `icon` carries what is
|
|
51
|
+
* printed on it. The board prints a three-letter abbreviation — `PDF`, `DOC`,
|
|
52
|
+
* `XLS`, `PPT` — and those come from the caller, because this package ships no
|
|
53
|
+
* strings and because the generic badge has no abbreviation to ship (Figma
|
|
54
|
+
* draws `ZIP` there, which is an example of a generic file and not the name of
|
|
55
|
+
* one). Pass nothing and the badge keeps the ramp and falls back to a plain
|
|
56
|
+
* file glyph, which is the one honest default. A sixth file type nobody has
|
|
57
|
+
* asked us for is `--ox-file-card-glyph-background` on the root.
|
|
58
|
+
*
|
|
59
|
+
* ### The bar is drawn here rather than being an `OxProgress`
|
|
60
|
+
*
|
|
61
|
+
* `OxProgress` is an 8px pill with a rounded fill on a `secondary` track. This
|
|
62
|
+
* is a 3px hairline flush against the bottom edge, square, and on `brand` it
|
|
63
|
+
* swaps both of its colours for `chat-user-media` and `brand-foreground`.
|
|
64
|
+
* Reaching for the component would mean overriding every declaration it has
|
|
65
|
+
* from outside, where the two module classes weigh the same — so which
|
|
66
|
+
* stylesheet the bundler emitted first would decide what the bar looked like.
|
|
67
|
+
* Two elements and `role="progressbar"` cost less and cannot lose that race.
|
|
68
|
+
* The bar names itself after the file, via the id on the name, so a screen
|
|
69
|
+
* reader says the file and not just a percentage.
|
|
28
70
|
*
|
|
29
71
|
* ### What it deliberately does not do
|
|
30
72
|
*
|
|
@@ -32,33 +74,52 @@
|
|
|
32
74
|
* `width`, and that figure belongs to the screen it was drawn on, not to the
|
|
33
75
|
* component: a card that hardcodes its own width cannot sit in a grid, a
|
|
34
76
|
* sidebar or a flexible column, and the parent owns external layout. It fills
|
|
35
|
-
* what it is given. The
|
|
36
|
-
*
|
|
37
|
-
*
|
|
77
|
+
* what it is given. The height stays, because that one is internal — a list of
|
|
78
|
+
* these has to have rows of equal height, and long names truncate rather than
|
|
79
|
+
* grow the row.
|
|
38
80
|
*
|
|
39
|
-
* **The root is not a button.** It already contains an interactive control,
|
|
40
|
-
* a button inside a button is invalid HTML; making the root activate would
|
|
41
|
-
* mean inventing a focus and keyboard contract the design has nothing to
|
|
42
|
-
* about. A caller who wants the whole row to do something wraps it, or
|
|
43
|
-
* link in `action`.
|
|
81
|
+
* **The root is not a button.** It already contains an interactive control,
|
|
82
|
+
* and a button inside a button is invalid HTML; making the root activate would
|
|
83
|
+
* also mean inventing a focus and keyboard contract the design has nothing to
|
|
84
|
+
* say about. A caller who wants the whole row to do something wraps it, or
|
|
85
|
+
* puts a link in `action`. The hover tint the upload board draws is on the row
|
|
86
|
+
* all the same, because the row is what the pointer is over whichever of those
|
|
87
|
+
* two the caller chose.
|
|
44
88
|
*/
|
|
45
|
-
export interface FileCardProps extends
|
|
46
|
-
/**
|
|
47
|
-
|
|
89
|
+
export interface FileCardProps extends React.ComponentPropsWithoutRef<'div'> {
|
|
90
|
+
/**
|
|
91
|
+
* Which of the five colour ramps the badge wears. Not which extension the
|
|
92
|
+
* file has — that mapping is yours, and the badge's content is a slot.
|
|
93
|
+
*/
|
|
94
|
+
type?: FileCardVariants['type'];
|
|
95
|
+
/**
|
|
96
|
+
* Which side of a chat bubble this sits in: `brand` inside the reader's own
|
|
97
|
+
* message, `default` anywhere else. The same axis the attachments carry.
|
|
98
|
+
*/
|
|
99
|
+
surface?: FileCardVariants['surface'];
|
|
100
|
+
/**
|
|
101
|
+
* `error` repaints the border and the meta line. Loading and uploaded are
|
|
102
|
+
* not states here — they are what you put in `meta` and `action`.
|
|
103
|
+
*/
|
|
104
|
+
state?: FileCardVariants['state'];
|
|
105
|
+
/**
|
|
106
|
+
* How far along, 0 to 100. Present at all means the bar is drawn; omit it
|
|
107
|
+
* and there is no bar and no `progressbar` role.
|
|
108
|
+
*/
|
|
109
|
+
progress?: number;
|
|
48
110
|
/**
|
|
49
|
-
* The second line:
|
|
50
|
-
*
|
|
51
|
-
* row is one line.
|
|
111
|
+
* The second line: a type and a size, a progress reading, or the reason an
|
|
112
|
+
* upload failed. Omit it and the row is one line.
|
|
52
113
|
*/
|
|
53
|
-
meta?:
|
|
114
|
+
meta?: React.ReactNode;
|
|
54
115
|
/**
|
|
55
|
-
*
|
|
56
|
-
*
|
|
116
|
+
* What is printed on the badge — the board uses a three-letter
|
|
117
|
+
* abbreviation. Left empty it falls back to a generic file glyph.
|
|
57
118
|
*/
|
|
58
119
|
icon?: React.ReactNode;
|
|
59
120
|
/**
|
|
60
|
-
*
|
|
61
|
-
*
|
|
121
|
+
* What you can do to this file. Whatever you put here brings its own
|
|
122
|
+
* accessible name.
|
|
62
123
|
*/
|
|
63
124
|
action?: React.ReactNode;
|
|
64
125
|
dataTestid: string;
|
|
@@ -1 +1,32 @@
|
|
|
1
|
+
import { VariantProps } from 'class-variance-authority';
|
|
1
2
|
export { FileCard, type FileCardProps } from './FileCard.tsx';
|
|
3
|
+
/**
|
|
4
|
+
* Three axes, and each of them is a thing the component draws rather than a
|
|
5
|
+
* thing the caller writes.
|
|
6
|
+
*
|
|
7
|
+
* `type` is the colour of the badge, not the text in it. The v3 board closes
|
|
8
|
+
* the set at five — PDF, Word, Excel, PowerPoint and a generic — and pins a
|
|
9
|
+
* colour to each (`file-pdf`, `file-word`, `file-excel`, `file-powerpoint`,
|
|
10
|
+
* `file-generic`). Which of the five a `.numbers` or an `.odt` belongs to is
|
|
11
|
+
* still the application's call; see the note in `FileCard.tsx`.
|
|
12
|
+
*
|
|
13
|
+
* `surface` is Figma's axis of the same name, with the same two values every
|
|
14
|
+
* chat attachment carries. The Vue original types it as the attachments'
|
|
15
|
+
* shared `AttachmentSurface`; this package has no `internal/AttachmentMedia`
|
|
16
|
+
* yet, so the prop reads the union off this cva instead — the same two values,
|
|
17
|
+
* to be re-pointed when the attachments port lands.
|
|
18
|
+
*
|
|
19
|
+
* `state` is two values where Figma's `File item` board draws four, and the
|
|
20
|
+
* other two are not missing — they are somewhere else. `Loading` and
|
|
21
|
+
* `Uploaded` differ only in what sits in the `action` slot and what the `meta`
|
|
22
|
+
* slot says, both of which are the caller's content; `Hover` is a tint, which
|
|
23
|
+
* is `:hover`. `Error` is the one state that is neither: it repaints the
|
|
24
|
+
* card's own border and its own meta line, and nothing a caller passes in
|
|
25
|
+
* could reach either.
|
|
26
|
+
*/
|
|
27
|
+
export declare const fileCardVariants: (props?: ({
|
|
28
|
+
type?: "pdf" | "word" | "excel" | "powerpoint" | "file" | null | undefined;
|
|
29
|
+
surface?: "default" | "brand" | null | undefined;
|
|
30
|
+
state?: "default" | "error" | null | undefined;
|
|
31
|
+
} & import('class-variance-authority/types').ClassProp) | undefined) => string;
|
|
32
|
+
export type FileCardVariants = VariantProps<typeof fileCardVariants>;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { AttachmentImage, AttachmentSurface } from '../../internal/AttachmentMedia/index.ts';
|
|
2
|
+
/**
|
|
3
|
+
* The pictures attached to a chat message: one across the bubble, or a 2×2
|
|
4
|
+
* grid with a count on the last cell.
|
|
5
|
+
* Mirrors `@sesame/orxata-core` ImageAttachment.
|
|
6
|
+
*
|
|
7
|
+
* No primitive underneath. shadcn has nothing in this shape and neither does
|
|
8
|
+
* Radix — a grid of image buttons has no roving focus, no selection and no
|
|
9
|
+
* open state to coordinate, so there is nothing for a headless primitive to
|
|
10
|
+
* hold. Each cell is a plain `<button>` in document order, which is what Radix
|
|
11
|
+
* would have rendered anyway.
|
|
12
|
+
*
|
|
13
|
+
* ### `Type` is not a prop, because the content already decides it
|
|
14
|
+
*
|
|
15
|
+
* Figma's axis holds `Image` and `Gallery`, and its own usage board says when
|
|
16
|
+
* each applies: one picture is `Image`, two or more is `Gallery`, and from the
|
|
17
|
+
* fifth the last cell carries `+N`. All three are counts, so `images.length`
|
|
18
|
+
* answers them and a prop would only let a caller contradict it. `More count`
|
|
19
|
+
* goes the same way: it is `images.length - 4`, so pass every picture you have
|
|
20
|
+
* and at most four are ever rendered.
|
|
21
|
+
*
|
|
22
|
+
* ### Each cell is a button
|
|
23
|
+
*
|
|
24
|
+
* The behaviour board puts the viewer behind a click and asks for keyboard
|
|
25
|
+
* access, and unlike `FileCard` — whose root refuses to be a control because it
|
|
26
|
+
* *contains* one — there is nothing interactive in here to nest. A gallery's
|
|
27
|
+
* cells could not be wrapped from outside either: the component renders them.
|
|
28
|
+
* So `onOpen` carries the index, and `images[index]` is the picture to show.
|
|
29
|
+
*
|
|
30
|
+
* ### The `+N` cell is still its own picture
|
|
31
|
+
*
|
|
32
|
+
* It is the fourth image under a scrim, not a separate control, so its
|
|
33
|
+
* accessible name is that image's `alt` and the count is `aria-hidden`
|
|
34
|
+
* decoration on top. The accessibility board asks instead for an aria-label
|
|
35
|
+
* reading "see N more images" — which this package cannot supply, because it
|
|
36
|
+
* ships no strings of its own and has no catalogue to add one to. Opening the
|
|
37
|
+
* viewer at the fourth picture reaches the rest from there.
|
|
38
|
+
*/
|
|
39
|
+
export interface ImageAttachmentProps extends React.ComponentPropsWithoutRef<'div'> {
|
|
40
|
+
/**
|
|
41
|
+
* Every picture attached, in order. One renders across the bubble; two or
|
|
42
|
+
* more render as a grid of up to four, and the rest become the `+N`.
|
|
43
|
+
*/
|
|
44
|
+
images: AttachmentImage[];
|
|
45
|
+
/**
|
|
46
|
+
* Which side of the bubble this sits in: `brand` inside the reader's own
|
|
47
|
+
* message, `default` inside anyone else's.
|
|
48
|
+
*/
|
|
49
|
+
surface?: AttachmentSurface;
|
|
50
|
+
/**
|
|
51
|
+
* A picture was activated. The index is into `images`, so the last cell of a
|
|
52
|
+
* clipped gallery still reports `3` and the viewer reaches the rest from
|
|
53
|
+
* there. Ports the Vue `@open` emit.
|
|
54
|
+
*/
|
|
55
|
+
onOpen?: (index: number) => void;
|
|
56
|
+
dataTestid: string;
|
|
57
|
+
}
|
|
58
|
+
export declare const ImageAttachment: import('react').ForwardRefExoticComponent<ImageAttachmentProps & import('react').RefAttributes<HTMLDivElement>>;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { AttachmentImage, AttachmentSurface } from '../../internal/AttachmentMedia/index.ts';
|
|
3
|
+
/**
|
|
4
|
+
* A URL shared in a chat, unfurled: the page's image, its domain, its title and
|
|
5
|
+
* an extract.
|
|
6
|
+
* Mirrors `@sesame/orxata-core` LinkPreview.
|
|
7
|
+
*
|
|
8
|
+
* No primitive underneath. shadcn has no link card and Radix has none either —
|
|
9
|
+
* the whole thing is one `<a>`, which is what Radix's `Primitive` would have
|
|
10
|
+
* rendered, and there is no state, no keyboard contract and no composition for
|
|
11
|
+
* a headless primitive to hold. The image, its tint and the `Surface` axis come
|
|
12
|
+
* from `AttachmentMedia`, shared with the other four attachments.
|
|
13
|
+
*
|
|
14
|
+
* ### The card is one link, named by its title
|
|
15
|
+
*
|
|
16
|
+
* The accessibility board asks for exactly that, and the domain and the extract
|
|
17
|
+
* are inside the `<a>` too — so `aria-labelledby` points the link's name at the
|
|
18
|
+
* title and leaves the other two as text a reader still reaches. Without it the
|
|
19
|
+
* name would be all three lines read out as one sentence.
|
|
20
|
+
*
|
|
21
|
+
* `children` is therefore the title, and it is the one piece of content this
|
|
22
|
+
* component cannot do without: an empty one leaves the link with no accessible
|
|
23
|
+
* name at all.
|
|
24
|
+
*
|
|
25
|
+
* The board also asks the card to announce that it opens in a new tab. The
|
|
26
|
+
* `target` and the `rel` are here; the wording is not, because this package
|
|
27
|
+
* ships no strings and has no catalogue to add one to.
|
|
28
|
+
*
|
|
29
|
+
* ### Three nodes and not three strings
|
|
30
|
+
*
|
|
31
|
+
* `title` and `description` are names the composition rule does not allow as
|
|
32
|
+
* props, and a domain beside them as a plain string prop would be the same
|
|
33
|
+
* component taking its text two different ways. So all three are `ReactNode` —
|
|
34
|
+
* which is also what lets a caller put a `<time>`, a mark or an abbreviation in
|
|
35
|
+
* one, and what makes the sibling attachments consistent:
|
|
36
|
+
* `OxVideoAttachment`'s duration is a node for the same reason.
|
|
37
|
+
*
|
|
38
|
+
* ### Without an image the header is simply gone
|
|
39
|
+
*
|
|
40
|
+
* The usage board's rule, and it needs no flag: `image` is optional and the
|
|
41
|
+
* media is conditional on it, which leaves the text block alone in the card
|
|
42
|
+
* rather than an empty grey band above it. That is the one place this differs
|
|
43
|
+
* from `OxVideoAttachment`, where the tint *is* the placeholder.
|
|
44
|
+
*/
|
|
45
|
+
export interface LinkPreviewProps extends React.ComponentPropsWithoutRef<'a'> {
|
|
46
|
+
/** Where the link goes. Opened in a new tab, as the behaviour board asks. */
|
|
47
|
+
href: string;
|
|
48
|
+
/**
|
|
49
|
+
* The page's image. Omitted — or absent from the page — leaves the card as
|
|
50
|
+
* its text block, which is the usage board's "no image" pattern. The boards
|
|
51
|
+
* call it decoration, so pass `alt: ''` unless it carries meaning of its own.
|
|
52
|
+
*/
|
|
53
|
+
image?: AttachmentImage;
|
|
54
|
+
/**
|
|
55
|
+
* Which side of the bubble this sits in: `brand` inside the reader's own
|
|
56
|
+
* message, `default` inside anyone else's.
|
|
57
|
+
*/
|
|
58
|
+
surface?: AttachmentSurface;
|
|
59
|
+
/** The domain of the shared URL, beside a globe glyph. Ports `#domain`. */
|
|
60
|
+
domain?: ReactNode;
|
|
61
|
+
/** An extract from the page. Two lines, then an ellipsis. Ports `#description`. */
|
|
62
|
+
description?: ReactNode;
|
|
63
|
+
dataTestid: string;
|
|
64
|
+
}
|
|
65
|
+
export declare const LinkPreview: import('react').ForwardRefExoticComponent<LinkPreviewProps & import('react').RefAttributes<HTMLAnchorElement>>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { LinkPreview, type LinkPreviewProps } from './LinkPreview.tsx';
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { AttachmentImage, AttachmentSurface } from '../../internal/AttachmentMedia/index.ts';
|
|
3
|
+
/**
|
|
4
|
+
* A place shared in a chat: a map band, the place's name and its address.
|
|
5
|
+
* Mirrors `@sesame/orxata-core` LocationAttachment.
|
|
6
|
+
*
|
|
7
|
+
* No primitive underneath, and here there was never a candidate — shadcn has
|
|
8
|
+
* no map card, Radix has no map anything, and the whole component is one `<a>`
|
|
9
|
+
* with no state, no keyboard contract beyond a link's own and nothing to
|
|
10
|
+
* coordinate. `AttachmentMedia` supplies the band, its tint and the `Surface`
|
|
11
|
+
* axis, shared with the other four attachments.
|
|
12
|
+
*
|
|
13
|
+
* ### The map is a prop, and the pin is what shows without one
|
|
14
|
+
*
|
|
15
|
+
* A prop rather than a node, on two counts. `OxImageAttachment`,
|
|
16
|
+
* `OxVideoAttachment` and `OxLinkPreview` all take their picture as an
|
|
17
|
+
* `image?: AttachmentImage`, and a node here would make one of the four odd for
|
|
18
|
+
* no gain a caller could use. And what the board draws in the band is a static
|
|
19
|
+
* map render — a URL, the same kind of thing as the `href` beside it, not
|
|
20
|
+
* arbitrary markup.
|
|
21
|
+
*
|
|
22
|
+
* The image **replaces** the pin rather than sitting under it, because the
|
|
23
|
+
* board's anatomy calls part one "map (placeholder)" with the pin inside it:
|
|
24
|
+
* the pin is what stands in for a map that is not there. So it is
|
|
25
|
+
* `AttachmentMedia`'s `placeholder`, which is also what makes it the
|
|
26
|
+
* failed-to-load state for free.
|
|
27
|
+
*
|
|
28
|
+
* Without an image the band is decoration: `AttachmentMedia` marks a
|
|
29
|
+
* placeholder it cannot name `aria-hidden`, which is the accessibility board's
|
|
30
|
+
* "the map is decorative". With one, the `alt` decides — and `''` is the right
|
|
31
|
+
* answer for a map of an address the line below already spells out.
|
|
32
|
+
*
|
|
33
|
+
* ### One link, named by what is in it
|
|
34
|
+
*
|
|
35
|
+
* Activating it opens the device's map application, which is an `href` — a
|
|
36
|
+
* `geo:` URI or a maps URL, the application's choice. The place and the address
|
|
37
|
+
* both sit inside the `<a>`, so the link's accessible name is the two of them,
|
|
38
|
+
* which is what the accessibility board asks for bar its "Location:" prefix.
|
|
39
|
+
* That prefix is a string, and this package ships none.
|
|
40
|
+
*
|
|
41
|
+
* Unlike `OxLinkPreview` there is no `aria-labelledby` here: that one has three
|
|
42
|
+
* lines and a board saying the name is the title alone, while this one has two
|
|
43
|
+
* and a board saying the name is both.
|
|
44
|
+
*
|
|
45
|
+
* ### Place and address are nodes
|
|
46
|
+
*
|
|
47
|
+
* The same reason as the siblings: `OxLinkPreview` carries a title and a
|
|
48
|
+
* description, names the composition rule does not allow as props, so the
|
|
49
|
+
* family takes its text as nodes rather than two different ways.
|
|
50
|
+
*/
|
|
51
|
+
export interface LocationAttachmentProps extends React.ComponentPropsWithoutRef<'a'> {
|
|
52
|
+
/**
|
|
53
|
+
* Where the card goes — a `geo:` URI or a maps URL. Which one, and whether it
|
|
54
|
+
* carries coordinates or a query, is the application's knowledge.
|
|
55
|
+
*/
|
|
56
|
+
href: string;
|
|
57
|
+
/**
|
|
58
|
+
* The static map of the place. It replaces the pin, which is the placeholder
|
|
59
|
+
* it stands in for; omitted — or failed to load — leaves the pin on the tint.
|
|
60
|
+
*/
|
|
61
|
+
image?: AttachmentImage;
|
|
62
|
+
/**
|
|
63
|
+
* Which side of the bubble this sits in: `brand` inside the reader's own
|
|
64
|
+
* message, `default` inside anyone else's.
|
|
65
|
+
*/
|
|
66
|
+
surface?: AttachmentSurface;
|
|
67
|
+
/** The full address. One line, then an ellipsis. Ports the `#address` slot. */
|
|
68
|
+
address?: ReactNode;
|
|
69
|
+
dataTestid: string;
|
|
70
|
+
}
|
|
71
|
+
export declare const LocationAttachment: import('react').ForwardRefExoticComponent<LocationAttachmentProps & import('react').RefAttributes<HTMLAnchorElement>>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { LocationAttachment, type LocationAttachmentProps, } from './LocationAttachment.tsx';
|