@parallelworks/ui 0.65.2 → 0.66.1

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/README.md CHANGED
@@ -647,8 +647,8 @@ such as `z-index: 100` for a top bar, rather than a z-index chosen to win:
647
647
  | `--layer-overlay` | 1000 | a panel over the page, such as a full-screen log |
648
648
  | `--layer-drawer` | 1100 | drawers |
649
649
  | `--layer-modal` | 1200 | modals |
650
- | `--layer-popover` | 1300 | menus, selects, popovers, hover cards and tooltips, so one opens over the dialog it belongs to |
651
- | `--layer-toast` | 1400 | toasts |
650
+ | `--layer-toast` | 1300 | toasts, so one stays usable while a modal is open |
651
+ | `--layer-popover` | 1400 | menus, selects, popovers, hover cards and tooltips, so one opens over the dialog it belongs to and over a toast |
652
652
 
653
653
  In Tailwind, `z-(--layer-modal)` reads one. Override a value on `:root` to move
654
654
  a layer; the defaults sit in a cascade layer, so the host's own rule wins.
@@ -663,10 +663,11 @@ import { AddIcon, TrashIcon } from '@parallelworks/ui/icons'
663
663
  ```
664
664
 
665
665
  `@parallelworks/ui/icons` is the one import point for interface icons. Each
666
- name is a meaning, such as `RetryIcon` or `WarningIcon`, drawn from
667
- [Lucide](https://lucide.dev), so every icon shares one 24px grid and one 2px
668
- stroke. Don't import `lucide-react` directly: a name here keeps one glyph per
669
- meaning across the apps.
666
+ name is a meaning, such as `RetryIcon` or `WarningIcon`. Every icon is drawn
667
+ for this module by one hand: one path on a 16px grid with a 1.5px stroke, in
668
+ fewer and simpler shapes than a 24px set so it stays crisp at the 16px it's
669
+ mostly shown at. Don't bring in another icon library: a name here keeps one
670
+ glyph per meaning across the apps.
670
671
 
671
672
  - **Size and color:** an icon is `1em` square in `currentColor`, so it follows
672
673
  the text around it. A class such as `size-4` or `h-3 w-3` sets another size,
@@ -690,10 +691,13 @@ meaning across the apps.
690
691
  write-up to read, such as a case study, and `NotebookIcon` one notebook.
691
692
  `PendingIcon` is a requirement not met yet and `NotRunningIcon` a run not
692
693
  started; they share the empty circle, so keep them out of the same list.
693
- - **Adding an icon:** pick the [Lucide glyph](https://lucide.dev/icons) and add
694
- `export const NameIcon = /* @__PURE__ */ icon(Glyph)` to `src/icons.tsx`,
695
- named for what it means rather than what it shows. Reuse an existing name
696
- when the meaning is the same.
694
+ - **Adding an icon:** draw it as one path on the 16px grid and add
695
+ `export const NameIcon = /* @__PURE__ */ drawn('M…')` to `src/icons.tsx`,
696
+ named for what it means rather than what it shows. Reuse the existing parts
697
+ (the status circle, the person, the file and folder outlines, the
698
+ bottom-right corner for a plus, minus, check or X) so families match, and
699
+ check it at 16px beside its neighbours in the `UI/Icons` story. Reuse an
700
+ existing name when the meaning is the same.
697
701
 
698
702
  Logos of other companies' products, such as `GitHubIcon` or `UbuntuIcon`, are
699
703
  in `@parallelworks/ui/brand-icons`. They take the same props, are filled in the
@@ -27,7 +27,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
27
27
  */
28
28
  /** A one-color logo in the text's color, at the text's size. */
29
29
  function brand(viewBox, ...paths) {
30
- function Icon({ title, size = "1em", color = "currentColor", strokeWidth: _strokeWidth, absoluteStrokeWidth: _absoluteStrokeWidth, nonScalingStroke: _nonScalingStroke, children, ...props }) {
30
+ function Icon({ title, size = "1em", color = "currentColor", strokeWidth: _strokeWidth, children, ...props }) {
31
31
  const named = Boolean(title || props["aria-label"] || props["aria-labelledby"]);
32
32
  return /* @__PURE__ */ jsxs("svg", {
33
33
  xmlns: "http://www.w3.org/2000/svg",
@@ -54,7 +54,10 @@ function Avatar$1({ src, name, colorKey, size = "md", status, className }) {
54
54
  }, src), /* @__PURE__ */ jsx(Avatar.Fallback, {
55
55
  "aria-hidden": "true",
56
56
  className: cx("flex size-full items-center justify-center", colorBy ? categoryClasses(colorBy) : "bg-gradient-to-br from-[var(--gradient-start,var(--theme-accent))] to-[var(--gradient-mid,var(--theme-link))] text-(--theme-accent-text)"),
57
- children: size === "xs" ? firstCharacter(avatarInitials(name)) : avatarInitials(name)
57
+ children: /* @__PURE__ */ jsx("span", {
58
+ className: "leading-none [text-box:trim-both_cap_alphabetic]",
59
+ children: size === "xs" ? firstCharacter(avatarInitials(name)) : avatarInitials(name)
60
+ })
58
61
  })]
59
62
  }), status && /* @__PURE__ */ jsx("span", {
60
63
  "data-testid": "online-indicator",
@@ -54,8 +54,8 @@ export interface Toaster {
54
54
  }) => Promise<T>;
55
55
  }
56
56
  /** Shows toasts from anywhere, a component or not. They render in the
57
- * `UIProvider`'s viewport, over dialogs and popups, and stay usable while a
58
- * modal is open. */
57
+ * `UIProvider`'s viewport, over dialogs and under an open popup such as a menu,
58
+ * and stay usable while a modal is open. */
59
59
  export declare const toast: Toaster;
60
60
  /** Whether a dialog's close request came from a press on a toast, which the
61
61
  * dialog ignores: a toast floats over it, so pressing one isn't leaving it. */
@@ -56,8 +56,8 @@ function show(kind) {
56
56
  });
57
57
  }
58
58
  /** Shows toasts from anywhere, a component or not. They render in the
59
- * `UIProvider`'s viewport, over dialogs and popups, and stay usable while a
60
- * modal is open. */
59
+ * `UIProvider`'s viewport, over dialogs and under an open popup such as a menu,
60
+ * and stay usable while a modal is open. */
61
61
  var toast = {
62
62
  info: show("info"),
63
63
  success: show("success"),
@@ -1,4 +1,4 @@
1
- /** Above dialogs and sticky chrome; hidden while its anchor is scrolled out of view. */
1
+ /** Above dialogs, toasts and sticky chrome; hidden while its anchor is scrolled out of view. */
2
2
  export declare const positionerClassName = "z-(--layer-popover) outline-none data-anchor-hidden:invisible";
3
3
  /** A panel of choices: a menu, a select's list, a combobox's list. */
4
4
  export declare const panelClassName = "rounded-lg border border-(--theme-border) bg-(--theme-app-bg) text-sm text-(--theme-app) shadow-(--theme-shadow-lg) outline-none origin-(--transform-origin) transition-[transform,opacity] duration-100 data-starting-style:scale-95 data-starting-style:opacity-0 data-ending-style:scale-95 data-ending-style:opacity-0 motion-reduce:data-starting-style:scale-100 motion-reduce:data-ending-style:scale-100";
@@ -1,5 +1,5 @@
1
1
  //#region src/components/floating.ts
2
- /** Above dialogs and sticky chrome; hidden while its anchor is scrolled out of view. */
2
+ /** Above dialogs, toasts and sticky chrome; hidden while its anchor is scrolled out of view. */
3
3
  var positionerClassName = "z-(--layer-popover) outline-none data-anchor-hidden:invisible";
4
4
  var enterExit = "origin-(--transform-origin) transition-[transform,opacity] duration-100 data-starting-style:scale-95 data-starting-style:opacity-0 data-ending-style:scale-95 data-ending-style:opacity-0 motion-reduce:data-starting-style:scale-100 motion-reduce:data-ending-style:scale-100";
5
5
  /** A panel of choices: a menu, a select's list, a combobox's list. */
@@ -138,6 +138,18 @@ function SummaryBody({ runSlug, path }) {
138
138
  children: data
139
139
  }) });
140
140
  }
141
+ function scroller(el) {
142
+ const { body, documentElement } = el.ownerDocument;
143
+ let position = getComputedStyle(el).position;
144
+ for (let node = el.parentElement; node && node !== body; node = node.parentElement) {
145
+ if (position === "fixed") return null;
146
+ const style = getComputedStyle(node);
147
+ if (position === "absolute" && style.position === "static") continue;
148
+ if (node !== documentElement && /auto|scroll|overlay/.test(style.overflowY) && node.scrollHeight > node.clientHeight) return node;
149
+ position = style.position;
150
+ }
151
+ return null;
152
+ }
141
153
  function useNearViewport(ref) {
142
154
  const [near, setNear] = useState(typeof IntersectionObserver === "undefined");
143
155
  useEffect(() => {
@@ -145,7 +157,10 @@ function useNearViewport(ref) {
145
157
  if (near || !el) return;
146
158
  const observer = new IntersectionObserver((entries) => {
147
159
  if (entries.some((entry) => entry.isIntersecting)) setNear(true);
148
- }, { rootMargin: "200% 0px" });
160
+ }, {
161
+ root: scroller(el),
162
+ rootMargin: "200% 0px"
163
+ });
149
164
  observer.observe(el);
150
165
  return () => observer.disconnect();
151
166
  }, [ref, near]);
package/dist/icons.d.ts CHANGED
@@ -1,6 +1,7 @@
1
- import type { LucideProps } from 'lucide-react';
2
- import type { ReactNode } from 'react';
3
- export interface IconProps extends LucideProps {
1
+ import type { ReactNode, SVGProps } from 'react';
2
+ export interface IconProps extends Omit<SVGProps<SVGSVGElement>, 'ref'> {
3
+ /** Width and height; the text's size by default. */
4
+ size?: string | number | undefined;
4
5
  /** Names the icon for assistive technology. Without it, or an `aria-label`,
5
6
  * the icon is decoration and hidden from assistive technology. */
6
7
  title?: string | undefined;