@domicile-desktop/manganese-notifications 1.0.0 → 2.0.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/package.json CHANGED
@@ -1,20 +1,21 @@
1
1
  {
2
2
  "dependencies": {
3
- "@domicile-desktop/component-library": "1.0.0",
4
- "@domicile-desktop/manganese-bar": "1.0.0",
5
- "@domicile-desktop/manganese-host": "1.0.0",
6
- "@domicile-desktop/react": "0.0.0",
7
- "@domicile-desktop/sdk": "1.0.0",
3
+ "@domicile-desktop/component-library": "2.1.0",
4
+ "@domicile-desktop/manganese-bar": "1.1.2",
5
+ "@domicile-desktop/manganese-host": "2.0.2",
6
+ "@domicile-desktop/react": "0.1.2",
7
+ "@domicile-desktop/sdk": "2.2.0",
8
8
  "@pandacss/dev": "^1.12.1",
9
9
  "@phosphor-icons/react": "^2.1.10",
10
10
  "react": "^19.2.8",
11
- "react-dom": "^19.2.8"
11
+ "react-dom": "^19.2.8",
12
+ "zod": "^4.5.4"
12
13
  },
13
14
  "description": "Manganese's notification toasts and drawer.",
14
15
  "devDependencies": {
15
16
  "@cprussin/tsconfig": "^5.0.0",
16
- "@domicile-desktop/manganese-window-management": "1.0.0",
17
- "@domicile-desktop/test-support": "0.0.1",
17
+ "@domicile-desktop/manganese-window-management": "1.3.0",
18
+ "@domicile-desktop/test-support": "0.0.2",
18
19
  "@pandacss/preset-panda": "^1.12.1",
19
20
  "@testing-library/react": "^16.3.3",
20
21
  "@testing-library/user-event": "^14.6.7",
@@ -25,8 +26,7 @@
25
26
  "typescript": "^7.0.2"
26
27
  },
27
28
  "engines": {
28
- "bun": ">=1.4.2",
29
- "node": ">=24"
29
+ "bun": ">=1.4.2"
30
30
  },
31
31
  "exports": {
32
32
  "./ago": "./src/ago.ts",
@@ -60,5 +60,5 @@
60
60
  "test:unit": "bun test"
61
61
  },
62
62
  "type": "module",
63
- "version": "1.0.0"
63
+ "version": "2.0.0"
64
64
  }
@@ -3,15 +3,17 @@ import { EmptyState } from "@domicile-desktop/component-library/EmptyState";
3
3
  import { LeavingRow } from "@domicile-desktop/component-library/LeavingRow";
4
4
  import { List } from "@domicile-desktop/component-library/ListRow";
5
5
  import type { Row } from "@domicile-desktop/component-library/leaving-rows";
6
- import { PanelFooter } from "@domicile-desktop/component-library/PanelFooter";
6
+ import { Menu } from "@domicile-desktop/component-library/Menu";
7
7
  import { PanelHeader } from "@domicile-desktop/component-library/PanelHeader";
8
8
  import { PanelSection } from "@domicile-desktop/component-library/PanelSection";
9
9
  import { Popover } from "@domicile-desktop/component-library/Popover";
10
- import { Switch } from "@domicile-desktop/component-library/Switch";
11
10
  import { useLeavingRows } from "@domicile-desktop/component-library/useLeavingRows";
12
11
  import type { Notification } from "@domicile-desktop/sdk/notification";
13
12
  import { BellSimpleIcon } from "@phosphor-icons/react/dist/ssr/BellSimple";
14
13
  import { BroomIcon } from "@phosphor-icons/react/dist/ssr/Broom";
14
+ import { CaretDownIcon } from "@phosphor-icons/react/dist/ssr/CaretDown";
15
+ import { PauseIcon } from "@phosphor-icons/react/dist/ssr/Pause";
16
+ import { PlayIcon } from "@phosphor-icons/react/dist/ssr/Play";
15
17
  import { memo } from "react";
16
18
 
17
19
  import { css } from "../styled-system/css";
@@ -21,6 +23,8 @@ import type { Inbox, InboxReading } from "./inbox";
21
23
  import { NotificationCard } from "./NotificationCard";
22
24
  import type { NotificationsHandle } from "./notifications-context";
23
25
  import { bellId, screenOfBell } from "./notifications-context";
26
+ import { PauseLength, pausedFor } from "./pause";
27
+ import type { Pause } from "./stored-pause";
24
28
 
25
29
  /** The inboxes listed in the drawer, and what the shell knows of them. */
26
30
  export type DrawerInboxes = {
@@ -32,8 +36,6 @@ export type DrawerInboxes = {
32
36
  };
33
37
 
34
38
  type Props = {
35
- /** Toasts are held back, but for critical ones. */
36
- doNotDisturb: boolean;
37
39
  /** Opens it under the bell pressed. */
38
40
  handle: NotificationsHandle;
39
41
  /** The inboxes, listed above the notifications. */
@@ -43,6 +45,8 @@ type Props = {
43
45
  /** The current time, for the relative timestamps. */
44
46
  now: number;
45
47
  open: boolean;
48
+ /** Toasts are held back, but for critical ones, until it ends. */
49
+ pause: Pause | undefined;
46
50
  /** The monitor whose bell it opens under. */
47
51
  screen: string | undefined;
48
52
  /** The bell on monitor `screen` was pressed. */
@@ -50,8 +54,10 @@ type Props = {
50
54
  onClose: () => void;
51
55
  /** Clear `ids`. */
52
56
  onDismiss: (ids: readonly number[]) => void;
53
- /** Turn do not disturb on or off. */
54
- onDoNotDisturb: (on: boolean) => void;
57
+ /** Pause toasts for `length`. */
58
+ onPause: (length: PauseLength) => void;
59
+ /** End the pause. */
60
+ onResume: () => void;
55
61
  /** Press `key` on `id`. */
56
62
  onAction: (id: number, key: string) => void;
57
63
  /** Send `text` from `id`'s reply field. */
@@ -61,8 +67,8 @@ type Props = {
61
67
  /**
62
68
  * The notification drawer, a panel under the bell. Each notification can be
63
69
  * pressed or cleared, or all cleared at once. A press closes the drawer,
64
- * which would cover the window it raises. Do not disturb is switched in its
65
- * footer.
70
+ * which would cover the window it raises. Its header pauses notifications from
71
+ * a menu of lengths, and says until when while paused.
66
72
  *
67
73
  * A notification stays here after its toast is gone, until it is cleared or its
68
74
  * application withdraws it. See docs/architecture/NOTIFICATIONS.md.
@@ -75,7 +81,6 @@ type Props = {
75
81
  */
76
82
  export const NotificationDrawer = memo(
77
83
  ({
78
- doNotDisturb,
79
84
  handle,
80
85
  inboxes,
81
86
  items,
@@ -83,10 +88,12 @@ export const NotificationDrawer = memo(
83
88
  onAction,
84
89
  onClose,
85
90
  onDismiss,
86
- onDoNotDisturb,
87
91
  onOpen,
92
+ onPause,
88
93
  onReply,
94
+ onResume,
89
95
  open,
96
+ pause,
90
97
  screen,
91
98
  }: Props) => {
92
99
  const { onLeft, rows } = useLeavingRows(items, idOf, open);
@@ -117,7 +124,26 @@ export const NotificationDrawer = memo(
117
124
  triggerId={screen === undefined ? null : bellId(screen)}
118
125
  >
119
126
  <section aria-labelledby={TITLE_ID} className={drawerStyles}>
120
- <PanelHeader title="Notifications" titleId={TITLE_ID} />
127
+ <PanelHeader
128
+ action={
129
+ pause === undefined ? (
130
+ <PauseMenu onPause={onPause} />
131
+ ) : (
132
+ <Button
133
+ beforeIcon={<PlayIcon aria-hidden size={12} weight="fill" />}
134
+ onClick={onResume}
135
+ rounded
136
+ size="xs"
137
+ variant="accent"
138
+ >
139
+ Resume
140
+ </Button>
141
+ )
142
+ }
143
+ status={pause === undefined ? undefined : pausedFor(pause, now)}
144
+ title="Notifications"
145
+ titleId={TITLE_ID}
146
+ />
121
147
  <div className={paneStyles}>
122
148
  {inboxes !== undefined && (
123
149
  <PanelSection label="Inboxes">
@@ -130,41 +156,70 @@ export const NotificationDrawer = memo(
130
156
  </List>
131
157
  </PanelSection>
132
158
  )}
133
- {inboxes === undefined ? (
134
- notifications
135
- ) : (
136
- <PanelSection label="Recent">{notifications}</PanelSection>
137
- )}
159
+ <PanelSection
160
+ action={
161
+ items.length > 0 && (
162
+ <Button
163
+ beforeIcon={<BroomIcon aria-hidden size={14} />}
164
+ onClick={() => {
165
+ onDismiss(items.map(({ id }) => id));
166
+ }}
167
+ rounded
168
+ size="xs"
169
+ variant="ghost"
170
+ >
171
+ Clear all
172
+ </Button>
173
+ )
174
+ }
175
+ label="Recent"
176
+ >
177
+ {notifications}
178
+ </PanelSection>
138
179
  </div>
139
- <PanelFooter
140
- action={
141
- items.length > 0 && (
142
- <Button
143
- beforeIcon={<BroomIcon aria-hidden size={14} />}
144
- onClick={() => {
145
- onDismiss(items.map(({ id }) => id));
146
- }}
147
- rounded
148
- size="xs"
149
- variant="ghost"
150
- >
151
- Clear all
152
- </Button>
153
- )
154
- }
155
- >
156
- <Switch
157
- checked={doNotDisturb}
158
- label="Do not disturb"
159
- onCheckedChange={onDoNotDisturb}
160
- />
161
- </PanelFooter>
162
180
  </section>
163
181
  </Popover>
164
182
  );
165
183
  },
166
184
  );
167
185
 
186
+ type PauseMenuProps = Pick<Props, "onPause">;
187
+
188
+ /** The header's Pause button, and the lengths it offers. */
189
+ const PauseMenu = ({ onPause }: PauseMenuProps) => (
190
+ <Menu
191
+ trigger={
192
+ <Button
193
+ afterIcon={<CaretDownIcon aria-hidden size={10} weight="fill" />}
194
+ beforeIcon={<PauseIcon aria-hidden size={12} weight="fill" />}
195
+ rounded
196
+ size="xs"
197
+ variant="ghost"
198
+ >
199
+ Pause
200
+ </Button>
201
+ }
202
+ >
203
+ {PAUSE_LENGTHS.map(({ label, length }) => (
204
+ <Menu.Item
205
+ key={label}
206
+ onClick={() => {
207
+ onPause(length);
208
+ }}
209
+ >
210
+ {label}
211
+ </Menu.Item>
212
+ ))}
213
+ </Menu>
214
+ );
215
+
216
+ /** The Pause menu's items, shortest first. */
217
+ const PAUSE_LENGTHS = [
218
+ { label: "For 1 hour", length: PauseLength.Hour },
219
+ { label: "Until tomorrow", length: PauseLength.Tomorrow },
220
+ { label: "Until I resume", length: PauseLength.UntilResumed },
221
+ ];
222
+
168
223
  /** Tells one notification from another. */
169
224
  const idOf = ({ id }: Notification): number => id;
170
225
 
package/src/fixture.ts CHANGED
@@ -5,6 +5,7 @@ export const notification = (fields: Partial<Notification>): Notification => ({
5
5
  actions: [],
6
6
  appName: "Firefox",
7
7
  body: "",
8
+ browserWindow: undefined,
8
9
  clickable: false,
9
10
  icon: undefined,
10
11
  id: 1,
package/src/inbox.ts CHANGED
@@ -107,9 +107,10 @@ export const Inbox = {
107
107
  open: (domicile: Domicile, reading: InboxReading | undefined) => void;
108
108
  /**
109
109
  * The app window it opens in, if any, which the launcher lists: app
110
- * `appId`'s, at `url`.
110
+ * `appId`'s, at `url`. With `pageNotifies`, its page notifies what the
111
+ * inbox notifies already, so the page's are cleared as they arrive.
111
112
  */
112
- app?: { appId: string; url: string };
113
+ app?: { appId: string; url: string; pageNotifies?: boolean };
113
114
  },
114
115
  ) => ({ ...inbox, kind: InboxKind.Watched as const }),
115
116
  /**
@@ -7,10 +7,10 @@ export type NotificationsHandle = ReturnType<typeof createHandle>;
7
7
 
8
8
  /** The notification drawer, for the bell that opens it. */
9
9
  export type NotificationsDrawer = {
10
- /** Toasts are held back, but for critical ones. */
11
- doNotDisturb: boolean;
12
10
  /** Opens the drawer under the bell pressed. */
13
11
  handle: NotificationsHandle;
12
+ /** Toasts are held back, but for critical ones. */
13
+ paused: boolean;
14
14
  /** How many notifications arrived since the drawer was last opened. */
15
15
  unread: number;
16
16
  };
package/src/pause.ts ADDED
@@ -0,0 +1,54 @@
1
+ import type { Pause } from "./stored-pause";
2
+
3
+ /** How long a pause lasts, as picked from the drawer's Pause menu. */
4
+ export enum PauseLength {
5
+ Hour,
6
+ Tomorrow,
7
+ UntilResumed,
8
+ }
9
+
10
+ const HOUR_MS = 3_600_000;
11
+
12
+ /** The hour of the morning a pause until tomorrow ends. */
13
+ const MORNING_HOUR = 8;
14
+
15
+ /**
16
+ * When a pause of `length` started at `now` ends, in ms since the epoch, or
17
+ * `undefined` for one that lasts until resumed.
18
+ */
19
+ export const pauseEnd = (
20
+ length: PauseLength,
21
+ now: number,
22
+ ): number | undefined => {
23
+ switch (length) {
24
+ case PauseLength.Hour: {
25
+ return now + HOUR_MS;
26
+ }
27
+ case PauseLength.Tomorrow: {
28
+ return nextMorning(new Date(now)).getTime();
29
+ }
30
+ case PauseLength.UntilResumed: {
31
+ return undefined;
32
+ }
33
+ }
34
+ };
35
+
36
+ /** What the drawer says under its title while `pause` holds at `now`. */
37
+ export const pausedFor = ({ until }: Pause, now: number): string =>
38
+ until === undefined ? "Paused" : `Paused until ${endsAt(until, now)}`;
39
+
40
+ /** 8 AM the day after `now`'s date, local time. */
41
+ const nextMorning = (now: Date): Date =>
42
+ new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1, MORNING_HOUR);
43
+
44
+ /** `until`'s time, after "tomorrow" when that is not `now`'s date. */
45
+ const endsAt = (until: number, now: number): string => {
46
+ const end = new Date(until);
47
+ const time = end.toLocaleTimeString(undefined, {
48
+ hour: "numeric",
49
+ minute: "2-digit",
50
+ });
51
+ return end.toDateString() === new Date(now).toDateString()
52
+ ? time
53
+ : `tomorrow, ${time}`;
54
+ };
@@ -0,0 +1,57 @@
1
+ import { z } from "zod";
2
+
3
+ // The notifications pause, stored locally so a reload keeps it. Every page of
4
+ // the desktop shares one origin, so they read the same pause.
5
+
6
+ /** The storage key, versioned per the persisted-state rule. */
7
+ const KEY = "notifications-paused:v1";
8
+
9
+ /** The key do not disturb was stored under; on means paused until resumed. */
10
+ const DO_NOT_DISTURB_KEY = "do-not-disturb:v1";
11
+
12
+ const pauseSchema = z.object({ until: z.number().optional() });
13
+
14
+ /** Notifications paused until `until` (ms since the epoch) or until resumed. */
15
+ export type Pause = z.infer<typeof pauseSchema>;
16
+
17
+ /**
18
+ * The stored pause, or none. Do not disturb left on reads as a pause until
19
+ * resumed.
20
+ *
21
+ * Parsed rather than cast: an unreadable value counts as none, and
22
+ * notifications toast.
23
+ */
24
+ export const storedPause = (): Pause | undefined => {
25
+ const stored = globalThis.localStorage.getItem(KEY);
26
+ if (stored === null) {
27
+ return globalThis.localStorage.getItem(DO_NOT_DISTURB_KEY) === "on"
28
+ ? {}
29
+ : undefined;
30
+ } else {
31
+ return storedPauseSchema.safeParse(stored).data;
32
+ }
33
+ };
34
+
35
+ /** Store `pause` for other pages and future loads, or clear it. */
36
+ export const storePause = (pause: Pause | undefined): void => {
37
+ globalThis.localStorage.removeItem(DO_NOT_DISTURB_KEY);
38
+ if (pause === undefined) {
39
+ globalThis.localStorage.removeItem(KEY);
40
+ } else {
41
+ globalThis.localStorage.setItem(KEY, JSON.stringify(pause));
42
+ }
43
+ };
44
+
45
+ // Invalid JSON is a parse issue, not a throw, so it is handled like any other
46
+ // invalid value.
47
+ const storedPauseSchema = z
48
+ .string()
49
+ .transform((text, context): unknown => {
50
+ try {
51
+ return JSON.parse(text);
52
+ } catch {
53
+ context.addIssue({ code: "custom", message: "not JSON" });
54
+ return z.NEVER;
55
+ }
56
+ })
57
+ .pipe(pauseSchema);
@@ -4,7 +4,10 @@ import type { Domicile } from "@domicile-desktop/sdk/desktop/desktop";
4
4
  import type { Notification } from "@domicile-desktop/sdk/notification";
5
5
  import { useCallback, useEffect, useRef, useState } from "react";
6
6
  import { arrivals } from "./arrivals";
7
- import { storeDoNotDisturb, storedDoNotDisturb } from "./do-not-disturb";
7
+ import type { PauseLength } from "./pause";
8
+ import { pauseEnd } from "./pause";
9
+ import type { Pause } from "./stored-pause";
10
+ import { storedPause, storePause } from "./stored-pause";
8
11
  import { toastTimeout } from "./toast-timeout";
9
12
 
10
13
  export type NotificationCenter = {
@@ -23,9 +26,13 @@ export type NotificationCenter = {
23
26
  invoke: (id: number, action: string) => void;
24
27
  /** Send `text` from `id`'s reply field. */
25
28
  reply: (id: number, text: string) => void;
26
- /** Holds back every toast but a critical one. Kept through a reload. */
27
- doNotDisturb: boolean;
28
- setDoNotDisturb: (on: boolean) => void;
29
+ /**
30
+ * Holds back every toast but a critical one, or `undefined` while not
31
+ * paused. Kept through a reload.
32
+ */
33
+ pause: Pause | undefined;
34
+ pauseFor: (length: PauseLength) => void;
35
+ resume: () => void;
29
36
  };
30
37
 
31
38
  /** The notifications and how far the user has read. */
@@ -49,14 +56,22 @@ type Desk = {
49
56
  *
50
57
  * A toast only interrupts. Closing it or timing out leaves the notification in
51
58
  * place. Clearing the notification (on any page) or its application closing it
52
- * removes the toast too. Do not disturb holds back every toast but a critical
53
- * one; the notifications still collect. See docs/architecture/NOTIFICATIONS.md.
59
+ * removes the toast too. A pause holds back every toast but a critical one;
60
+ * the notifications still collect. A timed pause ends by itself. See
61
+ * docs/architecture/NOTIFICATIONS.md.
54
62
  */
55
63
  export const useNotifications = (
56
64
  domicile: Pick<Domicile, "notifications">,
57
65
  toasts: ToastManager,
58
66
  /** The senders `unread` leaves out, by `appName`, such as the inboxes'. */
59
67
  uncounted: readonly string[],
68
+ /**
69
+ * The browser windows whose pages' notifications are cleared as they
70
+ * arrive, unshown, because another sends the same news: an inbox's window,
71
+ * when the inbox notifies its mail itself.
72
+ */
73
+ replaced: readonly string[],
74
+ clock: typeof wallClock = wallClock,
60
75
  ): NotificationCenter => {
61
76
  const [desk, setDesk] = useState<Desk>({
62
77
  items: [],
@@ -65,12 +80,22 @@ export const useNotifications = (
65
80
  // The last list received, for `arrivals`. A ref because it is only used in
66
81
  // the handler, not for rendering.
67
82
  const told = useRef<readonly Notification[] | undefined>(undefined);
68
- const [doNotDisturb, setQuiet] = useState(storedDoNotDisturb);
83
+ const [pause, setPause] = useState(() => unended(storedPause(), clock()));
69
84
  // Read by the handler, which subscribes once.
70
- const quiet = useRef(doNotDisturb);
85
+ const quiet = useRef(pause !== undefined);
71
86
 
72
87
  useEffect(() => {
73
- const heard = (items: readonly Notification[]) => {
88
+ const heard = (sent: readonly Notification[]) => {
89
+ const duplicates = sent
90
+ .filter(
91
+ ({ browserWindow }) =>
92
+ browserWindow !== undefined && replaced.includes(browserWindow),
93
+ )
94
+ .map(({ id }) => id);
95
+ if (duplicates.length > 0) {
96
+ domicile.notifications.dismiss(duplicates);
97
+ }
98
+ const items = sent.filter(({ id }) => !duplicates.includes(id));
74
99
  for (const arrived of arrivals(told.current, items).filter(
75
100
  ({ urgency }) => !quiet.current || urgency === "critical",
76
101
  )) {
@@ -100,18 +125,42 @@ export const useNotifications = (
100
125
  () => notifications.list,
101
126
  heard,
102
127
  );
103
- }, [domicile, toasts]);
128
+ }, [domicile, replaced, toasts]);
104
129
 
105
130
  const read = useCallback(() => {
106
131
  setDesk((previous) => ({ ...previous, readUpTo: latest(previous.items) }));
107
132
  }, []);
108
133
 
109
- const setDoNotDisturb = useCallback((on: boolean) => {
110
- quiet.current = on;
111
- storeDoNotDisturb(on);
112
- setQuiet(on);
134
+ const pauseAs = useCallback((next: Pause | undefined) => {
135
+ quiet.current = next !== undefined;
136
+ storePause(next);
137
+ setPause(next);
113
138
  }, []);
114
139
 
140
+ const pauseFor = useCallback(
141
+ (length: PauseLength) => {
142
+ const until = pauseEnd(length, clock());
143
+ pauseAs(until === undefined ? {} : { until });
144
+ },
145
+ [clock, pauseAs],
146
+ );
147
+
148
+ const resume = useCallback(() => {
149
+ pauseAs(undefined);
150
+ }, [pauseAs]);
151
+
152
+ const until = pause?.until;
153
+ useEffect(() => {
154
+ if (until === undefined) {
155
+ return undefined;
156
+ } else {
157
+ const timer = setTimeout(resume, until - clock());
158
+ return () => {
159
+ clearTimeout(timer);
160
+ };
161
+ }
162
+ }, [clock, resume, until]);
163
+
115
164
  const dismiss = useCallback(
116
165
  (ids: readonly number[]) => {
117
166
  domicile.notifications.dismiss(ids);
@@ -135,12 +184,13 @@ export const useNotifications = (
135
184
 
136
185
  return {
137
186
  dismiss,
138
- doNotDisturb,
139
187
  invoke,
140
188
  items: desk.items.toReversed(),
189
+ pause,
190
+ pauseFor,
141
191
  read,
142
192
  reply,
143
- setDoNotDisturb,
193
+ resume,
144
194
  unread: desk.items.filter(
145
195
  ({ appName, time }) =>
146
196
  time > (desk.readUpTo ?? 0) && !uncounted.includes(appName),
@@ -148,6 +198,13 @@ export const useNotifications = (
148
198
  };
149
199
  };
150
200
 
201
+ /** The wall clock in ms since the epoch; injected so tests can control it. */
202
+ const wallClock = (): number => Date.now();
203
+
204
+ /** `pause`, unless it ended by `now`. */
205
+ const unended = (pause: Pause | undefined, now: number): Pause | undefined =>
206
+ pause?.until !== undefined && pause.until <= now ? undefined : pause;
207
+
151
208
  /** The newest `time` in `items`, or `0` for none. */
152
209
  const latest = (items: readonly Notification[]): number =>
153
210
  Math.max(0, ...items.map(({ time }) => time));
@@ -3,11 +3,11 @@ import { css } from '../css/index.mjs';
3
3
 
4
4
  const aspectRatioConfig = {
5
5
  transform(props, { map }) {
6
- const { ratio = 4 / 3, ...rest } = props;
6
+ const { ratio = 1.3333333333333333, ...rest } = props;
7
7
  return {
8
8
  position: "relative",
9
9
  _before: {
10
- content: `""`,
10
+ content: '""',
11
11
  display: "block",
12
12
  height: "0",
13
13
  paddingBottom: map(ratio, (r) => `${1 / r * 100}%`)
@@ -3,8 +3,7 @@ import { css } from '../css/index.mjs';
3
3
 
4
4
  const bleedConfig = {
5
5
  transform(props, { map, isCssUnit, isCssVar }) {
6
- const { inline, block, ...rest } = props;
7
- const valueFn = (v) => isCssUnit(v) || isCssVar(v) ? v : `token(spacing.${v}, ${v})`;
6
+ const { inline, block, ...rest } = props, valueFn = (v) => isCssUnit(v) || isCssVar(v) ? v : `token(spacing.${v}, ${v})`;
8
7
  return {
9
8
  "--bleed-x": map(inline, valueFn),
10
9
  "--bleed-y": map(block, valueFn),
@@ -11,28 +11,22 @@ transform(props, { map }) {
11
11
  position: "absolute",
12
12
  insetBlockStart: map(placement, (v) => {
13
13
  const [side] = v.split("-");
14
- const map2 = { top: offsetY, middle: "50%", bottom: "auto" };
15
- return map2[side];
14
+ return { top: offsetY, middle: "50%", bottom: "auto" }[side];
16
15
  }),
17
16
  insetBlockEnd: map(placement, (v) => {
18
17
  const [side] = v.split("-");
19
- const map2 = { top: "auto", middle: "50%", bottom: offsetY };
20
- return map2[side];
18
+ return { top: "auto", middle: "50%", bottom: offsetY }[side];
21
19
  }),
22
20
  insetInlineStart: map(placement, (v) => {
23
21
  const [, align] = v.split("-");
24
- const map2 = { start: offsetX, center: "50%", end: "auto" };
25
- return map2[align];
22
+ return { start: offsetX, center: "50%", end: "auto" }[align];
26
23
  }),
27
24
  insetInlineEnd: map(placement, (v) => {
28
25
  const [, align] = v.split("-");
29
- const map2 = { start: "auto", center: "50%", end: offsetX };
30
- return map2[align];
26
+ return { start: "auto", center: "50%", end: offsetX }[align];
31
27
  }),
32
28
  translate: map(placement, (v) => {
33
- const [side, align] = v.split("-");
34
- const mapX = { start: "-50%", center: "-50%", end: "50%" };
35
- const mapY = { top: "-50%", middle: "-50%", bottom: "50%" };
29
+ const [side, align] = v.split("-"), mapX = { start: "-50%", center: "-50%", end: "50%" }, mapY = { top: "-50%", middle: "-50%", bottom: "50%" };
36
30
  return `${mapX[align]} ${mapY[side]}`;
37
31
  }),
38
32
  ...rest
@@ -3,8 +3,7 @@ import { css } from '../css/index.mjs';
3
3
 
4
4
  const gridItemConfig = {
5
5
  transform(props, { map }) {
6
- const { colSpan, rowSpan, colStart, rowStart, colEnd, rowEnd, ...rest } = props;
7
- const spanFn = (v) => v === "auto" ? v : `span ${v}`;
6
+ const { colSpan, rowSpan, colStart, rowStart, colEnd, rowEnd, ...rest } = props, spanFn = (v) => v === "auto" ? v : `span ${v}`;
8
7
  return {
9
8
  gridColumn: colSpan != null ? map(colSpan, spanFn) : void 0,
10
9
  gridRow: rowSpan != null ? map(rowSpan, spanFn) : void 0,
@@ -3,8 +3,7 @@ import { css } from '../css/index.mjs';
3
3
 
4
4
  const gridConfig = {
5
5
  transform(props, { map, isCssUnit }) {
6
- const { columnGap, rowGap, gap, columns, minChildWidth, ...rest } = props;
7
- const getValue = (v) => isCssUnit(v) ? v : `token(sizes.${v}, ${v})`;
6
+ const { columnGap, rowGap, gap, columns, minChildWidth, ...rest } = props, getValue = (v) => isCssUnit(v) ? v : `token(sizes.${v}, ${v})`;
8
7
  return {
9
8
  display: "grid",
10
9
  gridTemplateColumns: columns != null ? map(columns, (v) => `repeat(${v}, minmax(0, 1fr))`) : minChildWidth != null ? map(minChildWidth, (v) => `repeat(auto-fit, minmax(${getValue(v)}, 1fr))`) : void 0,
@@ -9,7 +9,7 @@ transform(props) {
9
9
  position: "absolute",
10
10
  inset: "0",
11
11
  zIndex: "0",
12
- ...props["_before"]
12
+ ...props._before
13
13
  },
14
14
  ...props
15
15
  };
@@ -8,9 +8,9 @@ transform(props, { map, isCssUnit, isCssVar }) {
8
8
  alignSelf: "stretch",
9
9
  justifySelf: "stretch",
10
10
  flex: map(size, (v) => {
11
- if (v == null) return "1";
12
- const val = isCssUnit(v) || isCssVar(v) ? v : `token(spacing.${v}, ${v})`;
13
- return `0 0 ${val}`;
11
+ if (v == null)
12
+ return "1";
13
+ return `0 0 ${isCssUnit(v) || isCssVar(v) ? v : `token(spacing.${v}, ${v})`}`;
14
14
  }),
15
15
  ...rest
16
16
  };