@botiverse/raft-ui 0.81.0 → 0.83.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 +80 -0
- package/dist/components/choice-list/choice-list.d.mts +8 -0
- package/dist/components/choice-list/choice-list.mjs +37 -16
- package/dist/components/choice-list/choice-list.recipe.mjs +1 -0
- package/dist/components/composer/composer-drop-overlay.d.mts +9 -4
- package/dist/components/composer/composer-drop-overlay.mjs +2 -5
- package/dist/components/dialog/dialog-sheet-context.mjs +7 -2
- package/dist/components/dialog/dialog-sheet.d.mts +3 -2
- package/dist/components/dialog/dialog-sheet.mjs +29 -43
- package/dist/components/dialog/dialog.d.mts +12 -1
- package/dist/components/dialog/dialog.mjs +50 -10
- package/dist/components/fit-toolbar/fit-toolbar.d.mts +110 -0
- package/dist/components/fit-toolbar/fit-toolbar.mjs +469 -0
- package/dist/components/fit-toolbar/fit-toolbar.recipe.mjs +21 -0
- package/dist/components/fit-toolbar/index.d.mts +2 -0
- package/dist/components/list-row/index.d.mts +2 -2
- package/dist/components/list-row/list-row.d.mts +9 -0
- package/dist/components/list-row/list-row.mjs +20 -1
- package/dist/components/list-row/list-row.recipe.d.mts +6 -3
- package/dist/components/list-row/list-row.recipe.mjs +8 -2
- package/dist/components/message-action-card/index.d.mts +2 -2
- package/dist/components/message-action-card/message-action-card.d.mts +8 -0
- package/dist/components/message-action-card/message-action-card.mjs +16 -1
- package/dist/components/message-action-card/message-action-card.recipe.mjs +5 -0
- package/dist/components/mobile-nav/mobile-nav-server.d.mts +21 -8
- package/dist/components/mobile-nav/mobile-nav-server.mjs +142 -13
- package/dist/components/resizable/resizable.recipe.mjs +4 -4
- package/dist/components/scroll-area/use-scrollbars.mjs +2 -1
- package/dist/components/segmented-control/segmented-control.d.mts +1 -1
- package/dist/components/segmented-control/segmented-control.mjs +11 -2
- package/dist/components/selection-toolbar/index.d.mts +2 -0
- package/dist/components/selection-toolbar/selection-toolbar.d.mts +100 -0
- package/dist/components/selection-toolbar/selection-toolbar.mjs +217 -0
- package/dist/components/selection-toolbar/selection-toolbar.recipe.mjs +42 -0
- package/dist/components/steps/otter-animation-visibility.mjs +45 -0
- package/dist/components/steps/otter-indicator.css +131 -106
- package/dist/components/steps/otter-indicator.d.mts +5 -8
- package/dist/components/steps/otter-indicator.mjs +38 -314
- package/dist/components/steps/ottie-assets/error.webp +0 -0
- package/dist/components/steps/ottie-assets/finish.webp +0 -0
- package/dist/components/steps/ottie-assets/process.webp +0 -0
- package/dist/components/steps/ottie-assets/wait.webp +0 -0
- package/dist/index.d.mts +5 -4
- package/dist/index.mjs +5 -4
- package/dist/lib/label-value-fit.mjs +33 -6
- package/dist/lib/use-mobile-viewport.mjs +5 -2
- package/dist/lib/use-soft-keyboard.mjs +72 -0
- package/dist/wip.d.mts +6 -3
- package/dist/wip.mjs +4 -3
- package/package.json +1 -1
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { Button } from "../button/button.mjs";
|
|
2
|
+
import { DropdownMenuItem } from "../dropdown-menu/dropdown-menu.mjs";
|
|
3
|
+
import { Tooltip, TooltipContent, TooltipTrigger } from "../tooltip/tooltip.mjs";
|
|
4
|
+
import { FitToolbar, FitToolbarGroup, FitToolbarMoreItems, useFitItem, useFitMore } from "../fit-toolbar/fit-toolbar.mjs";
|
|
5
|
+
import { selectionToolbar } from "./selection-toolbar.recipe.mjs";
|
|
6
|
+
import { ChevronDown, MoreHorizontal, X } from "lucide-react";
|
|
7
|
+
import { cloneElement, useCallback } from "react";
|
|
8
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
9
|
+
import { mergeProps } from "@base-ui/react/merge-props";
|
|
10
|
+
import { useRender } from "@base-ui/react/use-render";
|
|
11
|
+
import { Toolbar } from "@base-ui/react/toolbar";
|
|
12
|
+
//#region src/components/selection-toolbar/selection-toolbar.tsx
|
|
13
|
+
/**
|
|
14
|
+
* The order things give way in as the toolbar runs out of room: the hint,
|
|
15
|
+
* then the secondary actions into More (the last first), then the labels (the
|
|
16
|
+
* last first, so the first action's goes last), then the other actions into
|
|
17
|
+
* More, the last first. The count and × never do.
|
|
18
|
+
*/
|
|
19
|
+
const HINT_STEP = 10;
|
|
20
|
+
const SECONDARY_STEP = 20;
|
|
21
|
+
const LABEL_STEP = 30;
|
|
22
|
+
const MORE_STEP = 40;
|
|
23
|
+
/**
|
|
24
|
+
* Where the toolbar floats: centered, 12px above the bottom of the list or
|
|
25
|
+
* conversation it sits in (plus the safe area), 20px from its sides. Its
|
|
26
|
+
* parent must be positioned.
|
|
27
|
+
*/
|
|
28
|
+
function SelectionToolbarDock({ className, render, ...props }) {
|
|
29
|
+
const { dock } = selectionToolbar();
|
|
30
|
+
const defaultProps = { className: dock({ className }) };
|
|
31
|
+
return useRender({
|
|
32
|
+
defaultTagName: "div",
|
|
33
|
+
render,
|
|
34
|
+
props: mergeProps(defaultProps, props),
|
|
35
|
+
state: { slot: "selection-toolbar-dock" }
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* The floating toolbar for selected items (messages, tasks): the count, an
|
|
40
|
+
* optional hint, the actions on the selection, More while some are out of
|
|
41
|
+
* the row, then ×. One row at any width: what doesn't fit gives way, measured
|
|
42
|
+
* in the width it floats in, and never wraps. A `role="toolbar"`, one Tab
|
|
43
|
+
* stop with the arrow keys inside: give it an `aria-label`.
|
|
44
|
+
*/
|
|
45
|
+
function SelectionToolbar({ className, ...props }) {
|
|
46
|
+
const { root } = selectionToolbar();
|
|
47
|
+
return /* @__PURE__ */ jsx(FitToolbar, {
|
|
48
|
+
"data-slot": "selection-toolbar",
|
|
49
|
+
className: root({ className }),
|
|
50
|
+
...props
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* What the actions act on ("3 selected"), first. Announced when it changes.
|
|
55
|
+
* Put the number in `SelectionToolbarCountValue`.
|
|
56
|
+
*/
|
|
57
|
+
function SelectionToolbarCount({ className, render, ...props }) {
|
|
58
|
+
const { count } = selectionToolbar();
|
|
59
|
+
const defaultProps = {
|
|
60
|
+
"aria-live": "polite",
|
|
61
|
+
"aria-atomic": true,
|
|
62
|
+
className: count({ className })
|
|
63
|
+
};
|
|
64
|
+
return useRender({
|
|
65
|
+
defaultTagName: "span",
|
|
66
|
+
render,
|
|
67
|
+
props: mergeProps(defaultProps, props),
|
|
68
|
+
state: { slot: "selection-toolbar-count" }
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
/** The number in the count, in Space Mono. */
|
|
72
|
+
function SelectionToolbarCountValue({ className, render, ...props }) {
|
|
73
|
+
const { countValue } = selectionToolbar();
|
|
74
|
+
const defaultProps = { className: countValue({ className }) };
|
|
75
|
+
return useRender({
|
|
76
|
+
defaultTagName: "span",
|
|
77
|
+
render,
|
|
78
|
+
props: mergeProps(defaultProps, props),
|
|
79
|
+
state: { slot: "selection-toolbar-count-value" }
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* A short line after the count, such as "1 will be skipped". The first to
|
|
84
|
+
* give way: it is then read by screen readers only.
|
|
85
|
+
*/
|
|
86
|
+
function SelectionToolbarHint({ className, render, ...props }) {
|
|
87
|
+
const { hint } = selectionToolbar();
|
|
88
|
+
const item = useFitItem({ label: HINT_STEP });
|
|
89
|
+
const defaultProps = {
|
|
90
|
+
className: hint({ className }),
|
|
91
|
+
...item.props
|
|
92
|
+
};
|
|
93
|
+
return useRender({
|
|
94
|
+
defaultTagName: "span",
|
|
95
|
+
render,
|
|
96
|
+
props: mergeProps(defaultProps, props),
|
|
97
|
+
state: { slot: "selection-toolbar-hint" }
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Any other control of the toolbar, such as a thread's select-all checkbox
|
|
102
|
+
* before the count, so the arrow keys reach it. It takes the toolbar's focus
|
|
103
|
+
* handling only: the control keeps its role and its own keys.
|
|
104
|
+
*/
|
|
105
|
+
function SelectionToolbarItem({ children }) {
|
|
106
|
+
const renderItem = useCallback((itemProps) => {
|
|
107
|
+
const { role: _role, type: _type, onClick: _onClick, onKeyDown: _onKeyDown, onKeyUp: _onKeyUp, onMouseDown: _onMouseDown, onPointerDown: _onPointerDown, "aria-disabled": _ariaDisabled, ...composite } = itemProps;
|
|
108
|
+
return cloneElement(children, mergeProps(children.props, {
|
|
109
|
+
...composite,
|
|
110
|
+
"data-selection-toolbar-item": ""
|
|
111
|
+
}));
|
|
112
|
+
}, [children]);
|
|
113
|
+
return /* @__PURE__ */ jsx(Toolbar.Button, {
|
|
114
|
+
nativeButton: false,
|
|
115
|
+
render: renderItem
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
/** The actions on the selection, in one row. */
|
|
119
|
+
function SelectionToolbarActions({ className, ...props }) {
|
|
120
|
+
const { actions } = selectionToolbar();
|
|
121
|
+
return /* @__PURE__ */ jsx(FitToolbarGroup, {
|
|
122
|
+
"data-slot": "selection-toolbar-actions",
|
|
123
|
+
className: actions({ className }),
|
|
124
|
+
...props
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
let warnedMenuWithoutMoreItem = false;
|
|
128
|
+
function warnMenuWithoutMoreItem() {
|
|
129
|
+
if (warnedMenuWithoutMoreItem) return;
|
|
130
|
+
warnedMenuWithoutMoreItem = true;
|
|
131
|
+
console.warn("raft-ui: a SelectionToolbarAction with `menu` has no `moreItem`; it would vanish when it moves into More. Give it its menu as a DropdownMenuSubmenu.");
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* One action on the selection: a 14px icon and a label, an `md` ghost button
|
|
135
|
+
* with a mouse on a desktop, a 44px bar button showing its icon alone on a
|
|
136
|
+
* phone and a touch screen.
|
|
137
|
+
*/
|
|
138
|
+
function SelectionToolbarAction({ icon, children, secondary = false, menu = false, moreItem, className, onClick, disabled, ...props }) {
|
|
139
|
+
const { action, actionLabel, actionChevron } = selectionToolbar({ menu });
|
|
140
|
+
if (process.env.NODE_ENV !== "production" && menu && moreItem === void 0) warnMenuWithoutMoreItem();
|
|
141
|
+
const item = useFitItem({
|
|
142
|
+
label: LABEL_STEP,
|
|
143
|
+
more: secondary ? SECONDARY_STEP : MORE_STEP,
|
|
144
|
+
moreItem: moreItem !== void 0 ? moreItem : menu ? null : /* @__PURE__ */ jsxs(DropdownMenuItem, {
|
|
145
|
+
disabled,
|
|
146
|
+
onClick,
|
|
147
|
+
children: [icon, children]
|
|
148
|
+
})
|
|
149
|
+
});
|
|
150
|
+
return /* @__PURE__ */ jsxs(Tooltip, {
|
|
151
|
+
disabled: !item.taken("label"),
|
|
152
|
+
children: [/* @__PURE__ */ jsxs(TooltipTrigger, {
|
|
153
|
+
"data-slot": "button",
|
|
154
|
+
render: /* @__PURE__ */ jsx(Toolbar.Button, { render: /* @__PURE__ */ jsx(Button, {
|
|
155
|
+
"data-selection-toolbar-action": "",
|
|
156
|
+
"data-secondary": secondary ? "" : void 0,
|
|
157
|
+
variant: "ghost",
|
|
158
|
+
size: "md",
|
|
159
|
+
className: action({ className }),
|
|
160
|
+
onClick,
|
|
161
|
+
disabled,
|
|
162
|
+
...item.props,
|
|
163
|
+
...props
|
|
164
|
+
}) }),
|
|
165
|
+
children: [
|
|
166
|
+
icon,
|
|
167
|
+
/* @__PURE__ */ jsx("span", {
|
|
168
|
+
"data-slot": "selection-toolbar-action-label",
|
|
169
|
+
className: actionLabel(),
|
|
170
|
+
children
|
|
171
|
+
}),
|
|
172
|
+
menu ? /* @__PURE__ */ jsx(ChevronDown, {
|
|
173
|
+
"aria-hidden": true,
|
|
174
|
+
className: actionChevron()
|
|
175
|
+
}) : null
|
|
176
|
+
]
|
|
177
|
+
}), /* @__PURE__ */ jsx(TooltipContent, { children })]
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* More, after the actions, shown only while some are in it. Render it as a
|
|
182
|
+
* `DropdownMenuTrigger` whose menu opens upward (`side="top"`), with
|
|
183
|
+
* `SelectionToolbarMoreItems` in the menu.
|
|
184
|
+
*/
|
|
185
|
+
function SelectionToolbarMore({ className, children, ...props }) {
|
|
186
|
+
const { more } = selectionToolbar();
|
|
187
|
+
const fit = useFitMore();
|
|
188
|
+
return /* @__PURE__ */ jsx(Toolbar.Button, {
|
|
189
|
+
render: /* @__PURE__ */ jsx(Button, {
|
|
190
|
+
"data-selection-toolbar-more": "",
|
|
191
|
+
variant: "ghost",
|
|
192
|
+
size: "md",
|
|
193
|
+
className: more({ className }),
|
|
194
|
+
...fit.props,
|
|
195
|
+
...props
|
|
196
|
+
}),
|
|
197
|
+
children: children ?? /* @__PURE__ */ jsx(MoreHorizontal, {})
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
/** The actions in More, in their order, each as its `moreItem`: put it in More's menu. */
|
|
201
|
+
const SelectionToolbarMoreItems = FitToolbarMoreItems;
|
|
202
|
+
/** ×, last: ends the selection, as Escape does. */
|
|
203
|
+
function SelectionToolbarClose({ className, children, ...props }) {
|
|
204
|
+
const { close } = selectionToolbar();
|
|
205
|
+
return /* @__PURE__ */ jsx(Toolbar.Button, {
|
|
206
|
+
render: /* @__PURE__ */ jsx(Button, {
|
|
207
|
+
"data-selection-toolbar-close": "",
|
|
208
|
+
variant: "ghost",
|
|
209
|
+
size: "icon-md",
|
|
210
|
+
className: close({ className }),
|
|
211
|
+
...props
|
|
212
|
+
}),
|
|
213
|
+
children: children ?? /* @__PURE__ */ jsx(X, {})
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
//#endregion
|
|
217
|
+
export { SelectionToolbar, SelectionToolbarAction, SelectionToolbarActions, SelectionToolbarClose, SelectionToolbarCount, SelectionToolbarCountValue, SelectionToolbarDock, SelectionToolbarHint, SelectionToolbarItem, SelectionToolbarMore, SelectionToolbarMoreItems };
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { tv } from "../../lib/tv.mjs";
|
|
2
|
+
//#region src/components/selection-toolbar/selection-toolbar.recipe.ts
|
|
3
|
+
const selectionToolbar = tv({
|
|
4
|
+
slots: {
|
|
5
|
+
dock: [
|
|
6
|
+
"pointer-events-none absolute inset-x-0 bottom-0 z-10 flex justify-center",
|
|
7
|
+
"px-5 pb-[calc(--spacing(3)+env(safe-area-inset-bottom))]",
|
|
8
|
+
"*:pointer-events-auto"
|
|
9
|
+
],
|
|
10
|
+
root: [
|
|
11
|
+
"w-fit gap-0 md:pointer-fine:gap-1",
|
|
12
|
+
"h-(--header-height) px-(--header-air) py-0",
|
|
13
|
+
"rounded-container bg-layer-popover ring-1 ring-line-seam shadow-float",
|
|
14
|
+
"**:data-[slot=button]:after:inset-x-[-1.5px] md:pointer-fine:**:data-[slot=button]:after:inset-x-[-3px]",
|
|
15
|
+
"*:data-[slot=button]:first:after:-left-[(var(--header-air)+1.5px)] *:data-[slot=button]:last:after:-right-[(var(--header-air)+1.5px)]"
|
|
16
|
+
],
|
|
17
|
+
count: ["shrink-0 pr-0 pl-2 md:pointer-fine:px-2", "whitespace-nowrap text-callout font-bold text-foreground tabular-nums"],
|
|
18
|
+
countValue: ["font-mono"],
|
|
19
|
+
hint: ["shrink-0 pr-1 whitespace-nowrap text-caption text-foreground-hint", "data-fit-label:sr-only"],
|
|
20
|
+
actions: ["gap-0 md:pointer-fine:gap-1"],
|
|
21
|
+
action: [
|
|
22
|
+
"[&_svg:not([class*='size-'])]:size-3.5",
|
|
23
|
+
"size-(--bar-button-size) justify-center px-0 after:inset-y-[-1.5px]",
|
|
24
|
+
"rounded-(--bar-button-radius) md:pointer-fine:rounded-control",
|
|
25
|
+
"md:pointer-fine:w-auto md:pointer-fine:pr-3 md:pointer-fine:pl-2.5",
|
|
26
|
+
"md:pointer-fine:data-fit-label:w-(--control-height) md:pointer-fine:data-fit-label:px-0",
|
|
27
|
+
"data-fit-more:hidden"
|
|
28
|
+
],
|
|
29
|
+
actionLabel: ["sr-only md:pointer-fine:not-sr-only md:pointer-fine:in-data-fit-label:sr-only"],
|
|
30
|
+
actionChevron: ["-ml-1 hidden size-3 md:pointer-fine:block md:pointer-fine:in-data-fit-label:hidden"],
|
|
31
|
+
more: [
|
|
32
|
+
"hidden data-fit-shown:inline-flex [&_svg:not([class*='size-'])]:size-3.5",
|
|
33
|
+
"size-(--bar-button-size) px-0 after:inset-y-[-1.5px]",
|
|
34
|
+
"rounded-(--bar-button-radius) md:pointer-fine:rounded-control",
|
|
35
|
+
"md:pointer-fine:w-(--control-height)"
|
|
36
|
+
],
|
|
37
|
+
close: ["size-(--bar-button-size) after:inset-y-[-1.5px]", "rounded-(--bar-button-radius) md:pointer-fine:rounded-control"]
|
|
38
|
+
},
|
|
39
|
+
variants: { menu: { true: { action: "md:pointer-fine:pr-2.5" } } }
|
|
40
|
+
});
|
|
41
|
+
//#endregion
|
|
42
|
+
export { selectionToolbar };
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
//#region src/components/steps/otter-animation-visibility.ts
|
|
2
|
+
/** One observer/listener per document, regardless of the number of illustrations. */
|
|
3
|
+
const documents = /* @__PURE__ */ new WeakMap();
|
|
4
|
+
function observeOtterAnimation(node) {
|
|
5
|
+
const doc = node.ownerDocument;
|
|
6
|
+
let manager = documents.get(doc);
|
|
7
|
+
if (!manager) {
|
|
8
|
+
const visible = /* @__PURE__ */ new Map();
|
|
9
|
+
const update = (element, intersects) => {
|
|
10
|
+
element.dataset.animate = String(intersects && !doc.hidden);
|
|
11
|
+
};
|
|
12
|
+
const onVisibility = () => {
|
|
13
|
+
for (const [element, intersects] of visible) update(element, intersects);
|
|
14
|
+
};
|
|
15
|
+
const Observer = doc.defaultView?.IntersectionObserver;
|
|
16
|
+
const observer = Observer ? new Observer((entries) => {
|
|
17
|
+
for (const entry of entries) {
|
|
18
|
+
const element = entry.target;
|
|
19
|
+
if (!visible.has(element)) continue;
|
|
20
|
+
visible.set(element, entry.isIntersecting);
|
|
21
|
+
update(element, entry.isIntersecting);
|
|
22
|
+
}
|
|
23
|
+
}) : null;
|
|
24
|
+
doc.addEventListener("visibilitychange", onVisibility);
|
|
25
|
+
manager = { observe(element) {
|
|
26
|
+
visible.set(element, !observer);
|
|
27
|
+
update(element, !observer);
|
|
28
|
+
observer?.observe(element);
|
|
29
|
+
return () => {
|
|
30
|
+
observer?.unobserve(element);
|
|
31
|
+
visible.delete(element);
|
|
32
|
+
delete element.dataset.animate;
|
|
33
|
+
if (visible.size === 0) {
|
|
34
|
+
observer?.disconnect();
|
|
35
|
+
doc.removeEventListener("visibilitychange", onVisibility);
|
|
36
|
+
documents.delete(doc);
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
} };
|
|
40
|
+
documents.set(doc, manager);
|
|
41
|
+
}
|
|
42
|
+
return manager.observe(node);
|
|
43
|
+
}
|
|
44
|
+
//#endregion
|
|
45
|
+
export { observeOtterAnimation };
|
|
@@ -1,141 +1,166 @@
|
|
|
1
|
-
/*
|
|
2
|
-
OtterIndicator — an otter posed by a status (an agent's turn, a step). The
|
|
3
|
-
picture is fixed art; only the badges and the "z" take token colours.
|
|
4
|
-
───────────────────────────────────────────────────────────────────────── */
|
|
5
|
-
|
|
1
|
+
/* Fixed Ottie artwork in separate CSS assets; no image modules/base64 in JS. */
|
|
6
2
|
.r-otter-indicator {
|
|
7
|
-
overflow:
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}
|
|
22
|
-
.r-otter-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
3
|
+
overflow: hidden;
|
|
4
|
+
flex-shrink: 0;
|
|
5
|
+
}
|
|
6
|
+
.r-otter-indicator--wait {
|
|
7
|
+
--r-ottie-art: url("./ottie-assets/wait.webp");
|
|
8
|
+
}
|
|
9
|
+
.r-otter-indicator--process {
|
|
10
|
+
--r-ottie-art: url("./ottie-assets/process.webp");
|
|
11
|
+
}
|
|
12
|
+
.r-otter-indicator--error {
|
|
13
|
+
--r-ottie-art: url("./ottie-assets/error.webp");
|
|
14
|
+
}
|
|
15
|
+
.r-otter-indicator--finish {
|
|
16
|
+
--r-ottie-art: url("./ottie-assets/finish.webp");
|
|
17
|
+
}
|
|
18
|
+
.r-otter-indicator__viewport {
|
|
19
|
+
width: 100%;
|
|
20
|
+
aspect-ratio: 1;
|
|
21
|
+
overflow: hidden;
|
|
22
|
+
}
|
|
23
|
+
.r-otter-indicator__art {
|
|
24
|
+
width: 100%;
|
|
25
|
+
height: 100%;
|
|
26
|
+
position: relative;
|
|
27
|
+
}
|
|
28
|
+
.r-otter-indicator__base,
|
|
29
|
+
.r-otter-indicator__frames,
|
|
30
|
+
.r-otter-indicator__rest {
|
|
31
|
+
position: absolute;
|
|
32
|
+
inset: 0;
|
|
33
|
+
background-image: var(--r-ottie-art);
|
|
34
|
+
background-size: 300% 200%;
|
|
35
|
+
background-repeat: no-repeat;
|
|
36
|
+
background-position: 0% 0%;
|
|
37
|
+
}
|
|
38
|
+
.r-otter-indicator__rest {
|
|
39
|
+
background-size: contain;
|
|
40
|
+
background-position: center;
|
|
41
|
+
transform-origin: 50% 80%;
|
|
42
|
+
}
|
|
43
|
+
.r-otter-indicator--process .r-otter-indicator__frames {
|
|
44
|
+
mask-image:
|
|
45
|
+
radial-gradient(ellipse 11% 9% at 40% 60%, oklch(0 0 0) 88%, transparent 100%),
|
|
46
|
+
radial-gradient(ellipse 11% 9% at 61% 60%, oklch(0 0 0) 88%, transparent 100%);
|
|
47
|
+
}
|
|
48
|
+
.r-otter-indicator--error .r-otter-indicator__frames {
|
|
49
|
+
mask-image: linear-gradient(oklch(0 0 0) 0 58%, transparent 64%);
|
|
50
|
+
}
|
|
51
|
+
.r-otter-indicator--error .r-otter-indicator__frames,
|
|
52
|
+
.r-otter-indicator--finish .r-otter-indicator__frames {
|
|
53
|
+
background-position: 100% 100%;
|
|
54
|
+
}
|
|
55
|
+
.r-otter-indicator__question {
|
|
56
|
+
fill: oklch(0.57 0.09 75);
|
|
57
|
+
font-family: ui-rounded, system-ui, sans-serif;
|
|
27
58
|
}
|
|
28
59
|
@media (prefers-reduced-motion: no-preference) {
|
|
29
|
-
.r-otter-
|
|
30
|
-
animation: r-
|
|
60
|
+
.r-otter-indicator__rest {
|
|
61
|
+
animation: r-ottie-breathe 6s ease-in-out infinite;
|
|
31
62
|
}
|
|
32
|
-
.r-otter-
|
|
33
|
-
animation
|
|
63
|
+
.r-otter-indicator--process .r-otter-indicator__frames {
|
|
64
|
+
animation: r-ottie-type 4.52s steps(1, end) infinite;
|
|
34
65
|
}
|
|
35
|
-
.r-otter-indicator--
|
|
36
|
-
animation: r-
|
|
66
|
+
.r-otter-indicator--error .r-otter-indicator__frames {
|
|
67
|
+
animation: r-ottie-tilt 1.55s steps(1, end) both;
|
|
37
68
|
}
|
|
38
|
-
.r-otter-indicator--
|
|
39
|
-
animation: r-
|
|
69
|
+
.r-otter-indicator--finish .r-otter-indicator__frames {
|
|
70
|
+
animation: r-ottie-finish 1.24s steps(1, end) both;
|
|
40
71
|
}
|
|
41
|
-
.r-otter-
|
|
42
|
-
animation: r-
|
|
72
|
+
.r-otter-indicator__question {
|
|
73
|
+
animation: r-ottie-question 1s linear both;
|
|
43
74
|
}
|
|
44
|
-
.r-otter-indicator
|
|
45
|
-
|
|
75
|
+
.r-otter-indicator .r-otter-indicator__rest,
|
|
76
|
+
.r-otter-indicator .r-otter-indicator__frames,
|
|
77
|
+
.r-otter-indicator .r-otter-indicator__question {
|
|
78
|
+
animation-play-state: paused;
|
|
46
79
|
}
|
|
47
|
-
.r-otter-indicator
|
|
48
|
-
|
|
80
|
+
.r-otter-indicator[data-animate="true"] .r-otter-indicator__rest,
|
|
81
|
+
.r-otter-indicator[data-animate="true"] .r-otter-indicator__frames,
|
|
82
|
+
.r-otter-indicator[data-animate="true"] .r-otter-indicator__question {
|
|
83
|
+
animation-play-state: running;
|
|
49
84
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
85
|
+
}
|
|
86
|
+
@keyframes r-ottie-breathe {
|
|
87
|
+
0%,
|
|
88
|
+
100% {
|
|
89
|
+
transform: scaleY(1);
|
|
55
90
|
}
|
|
56
|
-
|
|
57
|
-
|
|
91
|
+
50% {
|
|
92
|
+
transform: scaleY(1.006);
|
|
58
93
|
}
|
|
59
|
-
|
|
60
|
-
|
|
94
|
+
}
|
|
95
|
+
@keyframes r-ottie-type {
|
|
96
|
+
0%,
|
|
97
|
+
100% {
|
|
98
|
+
background-position: 0% 0%;
|
|
61
99
|
}
|
|
62
|
-
.
|
|
63
|
-
|
|
100
|
+
48.6726% {
|
|
101
|
+
background-position: 50% 0%;
|
|
64
102
|
}
|
|
65
|
-
.
|
|
66
|
-
|
|
103
|
+
52.6549% {
|
|
104
|
+
background-position: 100% 0%;
|
|
67
105
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
.r-otter-indicator__z {
|
|
71
|
-
opacity: 1;
|
|
106
|
+
58.8496% {
|
|
107
|
+
background-position: 0% 100%;
|
|
72
108
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
from {
|
|
76
|
-
transform: translateY(-0.6px) rotate(-3deg);
|
|
109
|
+
62.8319% {
|
|
110
|
+
background-position: 50% 100%;
|
|
77
111
|
}
|
|
78
|
-
|
|
79
|
-
|
|
112
|
+
69.0265% {
|
|
113
|
+
background-position: 100% 100%;
|
|
80
114
|
}
|
|
81
115
|
}
|
|
82
|
-
@keyframes r-
|
|
83
|
-
|
|
84
|
-
|
|
116
|
+
@keyframes r-ottie-tilt {
|
|
117
|
+
0% {
|
|
118
|
+
background-position: 0% 0%;
|
|
85
119
|
}
|
|
86
|
-
|
|
87
|
-
|
|
120
|
+
8.3871% {
|
|
121
|
+
background-position: 50% 0%;
|
|
88
122
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
to {
|
|
92
|
-
transform: translateX(-12px);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
@keyframes r-otter-indicator-ripple {
|
|
96
|
-
from {
|
|
97
|
-
opacity: 0.8;
|
|
98
|
-
transform: scale(0.4);
|
|
123
|
+
16.7742% {
|
|
124
|
+
background-position: 100% 0%;
|
|
99
125
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
transform: scale(1.6);
|
|
126
|
+
25.1613% {
|
|
127
|
+
background-position: 0% 100%;
|
|
103
128
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
from {
|
|
107
|
-
transform: scale(1, 1);
|
|
129
|
+
33.5484% {
|
|
130
|
+
background-position: 50% 100%;
|
|
108
131
|
}
|
|
109
|
-
|
|
110
|
-
|
|
132
|
+
41.9355%,
|
|
133
|
+
100% {
|
|
134
|
+
background-position: 100% 100%;
|
|
111
135
|
}
|
|
112
136
|
}
|
|
113
|
-
@keyframes r-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
transform: translate(0, 0);
|
|
137
|
+
@keyframes r-ottie-finish {
|
|
138
|
+
0% {
|
|
139
|
+
background-position: 0% 0%;
|
|
117
140
|
}
|
|
118
|
-
|
|
119
|
-
|
|
141
|
+
13.7097% {
|
|
142
|
+
background-position: 50% 0%;
|
|
120
143
|
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
transform: translate(4px, -7px);
|
|
144
|
+
26.6129% {
|
|
145
|
+
background-position: 100% 0%;
|
|
124
146
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
from {
|
|
128
|
-
transform: translateX(-0.4px);
|
|
147
|
+
38.7097% {
|
|
148
|
+
background-position: 0% 100%;
|
|
129
149
|
}
|
|
130
|
-
|
|
131
|
-
|
|
150
|
+
51.6129% {
|
|
151
|
+
background-position: 50% 100%;
|
|
152
|
+
}
|
|
153
|
+
63.7097%,
|
|
154
|
+
100% {
|
|
155
|
+
background-position: 100% 100%;
|
|
132
156
|
}
|
|
133
157
|
}
|
|
134
|
-
@keyframes r-
|
|
135
|
-
|
|
136
|
-
|
|
158
|
+
@keyframes r-ottie-question {
|
|
159
|
+
0%,
|
|
160
|
+
72% {
|
|
161
|
+
opacity: 0;
|
|
137
162
|
}
|
|
138
|
-
|
|
139
|
-
|
|
163
|
+
100% {
|
|
164
|
+
opacity: 1;
|
|
140
165
|
}
|
|
141
166
|
}
|
|
@@ -5,13 +5,10 @@ export interface OtterIndicatorProps extends Omit<ComponentPropsWithRef<"svg">,
|
|
|
5
5
|
status: StepStatus;
|
|
6
6
|
}
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* - `error`: shivering in the water; the parcel has slipped away.
|
|
13
|
-
*
|
|
14
|
-
* It fills its box; give it 48px or more so the poses read.
|
|
8
|
+
* Ottie resting, quietly typing, tilting its head or celebrating.
|
|
9
|
+
* Wait/process loop only while visible. Error/finish play once on entry.
|
|
10
|
+
* Reduced motion shows a still pose. Give it an explicit square box, 40px or larger.
|
|
11
|
+
* Decorative by default; the consumer supplies the nearby status text.
|
|
15
12
|
*/
|
|
16
|
-
export declare function OtterIndicator({ status, className, ...props }: OtterIndicatorProps): import("react").JSX.Element;
|
|
13
|
+
export declare function OtterIndicator({ status, className, ref, ...props }: OtterIndicatorProps): import("react").JSX.Element;
|
|
17
14
|
//#endregion
|