@charcuterie/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/CHANGELOG.md +13 -0
- package/README.md +161 -0
- package/dist/Alert/Alert.d.ts +88 -0
- package/dist/Alert/Alert.d.ts.map +1 -0
- package/dist/Alert/Alert.js +67 -0
- package/dist/Alert/Alert.js.map +1 -0
- package/dist/Badge/Badge.d.ts +54 -0
- package/dist/Badge/Badge.d.ts.map +1 -0
- package/dist/Badge/Badge.js +54 -0
- package/dist/Badge/Badge.js.map +1 -0
- package/dist/Badge/useIsTextClipped.d.ts +22 -0
- package/dist/Badge/useIsTextClipped.d.ts.map +1 -0
- package/dist/Badge/useIsTextClipped.js +57 -0
- package/dist/Badge/useIsTextClipped.js.map +1 -0
- package/dist/Button/Button.d.ts +58 -0
- package/dist/Button/Button.d.ts.map +1 -0
- package/dist/Button/Button.js +37 -0
- package/dist/Button/Button.js.map +1 -0
- package/dist/Card/Card.d.ts +44 -0
- package/dist/Card/Card.d.ts.map +1 -0
- package/dist/Card/Card.js +59 -0
- package/dist/Card/Card.js.map +1 -0
- package/dist/EmptyState/EmptyState.d.ts +42 -0
- package/dist/EmptyState/EmptyState.d.ts.map +1 -0
- package/dist/EmptyState/EmptyState.js +37 -0
- package/dist/EmptyState/EmptyState.js.map +1 -0
- package/dist/IconButton/IconButton.d.ts +34 -0
- package/dist/IconButton/IconButton.d.ts.map +1 -0
- package/dist/IconButton/IconButton.js +23 -0
- package/dist/IconButton/IconButton.js.map +1 -0
- package/dist/LiveStatusIndicator/LiveStatusIndicator.d.ts +37 -0
- package/dist/LiveStatusIndicator/LiveStatusIndicator.d.ts.map +1 -0
- package/dist/LiveStatusIndicator/LiveStatusIndicator.js +39 -0
- package/dist/LiveStatusIndicator/LiveStatusIndicator.js.map +1 -0
- package/dist/MediaTile/MediaTile.d.ts +46 -0
- package/dist/MediaTile/MediaTile.d.ts.map +1 -0
- package/dist/MediaTile/MediaTile.js +79 -0
- package/dist/MediaTile/MediaTile.js.map +1 -0
- package/dist/MediaTile/mediaStatus.d.ts +22 -0
- package/dist/MediaTile/mediaStatus.d.ts.map +1 -0
- package/dist/MediaTile/mediaStatus.js +24 -0
- package/dist/MediaTile/mediaStatus.js.map +1 -0
- package/dist/Modal/Modal.d.ts +66 -0
- package/dist/Modal/Modal.d.ts.map +1 -0
- package/dist/Modal/Modal.js +111 -0
- package/dist/Modal/Modal.js.map +1 -0
- package/dist/Modal/lockScrollBehind.d.ts +22 -0
- package/dist/Modal/lockScrollBehind.d.ts.map +1 -0
- package/dist/Modal/lockScrollBehind.js +48 -0
- package/dist/Modal/lockScrollBehind.js.map +1 -0
- package/dist/Popover/Popover.d.ts +66 -0
- package/dist/Popover/Popover.d.ts.map +1 -0
- package/dist/Popover/Popover.js +88 -0
- package/dist/Popover/Popover.js.map +1 -0
- package/dist/ProgressBar/ProgressBar.d.ts +51 -0
- package/dist/ProgressBar/ProgressBar.d.ts.map +1 -0
- package/dist/ProgressBar/ProgressBar.js +50 -0
- package/dist/ProgressBar/ProgressBar.js.map +1 -0
- package/dist/ProgressBar/progressValue.d.ts +44 -0
- package/dist/ProgressBar/progressValue.d.ts.map +1 -0
- package/dist/ProgressBar/progressValue.js +55 -0
- package/dist/ProgressBar/progressValue.js.map +1 -0
- package/dist/SegmentedControl/SegmentedControl.d.ts +59 -0
- package/dist/SegmentedControl/SegmentedControl.d.ts.map +1 -0
- package/dist/SegmentedControl/SegmentedControl.js +129 -0
- package/dist/SegmentedControl/SegmentedControl.js.map +1 -0
- package/dist/SegmentedControl/SegmentedOption.d.ts +28 -0
- package/dist/SegmentedControl/SegmentedOption.d.ts.map +1 -0
- package/dist/SegmentedControl/SegmentedOption.js +40 -0
- package/dist/SegmentedControl/SegmentedOption.js.map +1 -0
- package/dist/Skeleton/Skeleton.d.ts +30 -0
- package/dist/Skeleton/Skeleton.d.ts.map +1 -0
- package/dist/Skeleton/Skeleton.js +43 -0
- package/dist/Skeleton/Skeleton.js.map +1 -0
- package/dist/Spinner/Spinner.d.ts +37 -0
- package/dist/Spinner/Spinner.d.ts.map +1 -0
- package/dist/Spinner/Spinner.js +33 -0
- package/dist/Spinner/Spinner.js.map +1 -0
- package/dist/Tabs/TabTrigger.d.ts +32 -0
- package/dist/Tabs/TabTrigger.d.ts.map +1 -0
- package/dist/Tabs/TabTrigger.js +62 -0
- package/dist/Tabs/TabTrigger.js.map +1 -0
- package/dist/Tabs/Tabs.d.ts +95 -0
- package/dist/Tabs/Tabs.d.ts.map +1 -0
- package/dist/Tabs/Tabs.js +195 -0
- package/dist/Tabs/Tabs.js.map +1 -0
- package/dist/VisuallyHidden/VisuallyHidden.d.ts +25 -0
- package/dist/VisuallyHidden/VisuallyHidden.d.ts.map +1 -0
- package/dist/VisuallyHidden/VisuallyHidden.js +23 -0
- package/dist/VisuallyHidden/VisuallyHidden.js.map +1 -0
- package/dist/controlStyles.d.ts +50 -0
- package/dist/controlStyles.d.ts.map +1 -0
- package/dist/controlStyles.js +60 -0
- package/dist/controlStyles.js.map +1 -0
- package/dist/expectNoAxeViolations.testHelpers.d.ts +19 -0
- package/dist/expectNoAxeViolations.testHelpers.d.ts.map +1 -0
- package/dist/expectNoAxeViolations.testHelpers.js +32 -0
- package/dist/expectNoAxeViolations.testHelpers.js.map +1 -0
- package/dist/index.d.ts +75 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +55 -0
- package/dist/index.js.map +1 -0
- package/dist/intentStyles.d.ts +76 -0
- package/dist/intentStyles.d.ts.map +1 -0
- package/dist/intentStyles.js +150 -0
- package/dist/intentStyles.js.map +1 -0
- package/dist/mountStory.testHelpers.d.ts +30 -0
- package/dist/mountStory.testHelpers.d.ts.map +1 -0
- package/dist/mountStory.testHelpers.js +19 -0
- package/dist/mountStory.testHelpers.js.map +1 -0
- package/dist/statusIntent.d.ts +33 -0
- package/dist/statusIntent.d.ts.map +1 -0
- package/dist/statusIntent.js +99 -0
- package/dist/statusIntent.js.map +1 -0
- package/dist/styles.css +130 -0
- package/dist/testing/expectAgentDrivable.d.ts +53 -0
- package/dist/testing/expectAgentDrivable.d.ts.map +1 -0
- package/dist/testing/expectAgentDrivable.js +163 -0
- package/dist/testing/expectAgentDrivable.js.map +1 -0
- package/dist/testing/index.d.ts +11 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +10 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/toClassName.d.ts +12 -0
- package/dist/toClassName.d.ts.map +1 -0
- package/dist/toClassName.js +12 -0
- package/dist/toClassName.js.map +1 -0
- package/dist/tokens.d.ts +16 -0
- package/dist/tokens.d.ts.map +1 -0
- package/dist/tokens.js +16 -0
- package/dist/tokens.js.map +1 -0
- package/package.json +70 -0
- package/src/Alert/Alert.mdx +70 -0
- package/src/Alert/Alert.stories.tsx +218 -0
- package/src/Alert/Alert.test.tsx +112 -0
- package/src/Alert/Alert.tsx +166 -0
- package/src/Badge/Badge.mdx +43 -0
- package/src/Badge/Badge.stories.tsx +229 -0
- package/src/Badge/Badge.test.tsx +153 -0
- package/src/Badge/Badge.tsx +130 -0
- package/src/Badge/useIsTextClipped.ts +77 -0
- package/src/Button/Button.mdx +44 -0
- package/src/Button/Button.stories.tsx +175 -0
- package/src/Button/Button.test.tsx +114 -0
- package/src/Button/Button.tsx +125 -0
- package/src/Card/Card.mdx +53 -0
- package/src/Card/Card.stories.tsx +207 -0
- package/src/Card/Card.test.tsx +56 -0
- package/src/Card/Card.tsx +142 -0
- package/src/EmptyState/EmptyState.mdx +40 -0
- package/src/EmptyState/EmptyState.stories.tsx +183 -0
- package/src/EmptyState/EmptyState.test.tsx +41 -0
- package/src/EmptyState/EmptyState.tsx +114 -0
- package/src/IconButton/IconButton.mdx +34 -0
- package/src/IconButton/IconButton.stories.tsx +191 -0
- package/src/IconButton/IconButton.test.tsx +70 -0
- package/src/IconButton/IconButton.tsx +54 -0
- package/src/LiveStatusIndicator/LiveStatusIndicator.mdx +43 -0
- package/src/LiveStatusIndicator/LiveStatusIndicator.stories.tsx +199 -0
- package/src/LiveStatusIndicator/LiveStatusIndicator.test.tsx +63 -0
- package/src/LiveStatusIndicator/LiveStatusIndicator.tsx +116 -0
- package/src/MediaTile/MediaTile.mdx +51 -0
- package/src/MediaTile/MediaTile.stories.tsx +211 -0
- package/src/MediaTile/MediaTile.test.tsx +41 -0
- package/src/MediaTile/MediaTile.tsx +227 -0
- package/src/MediaTile/mediaStatus.ts +28 -0
- package/src/Modal/Modal.mdx +113 -0
- package/src/Modal/Modal.stories.tsx +272 -0
- package/src/Modal/Modal.test.tsx +248 -0
- package/src/Modal/Modal.tsx +228 -0
- package/src/Modal/lockScrollBehind.ts +59 -0
- package/src/Popover/Popover.mdx +102 -0
- package/src/Popover/Popover.stories.tsx +247 -0
- package/src/Popover/Popover.test.tsx +191 -0
- package/src/Popover/Popover.tsx +164 -0
- package/src/ProgressBar/ProgressBar.mdx +48 -0
- package/src/ProgressBar/ProgressBar.stories.tsx +232 -0
- package/src/ProgressBar/ProgressBar.test.tsx +74 -0
- package/src/ProgressBar/ProgressBar.tsx +172 -0
- package/src/ProgressBar/progressValue.test.ts +101 -0
- package/src/ProgressBar/progressValue.ts +87 -0
- package/src/SegmentedControl/SegmentedControl.mdx +71 -0
- package/src/SegmentedControl/SegmentedControl.stories.tsx +150 -0
- package/src/SegmentedControl/SegmentedControl.test.tsx +208 -0
- package/src/SegmentedControl/SegmentedControl.tsx +219 -0
- package/src/SegmentedControl/SegmentedOption.tsx +94 -0
- package/src/Skeleton/Skeleton.mdx +30 -0
- package/src/Skeleton/Skeleton.stories.tsx +130 -0
- package/src/Skeleton/Skeleton.test.tsx +47 -0
- package/src/Skeleton/Skeleton.tsx +106 -0
- package/src/Spinner/Spinner.mdx +36 -0
- package/src/Spinner/Spinner.stories.tsx +118 -0
- package/src/Spinner/Spinner.test.tsx +38 -0
- package/src/Spinner/Spinner.tsx +92 -0
- package/src/Tabs/TabTrigger.tsx +126 -0
- package/src/Tabs/Tabs.mdx +126 -0
- package/src/Tabs/Tabs.stories.tsx +187 -0
- package/src/Tabs/Tabs.test.tsx +283 -0
- package/src/Tabs/Tabs.tsx +333 -0
- package/src/VisuallyHidden/VisuallyHidden.mdx +29 -0
- package/src/VisuallyHidden/VisuallyHidden.stories.tsx +58 -0
- package/src/VisuallyHidden/VisuallyHidden.test.tsx +31 -0
- package/src/VisuallyHidden/VisuallyHidden.tsx +43 -0
- package/src/argTypes.storyHelpers.ts +115 -0
- package/src/board.storyHelpers.tsx +150 -0
- package/src/controlStyles.ts +79 -0
- package/src/expectNoAxeViolations.testHelpers.ts +41 -0
- package/src/icons.storyHelpers.tsx +116 -0
- package/src/index.ts +132 -0
- package/src/intentStyles.ts +207 -0
- package/src/mdxReferences.test.ts +95 -0
- package/src/mountStory.testHelpers.ts +41 -0
- package/src/sourceRules.test.ts +303 -0
- package/src/statusIntent.test.ts +136 -0
- package/src/statusIntent.ts +137 -0
- package/src/storyControls.test.ts +128 -0
- package/src/styles.css +130 -0
- package/src/tailwindCandidates.test.ts +377 -0
- package/src/testing/expectAgentDrivable.test.tsx +71 -0
- package/src/testing/expectAgentDrivable.ts +273 -0
- package/src/testing/index.ts +17 -0
- package/src/toClassName.ts +13 -0
- package/src/tokens.ts +16 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@charcuterie/ui` — the one sanctioned barrel.
|
|
3
|
+
*
|
|
4
|
+
* Components never import each other through this file; they import
|
|
5
|
+
* the sibling module directly (`../Spinner/Spinner.tsx`). A barrel
|
|
6
|
+
* that a package's own internals go through turns every component
|
|
7
|
+
* into a dependency of every other one, which is how a 3 KB
|
|
8
|
+
* `Spinner` starts pulling in `MediaTile`.
|
|
9
|
+
*
|
|
10
|
+
* `"./src/*"` is also in `exports`, for the budget-sensitive
|
|
11
|
+
* consumers — `castkit/packages/slatecast` has 60 KB gz to spend and
|
|
12
|
+
* should be able to reach one component without the barrel. Same
|
|
13
|
+
* arrangement as `mux-magic/packages/tools`.
|
|
14
|
+
*
|
|
15
|
+
* The token **types and values** are re-exported at
|
|
16
|
+
* `@charcuterie/ui/tokens` rather than from here, so a React
|
|
17
|
+
* consumer's TypeScript never names a second package while Satori
|
|
18
|
+
* still gets `@charcuterie/tokens` with no React in sight.
|
|
19
|
+
*
|
|
20
|
+
* The token **stylesheet** is not, and M5 is why. There was a
|
|
21
|
+
* `"./tokens.css": "./node_modules/@charcuterie/tokens/dist/theme.css"`
|
|
22
|
+
* export here, and it resolved to nothing in the first real
|
|
23
|
+
* consumer: a hoisting linker puts `@charcuterie/tokens` at the
|
|
24
|
+
* *project* root, not inside this package, so that path exists only
|
|
25
|
+
* under a nesting one. A CSS `@import` fails silently in Tailwind —
|
|
26
|
+
* no error, no utilities, an unstyled app — which is exactly the
|
|
27
|
+
* class of failure `portal:` was chosen to catch
|
|
28
|
+
* ([decision](../../../docs/decisions/2026-07-29-consumers-link-tokens-by-portal-until-publish.md)).
|
|
29
|
+
* So a consumer installs both package names and imports
|
|
30
|
+
* `@charcuterie/tokens/theme.css` by its own name, which is what
|
|
31
|
+
* this package's README has always told it to do.
|
|
32
|
+
*/
|
|
33
|
+
export { Alert } from "./Alert/Alert.js";
|
|
34
|
+
export { Badge } from "./Badge/Badge.js";
|
|
35
|
+
export { Button } from "./Button/Button.js";
|
|
36
|
+
export { Card } from "./Card/Card.js";
|
|
37
|
+
export { BADGE_SIZE_CLASS, CONTROL_SIZE_CLASS, DOT_SIZE_CLASS, ICON_CONTROL_SIZE_CLASS, MIN_TOUCH_TARGET_CLASS, SPINNER_SIZE_CLASS, } from "./controlStyles.js";
|
|
38
|
+
export { EmptyState } from "./EmptyState/EmptyState.js";
|
|
39
|
+
export { IconButton } from "./IconButton/IconButton.js";
|
|
40
|
+
export { DISABLED_CLASS, FOCUS_RING_CLASS, INTENT_APPEARANCE_CLASS, INTENT_CONTENT_CLASS, INTENT_HOVER_CLASS, INTENT_SOLID_FILL_CLASS, } from "./intentStyles.js";
|
|
41
|
+
export { LiveStatusIndicator } from "./LiveStatusIndicator/LiveStatusIndicator.js";
|
|
42
|
+
export { MediaTile } from "./MediaTile/MediaTile.js";
|
|
43
|
+
export { mediaTransitions } from "./MediaTile/mediaStatus.js";
|
|
44
|
+
export { Modal } from "./Modal/Modal.js";
|
|
45
|
+
export { Popover } from "./Popover/Popover.js";
|
|
46
|
+
export { ProgressBar } from "./ProgressBar/ProgressBar.js";
|
|
47
|
+
export { getProgressIntent, toProgressPercent, toProgressValue, } from "./ProgressBar/progressValue.js";
|
|
48
|
+
export { SegmentedControl } from "./SegmentedControl/SegmentedControl.js";
|
|
49
|
+
export { Skeleton } from "./Skeleton/Skeleton.js";
|
|
50
|
+
export { Spinner } from "./Spinner/Spinner.js";
|
|
51
|
+
export { getAsyncIntent, getAsyncLabel, getConnectionIntent, getConnectionLabel, getIsConnectionBusy, } from "./statusIntent.js";
|
|
52
|
+
export { Tabs } from "./Tabs/Tabs.js";
|
|
53
|
+
export { toClassName } from "./toClassName.js";
|
|
54
|
+
export { VisuallyHidden } from "./VisuallyHidden/VisuallyHidden.js";
|
|
55
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAMH,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAmB,CAAA;AAEzC,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAmB,CAAA;AAEzC,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAqB,CAAA;AAM5C,OAAO,EAAE,IAAI,EAAE,MAAM,gBAAiB,CAAA;AAEtC,OAAO,EACL,gBAAgB,EAChB,kBAAkB,EAClB,cAAc,EACd,uBAAuB,EACvB,sBAAsB,EACtB,kBAAkB,GACnB,MAAM,oBAAoB,CAAA;AAK3B,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA6B,CAAA;AAExD,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA6B,CAAA;AAExD,OAAO,EACL,cAAc,EACd,gBAAgB,EAChB,uBAAuB,EACvB,oBAAoB,EACpB,kBAAkB,EAClB,uBAAuB,GACxB,MAAM,mBAAmB,CAAA;AAK1B,OAAO,EAAE,mBAAmB,EAAE,MAAM,8CAA+C,CAAA;AAKnF,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA2B,CAAA;AAErD,OAAO,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAA;AAK7D,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAmB,CAAA;AAEzC,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAuB,CAAA;AAK/C,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA+B,CAAA;AAE3D,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,eAAe,GAChB,MAAM,gCAAgC,CAAA;AAKvC,OAAO,EAAE,gBAAgB,EAAE,MAAM,wCAAyC,CAAA;AAK1E,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAyB,CAAA;AAElD,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAuB,CAAA;AAC/C,OAAO,EACL,cAAc,EACd,aAAa,EACb,mBAAmB,EACnB,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,mBAAmB,CAAA;AAO1B,OAAO,EAAE,IAAI,EAAE,MAAM,gBAAiB,CAAA;AACtC,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAE9C,OAAO,EAAE,cAAc,EAAE,MAAM,oCAAqC,CAAA"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One intent → class map, for the whole fleet.
|
|
3
|
+
*
|
|
4
|
+
* This file is what M5 deletes from rip-deck. `TONE_CLASS` is
|
|
5
|
+
* declared *identically* in `VerdictBadge.tsx` and
|
|
6
|
+
* `TowerAlerts.tsx` today — hardcoded hexes and `slate-400`s, no
|
|
7
|
+
* light mode, no relationship to mux-magic's `statusClassMap` which
|
|
8
|
+
* spells the same idea a fourth way. Six intents x four
|
|
9
|
+
* appearances, named once, is the generalisation.
|
|
10
|
+
*
|
|
11
|
+
* **Every class name is written out in full, and that is not
|
|
12
|
+
* negotiable.** Tailwind v4 scans source text for *complete* class
|
|
13
|
+
* strings, so `` `bg-intent-${intent}-solid` `` generates nothing
|
|
14
|
+
* at all — the element renders unstyled with no error, no warning,
|
|
15
|
+
* and no failing test unless something checks. Something does:
|
|
16
|
+
* `tailwindCandidates.test.ts` compiles every literal in this
|
|
17
|
+
* package through the real Tailwind and fails on any candidate
|
|
18
|
+
* Tailwind cannot generate. That test is the reason this file is
|
|
19
|
+
* allowed to be 24 lines of near-duplication instead of a loop.
|
|
20
|
+
*/
|
|
21
|
+
import type { IntentName } from "@charcuterie/tokens";
|
|
22
|
+
/**
|
|
23
|
+
* Four treatments, and the split is semantic rather than cosmetic:
|
|
24
|
+
*
|
|
25
|
+
* - `solid` — the saturated fill. One per view, usually; it is the
|
|
26
|
+
* thing you are meant to press.
|
|
27
|
+
* - `soft` — the tinted treatment the fleet already reaches for on
|
|
28
|
+
* status pills (`bg-blue-950 text-blue-300` in mux-magic,
|
|
29
|
+
* `bg-amber-950/40 text-amber-200` in rip-deck).
|
|
30
|
+
* - `outline` — border and content only. Reads as secondary
|
|
31
|
+
* without claiming a surface.
|
|
32
|
+
* - `ghost` — nothing until hovered. Toolbars and icon rows.
|
|
33
|
+
*/
|
|
34
|
+
export type IntentAppearance = "ghost" | "outline" | "soft" | "solid";
|
|
35
|
+
export declare const INTENT_APPEARANCE_CLASS: Record<IntentName, Record<IntentAppearance, string>>;
|
|
36
|
+
/**
|
|
37
|
+
* Hover, split out rather than folded into the appearance above —
|
|
38
|
+
* because most things wearing an intent are **not** interactive.
|
|
39
|
+
*
|
|
40
|
+
* A `Badge` that lightens under the cursor is telling the user it
|
|
41
|
+
* can be clicked, and rip-deck's verdict pills (which cannot) would
|
|
42
|
+
* inherit exactly that if the two maps were one. So an interactive
|
|
43
|
+
* component opts in, and a static one cannot accidentally opt in.
|
|
44
|
+
*
|
|
45
|
+
* `solid` hovers toward `solidHover`; the other three land on the
|
|
46
|
+
* intent's tinted surface, which is what gives a ghost toolbar
|
|
47
|
+
* button something to become.
|
|
48
|
+
*/
|
|
49
|
+
export declare const INTENT_HOVER_CLASS: Record<IntentName, Record<IntentAppearance, string>>;
|
|
50
|
+
/**
|
|
51
|
+
* The fill colour on its own, for the parts of a component that are
|
|
52
|
+
* a bar or a dot rather than a box — a `ProgressBar`'s fill, a
|
|
53
|
+
* `LiveStatusIndicator`'s dot. Same values, no border and no hover,
|
|
54
|
+
* because neither of those is a surface anyone can point at.
|
|
55
|
+
*/
|
|
56
|
+
export declare const INTENT_SOLID_FILL_CLASS: Record<IntentName, string>;
|
|
57
|
+
/** The same six as text, for a glyph or a dot's ring. */
|
|
58
|
+
export declare const INTENT_CONTENT_CLASS: Record<IntentName, string>;
|
|
59
|
+
/**
|
|
60
|
+
* The focus ring, in one place, reading the variant's own width and
|
|
61
|
+
* offset.
|
|
62
|
+
*
|
|
63
|
+
* `focus-visible` rather than `focus`: a mouse click on a button
|
|
64
|
+
* should not leave a ring behind, and every hand-rolled button in
|
|
65
|
+
* the inventory either rings on click or — more often — has
|
|
66
|
+
* `outline-none` with nothing put back.
|
|
67
|
+
*/
|
|
68
|
+
export declare const FOCUS_RING_CLASS = "focus-visible:outline-solid focus-visible:outline-(length:--focus-ring-width) focus-visible:outline-offset-(--focus-ring-offset) focus-visible:outline-focus-ring";
|
|
69
|
+
/**
|
|
70
|
+
* WCAG 1.4.3 exempts *inactive* controls from contrast, which is
|
|
71
|
+
* the only reason `content.disabled` is allowed to be this quiet —
|
|
72
|
+
* and the exemption is honest only on something actually disabled.
|
|
73
|
+
* Keeping it in one constant is what stops it drifting onto prose.
|
|
74
|
+
*/
|
|
75
|
+
export declare const DISABLED_CLASS = "disabled:cursor-not-allowed disabled:border-border-subtle disabled:bg-surface-sunken disabled:text-content-disabled disabled:shadow-none";
|
|
76
|
+
//# sourceMappingURL=intentStyles.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"intentStyles.d.ts","sourceRoot":"","sources":["../src/intentStyles.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAA;AAErD;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,gBAAgB,GACxB,OAAO,GACP,SAAS,GACT,MAAM,GACN,OAAO,CAAA;AAEX,eAAO,MAAM,uBAAuB,EAAE,MAAM,CAC1C,UAAU,EACV,MAAM,CAAC,gBAAgB,EAAE,MAAM,CAAC,CAwDjC,CAAA;AAED;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,kBAAkB,EAAE,MAAM,CACrC,UAAU,EACV,MAAM,CAAC,gBAAgB,EAAE,MAAM,CAAC,CAsCjC,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,EAAE,MAAM,CAC1C,UAAU,EACV,MAAM,CAQP,CAAA;AAED,yDAAyD;AACzD,eAAO,MAAM,oBAAoB,EAAE,MAAM,CACvC,UAAU,EACV,MAAM,CAQP,CAAA;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,gBAAgB,sKACwI,CAAA;AAErK;;;;;GAKG;AACH,eAAO,MAAM,cAAc,6IACiH,CAAA"}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One intent → class map, for the whole fleet.
|
|
3
|
+
*
|
|
4
|
+
* This file is what M5 deletes from rip-deck. `TONE_CLASS` is
|
|
5
|
+
* declared *identically* in `VerdictBadge.tsx` and
|
|
6
|
+
* `TowerAlerts.tsx` today — hardcoded hexes and `slate-400`s, no
|
|
7
|
+
* light mode, no relationship to mux-magic's `statusClassMap` which
|
|
8
|
+
* spells the same idea a fourth way. Six intents x four
|
|
9
|
+
* appearances, named once, is the generalisation.
|
|
10
|
+
*
|
|
11
|
+
* **Every class name is written out in full, and that is not
|
|
12
|
+
* negotiable.** Tailwind v4 scans source text for *complete* class
|
|
13
|
+
* strings, so `` `bg-intent-${intent}-solid` `` generates nothing
|
|
14
|
+
* at all — the element renders unstyled with no error, no warning,
|
|
15
|
+
* and no failing test unless something checks. Something does:
|
|
16
|
+
* `tailwindCandidates.test.ts` compiles every literal in this
|
|
17
|
+
* package through the real Tailwind and fails on any candidate
|
|
18
|
+
* Tailwind cannot generate. That test is the reason this file is
|
|
19
|
+
* allowed to be 24 lines of near-duplication instead of a loop.
|
|
20
|
+
*/
|
|
21
|
+
export const INTENT_APPEARANCE_CLASS = {
|
|
22
|
+
neutral: {
|
|
23
|
+
ghost: "border-transparent bg-transparent text-content-primary",
|
|
24
|
+
outline: "border-intent-neutral-border bg-transparent text-intent-neutral-content",
|
|
25
|
+
soft: "border-intent-neutral-border bg-intent-neutral-surface text-intent-neutral-content",
|
|
26
|
+
solid: "border-transparent bg-intent-neutral-solid text-intent-neutral-on-solid",
|
|
27
|
+
},
|
|
28
|
+
accent: {
|
|
29
|
+
ghost: "border-transparent bg-transparent text-intent-accent-content",
|
|
30
|
+
outline: "border-intent-accent-border bg-transparent text-intent-accent-content",
|
|
31
|
+
soft: "border-intent-accent-border bg-intent-accent-surface text-intent-accent-content",
|
|
32
|
+
solid: "border-transparent bg-intent-accent-solid text-intent-accent-on-solid",
|
|
33
|
+
},
|
|
34
|
+
success: {
|
|
35
|
+
ghost: "border-transparent bg-transparent text-intent-success-content",
|
|
36
|
+
outline: "border-intent-success-border bg-transparent text-intent-success-content",
|
|
37
|
+
soft: "border-intent-success-border bg-intent-success-surface text-intent-success-content",
|
|
38
|
+
solid: "border-transparent bg-intent-success-solid text-intent-success-on-solid",
|
|
39
|
+
},
|
|
40
|
+
warning: {
|
|
41
|
+
ghost: "border-transparent bg-transparent text-intent-warning-content",
|
|
42
|
+
outline: "border-intent-warning-border bg-transparent text-intent-warning-content",
|
|
43
|
+
soft: "border-intent-warning-border bg-intent-warning-surface text-intent-warning-content",
|
|
44
|
+
solid: "border-transparent bg-intent-warning-solid text-intent-warning-on-solid",
|
|
45
|
+
},
|
|
46
|
+
danger: {
|
|
47
|
+
ghost: "border-transparent bg-transparent text-intent-danger-content",
|
|
48
|
+
outline: "border-intent-danger-border bg-transparent text-intent-danger-content",
|
|
49
|
+
soft: "border-intent-danger-border bg-intent-danger-surface text-intent-danger-content",
|
|
50
|
+
solid: "border-transparent bg-intent-danger-solid text-intent-danger-on-solid",
|
|
51
|
+
},
|
|
52
|
+
info: {
|
|
53
|
+
ghost: "border-transparent bg-transparent text-intent-info-content",
|
|
54
|
+
outline: "border-intent-info-border bg-transparent text-intent-info-content",
|
|
55
|
+
soft: "border-intent-info-border bg-intent-info-surface text-intent-info-content",
|
|
56
|
+
solid: "border-transparent bg-intent-info-solid text-intent-info-on-solid",
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* Hover, split out rather than folded into the appearance above —
|
|
61
|
+
* because most things wearing an intent are **not** interactive.
|
|
62
|
+
*
|
|
63
|
+
* A `Badge` that lightens under the cursor is telling the user it
|
|
64
|
+
* can be clicked, and rip-deck's verdict pills (which cannot) would
|
|
65
|
+
* inherit exactly that if the two maps were one. So an interactive
|
|
66
|
+
* component opts in, and a static one cannot accidentally opt in.
|
|
67
|
+
*
|
|
68
|
+
* `solid` hovers toward `solidHover`; the other three land on the
|
|
69
|
+
* intent's tinted surface, which is what gives a ghost toolbar
|
|
70
|
+
* button something to become.
|
|
71
|
+
*/
|
|
72
|
+
export const INTENT_HOVER_CLASS = {
|
|
73
|
+
neutral: {
|
|
74
|
+
ghost: "hover:bg-intent-neutral-surface",
|
|
75
|
+
outline: "hover:bg-intent-neutral-surface",
|
|
76
|
+
soft: "hover:bg-intent-neutral-surface-hover",
|
|
77
|
+
solid: "hover:bg-intent-neutral-solid-hover",
|
|
78
|
+
},
|
|
79
|
+
accent: {
|
|
80
|
+
ghost: "hover:bg-intent-accent-surface",
|
|
81
|
+
outline: "hover:bg-intent-accent-surface",
|
|
82
|
+
soft: "hover:bg-intent-accent-surface-hover",
|
|
83
|
+
solid: "hover:bg-intent-accent-solid-hover",
|
|
84
|
+
},
|
|
85
|
+
success: {
|
|
86
|
+
ghost: "hover:bg-intent-success-surface",
|
|
87
|
+
outline: "hover:bg-intent-success-surface",
|
|
88
|
+
soft: "hover:bg-intent-success-surface-hover",
|
|
89
|
+
solid: "hover:bg-intent-success-solid-hover",
|
|
90
|
+
},
|
|
91
|
+
warning: {
|
|
92
|
+
ghost: "hover:bg-intent-warning-surface",
|
|
93
|
+
outline: "hover:bg-intent-warning-surface",
|
|
94
|
+
soft: "hover:bg-intent-warning-surface-hover",
|
|
95
|
+
solid: "hover:bg-intent-warning-solid-hover",
|
|
96
|
+
},
|
|
97
|
+
danger: {
|
|
98
|
+
ghost: "hover:bg-intent-danger-surface",
|
|
99
|
+
outline: "hover:bg-intent-danger-surface",
|
|
100
|
+
soft: "hover:bg-intent-danger-surface-hover",
|
|
101
|
+
solid: "hover:bg-intent-danger-solid-hover",
|
|
102
|
+
},
|
|
103
|
+
info: {
|
|
104
|
+
ghost: "hover:bg-intent-info-surface",
|
|
105
|
+
outline: "hover:bg-intent-info-surface",
|
|
106
|
+
soft: "hover:bg-intent-info-surface-hover",
|
|
107
|
+
solid: "hover:bg-intent-info-solid-hover",
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
/**
|
|
111
|
+
* The fill colour on its own, for the parts of a component that are
|
|
112
|
+
* a bar or a dot rather than a box — a `ProgressBar`'s fill, a
|
|
113
|
+
* `LiveStatusIndicator`'s dot. Same values, no border and no hover,
|
|
114
|
+
* because neither of those is a surface anyone can point at.
|
|
115
|
+
*/
|
|
116
|
+
export const INTENT_SOLID_FILL_CLASS = {
|
|
117
|
+
neutral: "bg-intent-neutral-solid",
|
|
118
|
+
accent: "bg-intent-accent-solid",
|
|
119
|
+
success: "bg-intent-success-solid",
|
|
120
|
+
warning: "bg-intent-warning-solid",
|
|
121
|
+
danger: "bg-intent-danger-solid",
|
|
122
|
+
info: "bg-intent-info-solid",
|
|
123
|
+
};
|
|
124
|
+
/** The same six as text, for a glyph or a dot's ring. */
|
|
125
|
+
export const INTENT_CONTENT_CLASS = {
|
|
126
|
+
neutral: "text-intent-neutral-content",
|
|
127
|
+
accent: "text-intent-accent-content",
|
|
128
|
+
success: "text-intent-success-content",
|
|
129
|
+
warning: "text-intent-warning-content",
|
|
130
|
+
danger: "text-intent-danger-content",
|
|
131
|
+
info: "text-intent-info-content",
|
|
132
|
+
};
|
|
133
|
+
/**
|
|
134
|
+
* The focus ring, in one place, reading the variant's own width and
|
|
135
|
+
* offset.
|
|
136
|
+
*
|
|
137
|
+
* `focus-visible` rather than `focus`: a mouse click on a button
|
|
138
|
+
* should not leave a ring behind, and every hand-rolled button in
|
|
139
|
+
* the inventory either rings on click or — more often — has
|
|
140
|
+
* `outline-none` with nothing put back.
|
|
141
|
+
*/
|
|
142
|
+
export const FOCUS_RING_CLASS = "focus-visible:outline-solid focus-visible:outline-(length:--focus-ring-width) focus-visible:outline-offset-(--focus-ring-offset) focus-visible:outline-focus-ring";
|
|
143
|
+
/**
|
|
144
|
+
* WCAG 1.4.3 exempts *inactive* controls from contrast, which is
|
|
145
|
+
* the only reason `content.disabled` is allowed to be this quiet —
|
|
146
|
+
* and the exemption is honest only on something actually disabled.
|
|
147
|
+
* Keeping it in one constant is what stops it drifting onto prose.
|
|
148
|
+
*/
|
|
149
|
+
export const DISABLED_CLASS = "disabled:cursor-not-allowed disabled:border-border-subtle disabled:bg-surface-sunken disabled:text-content-disabled disabled:shadow-none";
|
|
150
|
+
//# sourceMappingURL=intentStyles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"intentStyles.js","sourceRoot":"","sources":["../src/intentStyles.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAsBH,MAAM,CAAC,MAAM,uBAAuB,GAGhC;IACF,OAAO,EAAE;QACP,KAAK,EACH,wDAAwD;QAC1D,OAAO,EACL,yEAAyE;QAC3E,IAAI,EAAE,oFAAoF;QAC1F,KAAK,EACH,yEAAyE;KAC5E;IACD,MAAM,EAAE;QACN,KAAK,EACH,8DAA8D;QAChE,OAAO,EACL,uEAAuE;QACzE,IAAI,EAAE,iFAAiF;QACvF,KAAK,EACH,uEAAuE;KAC1E;IACD,OAAO,EAAE;QACP,KAAK,EACH,+DAA+D;QACjE,OAAO,EACL,yEAAyE;QAC3E,IAAI,EAAE,oFAAoF;QAC1F,KAAK,EACH,yEAAyE;KAC5E;IACD,OAAO,EAAE;QACP,KAAK,EACH,+DAA+D;QACjE,OAAO,EACL,yEAAyE;QAC3E,IAAI,EAAE,oFAAoF;QAC1F,KAAK,EACH,yEAAyE;KAC5E;IACD,MAAM,EAAE;QACN,KAAK,EACH,8DAA8D;QAChE,OAAO,EACL,uEAAuE;QACzE,IAAI,EAAE,iFAAiF;QACvF,KAAK,EACH,uEAAuE;KAC1E;IACD,IAAI,EAAE;QACJ,KAAK,EACH,4DAA4D;QAC9D,OAAO,EACL,mEAAmE;QACrE,IAAI,EAAE,2EAA2E;QACjF,KAAK,EACH,mEAAmE;KACtE;CACF,CAAA;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAG3B;IACF,OAAO,EAAE;QACP,KAAK,EAAE,iCAAiC;QACxC,OAAO,EAAE,iCAAiC;QAC1C,IAAI,EAAE,uCAAuC;QAC7C,KAAK,EAAE,qCAAqC;KAC7C;IACD,MAAM,EAAE;QACN,KAAK,EAAE,gCAAgC;QACvC,OAAO,EAAE,gCAAgC;QACzC,IAAI,EAAE,sCAAsC;QAC5C,KAAK,EAAE,oCAAoC;KAC5C;IACD,OAAO,EAAE;QACP,KAAK,EAAE,iCAAiC;QACxC,OAAO,EAAE,iCAAiC;QAC1C,IAAI,EAAE,uCAAuC;QAC7C,KAAK,EAAE,qCAAqC;KAC7C;IACD,OAAO,EAAE;QACP,KAAK,EAAE,iCAAiC;QACxC,OAAO,EAAE,iCAAiC;QAC1C,IAAI,EAAE,uCAAuC;QAC7C,KAAK,EAAE,qCAAqC;KAC7C;IACD,MAAM,EAAE;QACN,KAAK,EAAE,gCAAgC;QACvC,OAAO,EAAE,gCAAgC;QACzC,IAAI,EAAE,sCAAsC;QAC5C,KAAK,EAAE,oCAAoC;KAC5C;IACD,IAAI,EAAE;QACJ,KAAK,EAAE,8BAA8B;QACrC,OAAO,EAAE,8BAA8B;QACvC,IAAI,EAAE,oCAAoC;QAC1C,KAAK,EAAE,kCAAkC;KAC1C;CACF,CAAA;AAED;;;;;GAKG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAGhC;IACF,OAAO,EAAE,yBAAyB;IAClC,MAAM,EAAE,wBAAwB;IAChC,OAAO,EAAE,yBAAyB;IAClC,OAAO,EAAE,yBAAyB;IAClC,MAAM,EAAE,wBAAwB;IAChC,IAAI,EAAE,sBAAsB;CAC7B,CAAA;AAED,yDAAyD;AACzD,MAAM,CAAC,MAAM,oBAAoB,GAG7B;IACF,OAAO,EAAE,6BAA6B;IACtC,MAAM,EAAE,4BAA4B;IACpC,OAAO,EAAE,6BAA6B;IACtC,OAAO,EAAE,6BAA6B;IACtC,MAAM,EAAE,4BAA4B;IACpC,IAAI,EAAE,0BAA0B;CACjC,CAAA;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAC3B,mKAAmK,CAAA;AAErK;;;;;GAKG;AACH,MAAM,CAAC,MAAM,cAAc,GACzB,0IAA0I,CAAA"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { within } from "storybook/test";
|
|
2
|
+
type Canvas = ReturnType<typeof within>;
|
|
3
|
+
/**
|
|
4
|
+
* Structural on purpose — the one thing this helper needs off a
|
|
5
|
+
* composed story. `ComposedStoryFn` is invariant in its `Args`, so
|
|
6
|
+
* naming it here would force every caller to restate the props of
|
|
7
|
+
* the component it is testing.
|
|
8
|
+
*/
|
|
9
|
+
type MountableStory = {
|
|
10
|
+
run: (context: {
|
|
11
|
+
canvasElement: HTMLElement;
|
|
12
|
+
}) => Promise<void>;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Mount a composed story into a fresh canvas and hand back the same
|
|
16
|
+
* two things a `play` function used to receive.
|
|
17
|
+
*
|
|
18
|
+
* `run()` — rather than rendering the component directly — is what
|
|
19
|
+
* keeps these tests honest after the story/test split: the subject
|
|
20
|
+
* is the story a reader sees, with its args, its decorators, and its
|
|
21
|
+
* loaders, not a second assembly of the same component that can
|
|
22
|
+
* drift from it. It is also what triggers the a11y addon's
|
|
23
|
+
* `afterEach`, so every mount here is an axe run.
|
|
24
|
+
*/
|
|
25
|
+
export declare const mountStory: (story: MountableStory) => Promise<{
|
|
26
|
+
canvas: Canvas;
|
|
27
|
+
canvasElement: HTMLElement;
|
|
28
|
+
}>;
|
|
29
|
+
export {};
|
|
30
|
+
//# sourceMappingURL=mountStory.testHelpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mountStory.testHelpers.d.ts","sourceRoot":"","sources":["../src/mountStory.testHelpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAEvC,KAAK,MAAM,GAAG,UAAU,CAAC,OAAO,MAAM,CAAC,CAAA;AAEvC;;;;;GAKG;AACH,KAAK,cAAc,GAAG;IACpB,GAAG,EAAE,CAAC,OAAO,EAAE;QACb,aAAa,EAAE,WAAW,CAAA;KAC3B,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CACpB,CAAA;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,UAAU,GACrB,OAAO,cAAc,KACpB,OAAO,CAAC;IACT,MAAM,EAAE,MAAM,CAAA;IACd,aAAa,EAAE,WAAW,CAAA;CAC3B,CAQA,CAAA"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { within } from "storybook/test";
|
|
2
|
+
/**
|
|
3
|
+
* Mount a composed story into a fresh canvas and hand back the same
|
|
4
|
+
* two things a `play` function used to receive.
|
|
5
|
+
*
|
|
6
|
+
* `run()` — rather than rendering the component directly — is what
|
|
7
|
+
* keeps these tests honest after the story/test split: the subject
|
|
8
|
+
* is the story a reader sees, with its args, its decorators, and its
|
|
9
|
+
* loaders, not a second assembly of the same component that can
|
|
10
|
+
* drift from it. It is also what triggers the a11y addon's
|
|
11
|
+
* `afterEach`, so every mount here is an axe run.
|
|
12
|
+
*/
|
|
13
|
+
export const mountStory = async (story) => {
|
|
14
|
+
const canvasElement = document.createElement("div");
|
|
15
|
+
document.body.append(canvasElement);
|
|
16
|
+
await story.run({ canvasElement });
|
|
17
|
+
return { canvas: within(canvasElement), canvasElement };
|
|
18
|
+
};
|
|
19
|
+
//# sourceMappingURL=mountStory.testHelpers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mountStory.testHelpers.js","sourceRoot":"","sources":["../src/mountStory.testHelpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAgBvC;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,KAAK,EAC7B,KAAqB,EAIpB,EAAE;IACH,MAAM,aAAa,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAA;IAEnD,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,CAAA;IAEnC,MAAM,KAAK,CAAC,GAAG,CAAC,EAAE,aAAa,EAAE,CAAC,CAAA;IAElC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,aAAa,CAAC,EAAE,aAAa,EAAE,CAAA;AACzD,CAAC,CAAA"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The two shared machines → an intent and a human label.
|
|
3
|
+
*
|
|
4
|
+
* This is the join M2 promised and the reason `Status` earned its
|
|
5
|
+
* place as a fifth state kind. `Badge` and `LiveStatusIndicator`
|
|
6
|
+
* consume `connectionTransitions` / `asyncTransitions` through
|
|
7
|
+
* these functions, so a connection state has **one** colour and
|
|
8
|
+
* **one** wording across the fleet — against four repos that each
|
|
9
|
+
* spell the same four states differently today, and one
|
|
10
|
+
* (`mux-magic`) whose `statusClassMap` is a
|
|
11
|
+
* `Record<string, string>` that silently renders an unknown status
|
|
12
|
+
* with no colour at all.
|
|
13
|
+
*
|
|
14
|
+
* Every switch here ends in `assertNeverStatus`, which is the whole
|
|
15
|
+
* point: add a state to `connectionTransitions` and this file stops
|
|
16
|
+
* compiling. A `Record<string, string>` would have shipped a blank
|
|
17
|
+
* badge instead.
|
|
18
|
+
*/
|
|
19
|
+
import type { AsyncStatus, ConnectionStatus } from "@charcuterie/logic";
|
|
20
|
+
import type { IntentName } from "@charcuterie/tokens";
|
|
21
|
+
export declare const getConnectionIntent: (status: ConnectionStatus) => IntentName;
|
|
22
|
+
/**
|
|
23
|
+
* The ellipsis is a real `…`, not three dots, and the trailing one
|
|
24
|
+
* is load-bearing: it is what makes "Reconnecting…" read as
|
|
25
|
+
* in-progress to someone who glances at a wall-mounted panel from
|
|
26
|
+
* across the room.
|
|
27
|
+
*/
|
|
28
|
+
export declare const getConnectionLabel: (status: ConnectionStatus) => string;
|
|
29
|
+
/** Whether the state is one a moving affordance should mark. */
|
|
30
|
+
export declare const getIsConnectionBusy: (status: ConnectionStatus) => boolean;
|
|
31
|
+
export declare const getAsyncIntent: (status: AsyncStatus) => IntentName;
|
|
32
|
+
export declare const getAsyncLabel: (status: AsyncStatus) => string;
|
|
33
|
+
//# sourceMappingURL=statusIntent.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"statusIntent.d.ts","sourceRoot":"","sources":["../src/statusIntent.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EACV,WAAW,EACX,gBAAgB,EACjB,MAAM,oBAAoB,CAAA;AAE3B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAA;AAErD,eAAO,MAAM,mBAAmB,GAC9B,QAAQ,gBAAgB,KACvB,UAqBF,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,GAC7B,QAAQ,gBAAgB,KACvB,MAiBF,CAAA;AAED,gEAAgE;AAChE,eAAO,MAAM,mBAAmB,GAC9B,QAAQ,gBAAgB,KACvB,OAaF,CAAA;AAED,eAAO,MAAM,cAAc,GACzB,QAAQ,WAAW,KAClB,UAiBF,CAAA;AAED,eAAO,MAAM,aAAa,GACxB,QAAQ,WAAW,KAClB,MAiBF,CAAA"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The two shared machines → an intent and a human label.
|
|
3
|
+
*
|
|
4
|
+
* This is the join M2 promised and the reason `Status` earned its
|
|
5
|
+
* place as a fifth state kind. `Badge` and `LiveStatusIndicator`
|
|
6
|
+
* consume `connectionTransitions` / `asyncTransitions` through
|
|
7
|
+
* these functions, so a connection state has **one** colour and
|
|
8
|
+
* **one** wording across the fleet — against four repos that each
|
|
9
|
+
* spell the same four states differently today, and one
|
|
10
|
+
* (`mux-magic`) whose `statusClassMap` is a
|
|
11
|
+
* `Record<string, string>` that silently renders an unknown status
|
|
12
|
+
* with no colour at all.
|
|
13
|
+
*
|
|
14
|
+
* Every switch here ends in `assertNeverStatus`, which is the whole
|
|
15
|
+
* point: add a state to `connectionTransitions` and this file stops
|
|
16
|
+
* compiling. A `Record<string, string>` would have shipped a blank
|
|
17
|
+
* badge instead.
|
|
18
|
+
*/
|
|
19
|
+
import { assertNeverStatus } from "@charcuterie/logic";
|
|
20
|
+
export const getConnectionIntent = (status) => {
|
|
21
|
+
switch (status) {
|
|
22
|
+
case "connected":
|
|
23
|
+
return "success";
|
|
24
|
+
case "connecting":
|
|
25
|
+
return "info";
|
|
26
|
+
// Warning rather than danger, and this is the distinction the
|
|
27
|
+
// shared machine exists for: a user who has seen data wants
|
|
28
|
+
// "lost it, getting it back", not the cold-start spinner they
|
|
29
|
+
// already sat through, and not the red of a dead link either.
|
|
30
|
+
case "reconnecting":
|
|
31
|
+
return "warning";
|
|
32
|
+
case "disconnected":
|
|
33
|
+
return "danger";
|
|
34
|
+
default:
|
|
35
|
+
return assertNeverStatus(status);
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* The ellipsis is a real `…`, not three dots, and the trailing one
|
|
40
|
+
* is load-bearing: it is what makes "Reconnecting…" read as
|
|
41
|
+
* in-progress to someone who glances at a wall-mounted panel from
|
|
42
|
+
* across the room.
|
|
43
|
+
*/
|
|
44
|
+
export const getConnectionLabel = (status) => {
|
|
45
|
+
switch (status) {
|
|
46
|
+
case "connected":
|
|
47
|
+
return "Connected";
|
|
48
|
+
case "connecting":
|
|
49
|
+
return "Connecting…";
|
|
50
|
+
case "reconnecting":
|
|
51
|
+
return "Reconnecting…";
|
|
52
|
+
case "disconnected":
|
|
53
|
+
return "Disconnected";
|
|
54
|
+
default:
|
|
55
|
+
return assertNeverStatus(status);
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
/** Whether the state is one a moving affordance should mark. */
|
|
59
|
+
export const getIsConnectionBusy = (status) => {
|
|
60
|
+
switch (status) {
|
|
61
|
+
case "connecting":
|
|
62
|
+
case "reconnecting":
|
|
63
|
+
return true;
|
|
64
|
+
case "connected":
|
|
65
|
+
case "disconnected":
|
|
66
|
+
return false;
|
|
67
|
+
default:
|
|
68
|
+
return assertNeverStatus(status);
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
export const getAsyncIntent = (status) => {
|
|
72
|
+
switch (status) {
|
|
73
|
+
case "idle":
|
|
74
|
+
return "neutral";
|
|
75
|
+
case "loading":
|
|
76
|
+
return "info";
|
|
77
|
+
case "success":
|
|
78
|
+
return "success";
|
|
79
|
+
case "error":
|
|
80
|
+
return "danger";
|
|
81
|
+
default:
|
|
82
|
+
return assertNeverStatus(status);
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
export const getAsyncLabel = (status) => {
|
|
86
|
+
switch (status) {
|
|
87
|
+
case "idle":
|
|
88
|
+
return "Idle";
|
|
89
|
+
case "loading":
|
|
90
|
+
return "Loading…";
|
|
91
|
+
case "success":
|
|
92
|
+
return "Done";
|
|
93
|
+
case "error":
|
|
94
|
+
return "Failed";
|
|
95
|
+
default:
|
|
96
|
+
return assertNeverStatus(status);
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
//# sourceMappingURL=statusIntent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"statusIntent.js","sourceRoot":"","sources":["../src/statusIntent.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAMH,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AAGtD,MAAM,CAAC,MAAM,mBAAmB,GAAG,CACjC,MAAwB,EACZ,EAAE;IACd,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,WAAW;YACd,OAAO,SAAS,CAAA;QAElB,KAAK,YAAY;YACf,OAAO,MAAM,CAAA;QAEf,8DAA8D;QAC9D,4DAA4D;QAC5D,8DAA8D;QAC9D,8DAA8D;QAC9D,KAAK,cAAc;YACjB,OAAO,SAAS,CAAA;QAElB,KAAK,cAAc;YACjB,OAAO,QAAQ,CAAA;QAEjB;YACE,OAAO,iBAAiB,CAAC,MAAM,CAAC,CAAA;IACpC,CAAC;AACH,CAAC,CAAA;AAED;;;;;GAKG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAChC,MAAwB,EAChB,EAAE;IACV,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,WAAW;YACd,OAAO,WAAW,CAAA;QAEpB,KAAK,YAAY;YACf,OAAO,aAAa,CAAA;QAEtB,KAAK,cAAc;YACjB,OAAO,eAAe,CAAA;QAExB,KAAK,cAAc;YACjB,OAAO,cAAc,CAAA;QAEvB;YACE,OAAO,iBAAiB,CAAC,MAAM,CAAC,CAAA;IACpC,CAAC;AACH,CAAC,CAAA;AAED,gEAAgE;AAChE,MAAM,CAAC,MAAM,mBAAmB,GAAG,CACjC,MAAwB,EACf,EAAE;IACX,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,YAAY,CAAC;QAClB,KAAK,cAAc;YACjB,OAAO,IAAI,CAAA;QAEb,KAAK,WAAW,CAAC;QACjB,KAAK,cAAc;YACjB,OAAO,KAAK,CAAA;QAEd;YACE,OAAO,iBAAiB,CAAC,MAAM,CAAC,CAAA;IACpC,CAAC;AACH,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,cAAc,GAAG,CAC5B,MAAmB,EACP,EAAE;IACd,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,MAAM;YACT,OAAO,SAAS,CAAA;QAElB,KAAK,SAAS;YACZ,OAAO,MAAM,CAAA;QAEf,KAAK,SAAS;YACZ,OAAO,SAAS,CAAA;QAElB,KAAK,OAAO;YACV,OAAO,QAAQ,CAAA;QAEjB;YACE,OAAO,iBAAiB,CAAC,MAAM,CAAC,CAAA;IACpC,CAAC;AACH,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,aAAa,GAAG,CAC3B,MAAmB,EACX,EAAE;IACV,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,MAAM;YACT,OAAO,MAAM,CAAA;QAEf,KAAK,SAAS;YACZ,OAAO,UAAU,CAAA;QAEnB,KAAK,SAAS;YACZ,OAAO,MAAM,CAAA;QAEf,KAAK,OAAO;YACV,OAAO,QAAQ,CAAA;QAEjB;YACE,OAAO,iBAAiB,CAAC,MAAM,CAAC,CAAA;IACpC,CAAC;AACH,CAAC,CAAA"}
|
package/dist/styles.css
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/* `@charcuterie/ui/styles.css` — the four looping affordances, and
|
|
2
|
+
* nothing else.
|
|
3
|
+
*
|
|
4
|
+
* Every other visual in this package is a Tailwind utility reading
|
|
5
|
+
* a token, which is what makes a scheme/density/variant flip a
|
|
6
|
+
* repaint with no re-render. Animation is the one thing utilities
|
|
7
|
+
* cannot express against our tokens: Tailwind's own `animate-spin`
|
|
8
|
+
* and `animate-pulse` hardcode 1s and 2s, ignore
|
|
9
|
+
* `prefers-reduced-motion`, and are therefore beyond the theme's
|
|
10
|
+
* reach — which is exactly why `MotionTokens` carries a separate
|
|
11
|
+
* `loopFast`/`loopSlow` ramp.
|
|
12
|
+
*
|
|
13
|
+
* Consumers import this once, after `@charcuterie/tokens/theme.css`.
|
|
14
|
+
*
|
|
15
|
+
* The reduced-motion block is not a nicety. `variables.css` already
|
|
16
|
+
* collapses every duration to `0ms`, and for a *transition* that is
|
|
17
|
+
* the whole fix — but `animation: sweep 0ms infinite` still paints
|
|
18
|
+
* its first keyframe forever, and the first keyframe of a sweep is
|
|
19
|
+
* an off-screen bar. So each loop is switched off outright and owes
|
|
20
|
+
* a static fallback that still reads correctly, which is what the
|
|
21
|
+
* `@media` block below supplies. A spinner that stops spinning must
|
|
22
|
+
* still say "loading", and it does: the label is text in a
|
|
23
|
+
* `role="status"`, not the animation. */
|
|
24
|
+
|
|
25
|
+
@keyframes charcuterie-spin {
|
|
26
|
+
to {
|
|
27
|
+
transform: rotate(360deg);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
@keyframes charcuterie-shimmer {
|
|
32
|
+
from {
|
|
33
|
+
background-position: 100% 0;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
to {
|
|
37
|
+
background-position: -100% 0;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/* Logical, not `translateX`. A sweep is the one animation whose
|
|
42
|
+
* direction carries meaning — it reads as "moving forward" — so in
|
|
43
|
+
* RTL it has to move the other way, and `inset-inline-start` is
|
|
44
|
+
* what makes that free. */
|
|
45
|
+
@keyframes charcuterie-sweep {
|
|
46
|
+
from {
|
|
47
|
+
inset-inline-start: -40%;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
to {
|
|
51
|
+
inset-inline-start: 100%;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
@keyframes charcuterie-pulse {
|
|
56
|
+
0% {
|
|
57
|
+
opacity: 1;
|
|
58
|
+
transform: scale(1);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
50% {
|
|
62
|
+
opacity: 0.45;
|
|
63
|
+
transform: scale(0.82);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
100% {
|
|
67
|
+
opacity: 1;
|
|
68
|
+
transform: scale(1);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.charcuterie-spin {
|
|
73
|
+
animation: charcuterie-spin var(--duration-loop-fast)
|
|
74
|
+
linear infinite;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.charcuterie-shimmer {
|
|
78
|
+
background-image: linear-gradient(
|
|
79
|
+
90deg,
|
|
80
|
+
var(--color-surface-sunken) 0%,
|
|
81
|
+
var(--color-surface-raised) 50%,
|
|
82
|
+
var(--color-surface-sunken) 100%
|
|
83
|
+
);
|
|
84
|
+
background-size: 200% 100%;
|
|
85
|
+
animation: charcuterie-shimmer var(--duration-loop-slow)
|
|
86
|
+
var(--easing-standard) infinite;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.charcuterie-sweep {
|
|
90
|
+
animation: charcuterie-sweep var(--duration-loop-slow)
|
|
91
|
+
var(--easing-standard) infinite;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.charcuterie-pulse {
|
|
95
|
+
animation: charcuterie-pulse var(--duration-loop-slow)
|
|
96
|
+
var(--easing-standard) infinite;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
@media (prefers-reduced-motion: reduce) {
|
|
100
|
+
.charcuterie-spin,
|
|
101
|
+
.charcuterie-shimmer,
|
|
102
|
+
.charcuterie-sweep,
|
|
103
|
+
.charcuterie-pulse {
|
|
104
|
+
animation: none;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/* Flat, not mid-gradient: a stopped shimmer left holding its
|
|
108
|
+
* first keyframe is a lopsided highlight that reads as a
|
|
109
|
+
* rendering bug rather than as a placeholder. */
|
|
110
|
+
.charcuterie-shimmer {
|
|
111
|
+
background-image: none;
|
|
112
|
+
background-color: var(--color-surface-sunken);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/* The sweep's static fallback carries the whole meaning of the
|
|
116
|
+
* indeterminate state — "working, no measurable progress" — so it
|
|
117
|
+
* fills the track with hatching rather than parking a bar at one
|
|
118
|
+
* end, where it would read as either finished or stuck. */
|
|
119
|
+
.charcuterie-sweep {
|
|
120
|
+
inset-inline-start: 0;
|
|
121
|
+
inline-size: 100%;
|
|
122
|
+
background-image: repeating-linear-gradient(
|
|
123
|
+
135deg,
|
|
124
|
+
currentColor 0,
|
|
125
|
+
currentColor 2px,
|
|
126
|
+
transparent 2px,
|
|
127
|
+
transparent 6px
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
}
|