@rtzll/reflow 0.3.0 → 0.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.
@@ -0,0 +1,57 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "remix/component/jsx-runtime";
2
+ import { on } from 'remix/component';
3
+ import * as popover from '@remix-run/ui/popover';
4
+ import { Button } from './basic.js';
5
+ // A field to type into, or an element the content marks to take focus. Buttons
6
+ // and links are left to Tab, so opening never fires a control's own tooltip.
7
+ const initialFocus = '[autofocus], input:not(:disabled, [readonly], [type="hidden"]), select:not(:disabled), textarea:not(:disabled, [readonly])';
8
+ /**
9
+ * A panel anchored to a trigger, for content that needs more room than a
10
+ * tooltip: a filter, inline settings, a share sheet. A click opens it; Escape
11
+ * and a click outside close it. It is a non-modal dialog: focus moves to its
12
+ * first field, or to the panel, on open and returns to the trigger on close.
13
+ */
14
+ export function Popover(handle) {
15
+ let localOpen = handle.props.defaultOpen ?? false;
16
+ const panelId = `${handle.id}-panel`;
17
+ const titleId = `${handle.id}-title`;
18
+ const descriptionId = `${handle.id}-description`;
19
+ const isOpen = () => handle.props.open ?? localOpen;
20
+ function setOpen(next) {
21
+ if (isOpen() === next)
22
+ return;
23
+ localOpen = next;
24
+ handle.props.onOpenChange?.(next);
25
+ void handle.update();
26
+ }
27
+ // On open, focus the panel's first field, or the panel itself.
28
+ const focusInside = on('toggle', (event) => {
29
+ if (event.newState !== 'open')
30
+ return;
31
+ const panel = event.currentTarget;
32
+ const target = panel.querySelector(initialFocus) ?? panel;
33
+ target.focus({ preventScroll: true });
34
+ });
35
+ return () => {
36
+ const { label, title, description, children, trigger, triggerVariant = 'secondary', triggerSize, renderTrigger, placement = 'bottom', offset = 8, size, } = handle.props;
37
+ const open = isOpen();
38
+ const triggerProps = {
39
+ 'aria-haspopup': 'dialog',
40
+ 'aria-expanded': open,
41
+ 'aria-controls': panelId,
42
+ mix: [
43
+ popover.anchor({ placement, offset }),
44
+ popover.focusOnHide(),
45
+ on('click', () => setOpen(!isOpen())),
46
+ ],
47
+ };
48
+ return (_jsx("span", { class: "rf-popover", "data-density": size, children: _jsxs(popover.Context, { children: [renderTrigger ? (renderTrigger(triggerProps)) : (_jsx(Button, { variant: triggerVariant, size: triggerSize ?? size, ...triggerProps, children: trigger })), _jsxs("div", { class: "rf-menu-surface rf-popover-surface", id: panelId, role: "dialog", tabIndex: -1, "aria-label": title == null ? label : undefined, "aria-labelledby": title == null ? undefined : titleId, "aria-describedby": description == null ? undefined : descriptionId, "data-density": size, mix: [
49
+ popover.surface({
50
+ open,
51
+ closeOnAnchorClick: false,
52
+ onHide: () => setOpen(false),
53
+ }),
54
+ focusInside,
55
+ ], children: [(title != null || description != null) && (_jsxs("header", { class: "rf-popover-header", children: [title != null && (_jsx("h2", { class: "rf-popover-title", id: titleId, children: title })), description != null && (_jsx("p", { class: "rf-popover-description", id: descriptionId, children: description }))] })), children != null && _jsx("div", { class: "rf-popover-body", children: children })] })] }) }));
56
+ };
57
+ }
@@ -0,0 +1,172 @@
1
+ /* SegmentedControl: a filled track around a row of segments, with a raised
2
+ thumb that glides to the selected one. The track's padding keeps the whole
3
+ control on the size ladder: 4px around 28px segments makes 36px, 2px around
4
+ 24px makes 28px compact. Corners stay concentric: the track's radius is the
5
+ thumb's plus the padding. */
6
+ .rf-segmented {
7
+ --rf-segment-pad: 4px;
8
+ display: inline-flex;
9
+ min-inline-size: 0;
10
+ max-inline-size: 100%;
11
+ margin: 0;
12
+ padding: 0;
13
+ border: 0;
14
+ color: var(--rf-fg);
15
+ }
16
+ .rf-segmented[data-density='compact'],
17
+ [data-density='compact'] .rf-segmented:not([data-density='default']) {
18
+ --rf-segment-pad: 2px;
19
+ }
20
+ .rf-segmented[data-fill] {
21
+ display: flex;
22
+ inline-size: 100%;
23
+ }
24
+
25
+ .rf-segmented-track {
26
+ display: inline-flex;
27
+ flex: 1;
28
+ align-items: stretch;
29
+ box-sizing: border-box;
30
+ min-inline-size: 0;
31
+ block-size: var(--rf-control-height, 36px);
32
+ padding: var(--rf-segment-pad);
33
+ border-radius: calc(var(--rf-radius, 8px) + var(--rf-segment-pad));
34
+ background: var(--rf-hover);
35
+ }
36
+ .rf-segmented[data-fill] .rf-segmented-track {
37
+ display: grid;
38
+ grid-auto-columns: minmax(0, 1fr);
39
+ grid-auto-flow: column;
40
+ }
41
+
42
+ .rf-segment {
43
+ position: relative;
44
+ display: inline-flex;
45
+ align-items: center;
46
+ justify-content: center;
47
+ gap: 6px;
48
+ box-sizing: border-box;
49
+ min-inline-size: 0;
50
+ padding: 0 var(--rf-control-padding, 12px);
51
+ border-radius: var(--rf-radius, 8px);
52
+ color: var(--rf-muted);
53
+ font-size: var(--rf-text-body, 13px);
54
+ line-height: var(--rf-leading-body, 20px);
55
+ white-space: nowrap;
56
+ cursor: pointer;
57
+ user-select: none;
58
+ -webkit-tap-highlight-color: transparent;
59
+ transition: color 80ms ease;
60
+ }
61
+ .rf-segment[data-icon-only] {
62
+ inline-size: calc(var(--rf-control-height, 36px) - var(--rf-segment-pad) * 2);
63
+ padding: 0;
64
+ }
65
+ .rf-segmented[data-fill] .rf-segment[data-icon-only] {
66
+ inline-size: auto;
67
+ }
68
+ .rf-segment:is([data-rf-hovered], :has(:checked), :has(:focus-visible)) {
69
+ color: var(--rf-fg);
70
+ }
71
+ .rf-segment[aria-disabled='true'] {
72
+ opacity: 0.4;
73
+ cursor: default;
74
+ }
75
+
76
+ /* The radio stays focusable and in the form, out of sight. */
77
+ .rf-segment-input {
78
+ position: absolute;
79
+ overflow: hidden;
80
+ inline-size: 1px;
81
+ block-size: 1px;
82
+ margin: 0;
83
+ padding: 0;
84
+ border: 0;
85
+ clip-path: inset(50%);
86
+ white-space: nowrap;
87
+ appearance: none;
88
+ }
89
+
90
+ .rf-segment-icon {
91
+ display: inline-flex;
92
+ flex: none;
93
+ }
94
+ .rf-segment-icon > svg {
95
+ inline-size: var(--rf-icon-size, 16px);
96
+ block-size: var(--rf-icon-size, 16px);
97
+ }
98
+
99
+ /* An invisible copy at the strong weight reserves the width, so the selected
100
+ label's weight shift never moves its neighbours. */
101
+ .rf-segment-text {
102
+ display: inline-grid;
103
+ min-inline-size: 0;
104
+ }
105
+ .rf-segment-sizer,
106
+ .rf-segment-label {
107
+ grid-area: 1 / 1;
108
+ overflow: hidden;
109
+ text-overflow: ellipsis;
110
+ }
111
+ .rf-segment-sizer {
112
+ visibility: hidden;
113
+ font-variation-settings: var(--rf-weight-strong);
114
+ }
115
+ .rf-segment-label {
116
+ font-variation-settings: var(--rf-weight-normal);
117
+ transition: font-variation-settings 80ms ease;
118
+ }
119
+ .rf-segment:has(:checked) .rf-segment-label {
120
+ font-variation-settings: var(--rf-weight-strong);
121
+ }
122
+
123
+ /* The thumb rides the shared selection layer; pressing any segment squeezes it. */
124
+ .rf-segmented-thumb {
125
+ background: var(--rf-surface-5);
126
+ box-shadow:
127
+ 0 1px 2px rgb(0 0 0 / 12%),
128
+ 0 0 0 1px light-dark(rgb(0 0 0 / 4%), rgb(255 255 255 / 4%));
129
+ transition:
130
+ opacity 80ms ease,
131
+ scale var(--rf-spring-fast);
132
+ }
133
+ .rf-segmented-thumb[data-moving] {
134
+ transition:
135
+ var(--rf-highlight-travel),
136
+ opacity 80ms ease,
137
+ scale var(--rf-spring-fast);
138
+ }
139
+ .rf-segmented-track:has(.rf-segment:not([aria-disabled='true']):active) .rf-segmented-thumb {
140
+ scale: 0.97;
141
+ }
142
+ .rf-segmented-hover {
143
+ background: var(--rf-hover);
144
+ }
145
+
146
+ /* Before the layers mount, the checked segment carries the thumb itself. */
147
+ .rf-segmented-track:not([data-rf-has-selection]) .rf-segment:has(:checked) {
148
+ background: var(--rf-surface-5);
149
+ box-shadow: 0 1px 2px rgb(0 0 0 / 12%);
150
+ }
151
+
152
+ .rf-segment:has(:focus-visible) {
153
+ outline: 1px solid var(--rf-accent, #6b97ff);
154
+ outline-offset: 1px;
155
+ }
156
+ @media (forced-colors: none) {
157
+ .rf-segmented-track:has(> .rf-focus-ring[data-visible]) .rf-segment:has(:focus-visible) {
158
+ outline-color: transparent;
159
+ }
160
+ }
161
+ @media (forced-colors: active) {
162
+ .rf-segment:has(:checked) {
163
+ outline: 1px solid Highlight;
164
+ outline-offset: -1px;
165
+ }
166
+ }
167
+ @media (prefers-reduced-motion: reduce) {
168
+ .rf-segmented-thumb,
169
+ .rf-segmented-thumb[data-moving] {
170
+ transition: opacity 80ms ease;
171
+ }
172
+ }
@@ -0,0 +1,34 @@
1
+ import { type Handle, type RemixNode } from 'remix/component';
2
+ export interface SegmentedControlOption {
3
+ value: string;
4
+ /** Names the segment; for an icon-only segment it is read out and shown in a Tooltip. */
5
+ label: string;
6
+ icon?: RemixNode;
7
+ /** Shows only the icon. The label stays for screen readers and appears in a Tooltip on hover. */
8
+ iconOnly?: boolean;
9
+ disabled?: boolean;
10
+ }
11
+ export interface SegmentedControlProps {
12
+ /** Names the group for assistive technology. */
13
+ label: string;
14
+ options: readonly SegmentedControlOption[];
15
+ /** The selected option in controlled mode. */
16
+ value?: string;
17
+ /** The option selected first in uncontrolled mode. Defaults to the first enabled option. */
18
+ defaultValue?: string;
19
+ onValueChange?: (value: string) => void;
20
+ /** Submits the selected value with a surrounding form. */
21
+ name?: string;
22
+ disabled?: boolean;
23
+ size?: 'default' | 'compact';
24
+ /** Stretches the control to its container, with equal-width segments. */
25
+ fill?: boolean;
26
+ }
27
+ /**
28
+ * One choice from a short row of options, such as a view or a time range. A
29
+ * segmented control is never empty: without a value, the first enabled option
30
+ * is selected. Each segment is a native radio, so arrow keys move the
31
+ * selection and the value takes part in forms. A raised thumb glides to the
32
+ * selected segment and squeezes while a segment is pressed.
33
+ */
34
+ export declare function SegmentedControl(handle: Handle<SegmentedControlProps>): () => import("remix/component").RemixElement;
@@ -0,0 +1,38 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "remix/component/jsx-runtime";
2
+ import { on } from 'remix/component';
3
+ import { fluidHover } from './interactive.js';
4
+ import { Tooltip } from './overlays.js';
5
+ /**
6
+ * One choice from a short row of options, such as a view or a time range. A
7
+ * segmented control is never empty: without a value, the first enabled option
8
+ * is selected. Each segment is a native radio, so arrow keys move the
9
+ * selection and the value takes part in forms. A raised thumb glides to the
10
+ * selected segment and squeezes while a segment is pressed.
11
+ */
12
+ export function SegmentedControl(handle) {
13
+ const indicators = fluidHover({
14
+ axis: 'x',
15
+ selectedSelector: ':has(input:checked)',
16
+ focusSelector: ':has(input:focus-visible)',
17
+ });
18
+ return () => {
19
+ const { label, options, value, defaultValue, onValueChange, name = handle.id, disabled, size, fill, } = handle.props;
20
+ const selectable = (candidate) => options.some((option) => option.value === candidate && !option.disabled);
21
+ const fallback = options.find((option) => !option.disabled)?.value;
22
+ const selected = value !== undefined
23
+ ? selectable(value)
24
+ ? value
25
+ : fallback
26
+ : selectable(defaultValue)
27
+ ? defaultValue
28
+ : fallback;
29
+ const segment = (option, mix) => (_jsxs("label", { class: "rf-segment", "data-rf-hover-item": true, "data-icon-only": option.iconOnly && option.icon != null ? '' : undefined, "aria-disabled": disabled || option.disabled || undefined, mix: mix, children: [_jsx("input", { type: "radio", class: "rf-segment-input", name: name, value: option.value, disabled: option.disabled, ...(value === undefined
30
+ ? { defaultChecked: selected === option.value }
31
+ : { checked: selected === option.value }), mix: onValueChange &&
32
+ on('change', (event) => {
33
+ if (event.currentTarget.checked)
34
+ onValueChange(option.value);
35
+ }) }), option.icon != null && (_jsx("span", { class: "rf-segment-icon", "aria-hidden": "true", children: option.icon })), option.iconOnly && option.icon != null ? (_jsx("span", { class: "rf-basic-sr-only", children: option.label })) : (_jsxs("span", { class: "rf-segment-text", children: [_jsx("span", { class: "rf-segment-sizer", "aria-hidden": "true", children: option.label }), _jsx("span", { class: "rf-segment-label", children: option.label })] }))] }, option.value));
36
+ return (_jsxs("fieldset", { class: "rf-segmented", "data-density": size, "data-fill": fill ? '' : undefined, disabled: disabled, children: [_jsx("legend", { class: "rf-basic-sr-only", children: label }), _jsxs("div", { class: "rf-segmented-track", mix: indicators, children: [_jsx("span", { class: "rf-highlight rf-segmented-thumb", "aria-hidden": "true", "data-rf-selection-layer": true }), _jsx("span", { class: "rf-highlight rf-segmented-hover", "aria-hidden": "true", "data-rf-hover-layer": true }), _jsx("span", { class: "rf-highlight rf-focus-ring", "aria-hidden": "true", "data-rf-focus-layer": true }), options.map((option) => option.iconOnly && option.icon != null ? (_jsx(Tooltip, { content: option.label, renderTrigger: (trigger) => segment(option, trigger.mix) }, option.value)) : (segment(option)))] })] }));
37
+ };
38
+ }
package/dist/sidebar.css CHANGED
@@ -926,68 +926,27 @@
926
926
  margin-inline-start: 8px;
927
927
  }
928
928
 
929
- /* Mobile drawer: below 768px the rail becomes a sheet that slides in from the left. */
929
+ /* Mobile drawer: below 768px the rail moves into a left Drawer, which brings
930
+ the slide, the drag to close and the backdrop; it keeps the rail's width,
931
+ surface and shadow. */
930
932
  .rf-sidebar-drawer-trigger {
931
933
  display: none;
932
934
  }
933
- .rf-sidebar-drawer {
934
- position: fixed;
935
- inset-block: 0;
936
- inset-inline-start: 0;
937
- box-sizing: border-box;
938
- width: min(288px, 100vw);
939
- max-width: none;
940
- height: 100dvh;
941
- max-height: none;
942
- margin: 0;
943
- padding: 0;
935
+ .rf-drawer.rf-sidebar-drawer[data-side] {
936
+ --rf-drawer-bg: var(--rf-surface-2);
937
+ inline-size: min(288px, 100vw);
944
938
  overflow: hidden;
945
- border: 0;
946
- background: var(--rf-surface-2);
947
- box-shadow: var(--rf-sb-surface-shadow);
948
- color: var(--rf-fg);
949
- translate: -100% 0;
950
- transition:
951
- translate var(--rf-exit-moderate),
952
- display var(--rf-exit-moderate) allow-discrete,
953
- overlay var(--rf-exit-moderate) allow-discrete;
954
- }
955
- .rf-sidebar-drawer[open] {
956
- translate: 0 0;
957
- transition:
958
- translate var(--rf-spring-moderate),
959
- display var(--rf-spring-moderate) allow-discrete,
960
- overlay var(--rf-spring-moderate) allow-discrete;
961
- }
962
- @starting-style {
963
- .rf-sidebar-drawer[open] {
964
- translate: -100% 0;
965
- }
966
- }
967
- .rf-sidebar-drawer::backdrop {
968
- background: light-dark(rgb(0 0 0 / 40%), rgb(0 0 0 / 80%));
969
- opacity: 0;
970
- transition:
971
- opacity var(--rf-exit-moderate),
972
- display var(--rf-exit-moderate) allow-discrete,
973
- overlay var(--rf-exit-moderate) allow-discrete;
974
- }
975
- .rf-sidebar-drawer[open]::backdrop {
976
- opacity: 1;
977
- transition:
978
- opacity 160ms ease,
979
- display 160ms allow-discrete,
980
- overlay 160ms allow-discrete;
939
+ box-shadow: var(--rf-drawer-tail), var(--rf-sb-surface-shadow);
981
940
  }
982
- @starting-style {
983
- .rf-sidebar-drawer[open]::backdrop {
984
- opacity: 0;
985
- }
941
+ .rf-sidebar-drawer .rf-drawer-body {
942
+ display: flex;
943
+ flex-direction: column;
986
944
  }
987
- .rf-sidebar-drawer-content {
988
- height: 100%;
945
+ .rf-sidebar-drawer .rf-sidebar-inner {
946
+ flex: 1;
947
+ min-block-size: 0;
989
948
  }
990
- .rf-sidebar-drawer-content > .rf-basic-sr-only:focus-visible {
949
+ .rf-sidebar-drawer .rf-drawer-body > .rf-basic-sr-only:focus-visible {
991
950
  clip-path: none;
992
951
  width: auto;
993
952
  height: auto;
@@ -996,8 +955,6 @@
996
955
  position: static;
997
956
  }
998
957
  @media (prefers-reduced-motion: reduce) {
999
- .rf-sidebar-drawer,
1000
- .rf-sidebar-drawer::backdrop,
1001
958
  .rf-sidebar-layout,
1002
959
  .rf-sidebar-card {
1003
960
  transition-duration: 0s !important;
package/dist/sidebar.js CHANGED
@@ -4,6 +4,7 @@ import { on, ref } from 'remix/component';
4
4
  import { Button, Card, CardGroup, CardHeader, CardTitle, CardDescription, CardMedia, CardImage, } from './basic.js';
5
5
  import { fluidHover } from './interactive.js';
6
6
  import { ScrollArea } from './scroll-area.js';
7
+ import { Drawer } from './drawer.js';
7
8
  /** How far past the minimum width a drag has to travel before it previews the collapse. */
8
9
  const collapseSlop = 56;
9
10
  function SidebarNavigation(handle) {
@@ -113,13 +114,14 @@ export function Sidebar(handle) {
113
114
  let exitingPeek;
114
115
  let peekTimer;
115
116
  let drag;
116
- let drawer;
117
+ let drawerOpen = false;
117
118
  const panelId = `${handle.id}-sidebar`;
118
- const drawerId = `${panelId}-drawer`;
119
- const captureDrawer = ref((node) => {
120
- if (node instanceof HTMLDialogElement)
121
- drawer = node;
122
- });
119
+ const setDrawer = (open) => {
120
+ if (drawerOpen === open)
121
+ return;
122
+ drawerOpen = open;
123
+ void handle.update();
124
+ };
123
125
  function limits() {
124
126
  const minimum = handle.props.minWidth ?? 160;
125
127
  return { minimum, maximum: Math.max(minimum, handle.props.maxWidth ?? 360) };
@@ -375,13 +377,7 @@ export function Sidebar(handle) {
375
377
  if (!peeking)
376
378
  cancelPeekTimer();
377
379
  }),
378
- ], children: _jsx(Icon, { name: "panel-left" }) }), _jsx(Button, { class: "rf-sidebar-drawer-trigger", variant: "ghost", size: "compact", "aria-controls": drawerId, "aria-haspopup": "dialog", mix: on('click', () => drawer?.showModal()), children: "Open navigation" })] })), toolbar != null && _jsx("div", { class: "rf-sidebar-toolbar-content", children: toolbar })] })), children] }), _jsx("dialog", { id: drawerId, class: "rf-sidebar-drawer", "aria-label": label, mix: [
379
- captureDrawer,
380
- on('click', (event) => {
381
- if (event.target === drawer)
382
- drawer?.close();
383
- }),
384
- ], children: _jsxs("div", { class: "rf-sidebar-drawer-content", children: [_jsx("button", { type: "button", class: "rf-basic-sr-only", mix: on('click', () => drawer?.close()), children: "Close navigation" }), _jsx(SidebarNavigation, { ...navigation, variant: "default", onNavigate: () => drawer?.close() })] }) })] }));
380
+ ], children: _jsx(Icon, { name: "panel-left" }) }), _jsxs(Drawer, { side: "left", title: label, flush: true, class: "rf-sidebar-drawer", open: drawerOpen, onOpenChange: setDrawer, renderTrigger: (trigger) => (_jsx(Button, { class: "rf-sidebar-drawer-trigger", variant: "ghost", size: "compact", ...trigger, children: "Open navigation" })), children: [_jsx("button", { type: "button", class: "rf-basic-sr-only", mix: on('click', () => setDrawer(false)), children: "Close navigation" }), _jsx(SidebarNavigation, { ...navigation, variant: "default", onNavigate: () => setDrawer(false) })] })] })), toolbar != null && _jsx("div", { class: "rf-sidebar-toolbar-content", children: toolbar })] })), children] })] }));
385
381
  };
386
382
  }
387
383
  /** A measured, dismissible pile of real Cards. Keyboard focus opens the inline pile too. */
@@ -0,0 +1,3 @@
1
+ /** The status glyphs Banner and toasts share. Internal: not exported from the package. */
2
+ export type StatusIconName = 'default' | 'info' | 'success' | 'warning' | 'error';
3
+ export declare function statusIcon(status: StatusIconName): import("remix/component").RemixElement;
@@ -0,0 +1,7 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "remix/component/jsx-runtime";
2
+ export function statusIcon(status) {
3
+ if (status === 'default') {
4
+ return (_jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "1.5", "stroke-linecap": "round", children: [_jsx("circle", { cx: "12", cy: "12", r: "9" }), _jsx("path", { d: "M12 16v-4m0-4h.01" })] }));
5
+ }
6
+ return (_jsxs("svg", { viewBox: "0 0 24 24", fill: "none", children: [status === 'warning' ? (_jsx("path", { d: "m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z", fill: "currentColor", stroke: "currentColor", "stroke-width": "1.5", "stroke-linejoin": "round" })) : (_jsx("circle", { cx: "12", cy: "12", r: "10.5", fill: "currentColor" })), _jsx("g", { stroke: "var(--rf-bg)", "stroke-width": "2.25", "stroke-linecap": "round", "stroke-linejoin": "round", children: status === 'success' ? (_jsx("path", { d: "m8.5 12.25 2.5 2.5 4.5-5" })) : status === 'warning' ? (_jsx("path", { d: "M12 9.5v3.5M12 17h.01" })) : status === 'error' ? (_jsx("path", { d: "M12 7.5v5M12 16.5h.01" })) : (_jsx("path", { d: "M12 16.5v-5M12 7.5h.01" })) })] }));
7
+ }
package/dist/styles.css CHANGED
@@ -10,4 +10,8 @@
10
10
  @import './range-color.css';
11
11
  @import './color-picker.css';
12
12
  @import './breadcrumb.css';
13
+ @import './segmented-control.css';
14
+ @import './popover.css';
15
+ @import './toaster.css';
16
+ @import './drawer.css';
13
17
  @import './systems.css';
package/dist/systems.js CHANGED
@@ -55,7 +55,11 @@ export function reveal(options = {}) {
55
55
  return;
56
56
  observer.disconnect();
57
57
  animation = element.animate([
58
- { opacity: 0.3, translate: `0 ${preset?.distance ?? 6}px`, scale: preset?.scale ?? 1 },
58
+ {
59
+ opacity: preset ? 0 : 0.3,
60
+ translate: `0 ${preset?.distance ?? 6}px`,
61
+ scale: preset?.scale ?? 1,
62
+ },
59
63
  { opacity: 1, translate: '0 0', scale: 1 },
60
64
  ], {
61
65
  ...(preset