@birdycom/omniaui-react 0.12.2 → 0.12.3

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/manifest.json CHANGED
@@ -258,6 +258,12 @@
258
258
  "file": "src/components/FormField.tsx",
259
259
  "snippet": "@birdycom/omniaui/snippets/form-field.html"
260
260
  },
261
+ {
262
+ "name": "glass",
263
+ "kind": "component",
264
+ "file": "src/components/Glass.tsx",
265
+ "snippet": "@birdycom/omniaui/snippets/glass.html"
266
+ },
261
267
  {
262
268
  "name": "greeting",
263
269
  "kind": "component",
@@ -534,6 +540,12 @@
534
540
  "file": "src/components/StatCard.tsx",
535
541
  "snippet": "@birdycom/omniaui/snippets/stat-card.html"
536
542
  },
543
+ {
544
+ "name": "status-dot",
545
+ "kind": "component",
546
+ "file": "src/components/StatusDot.tsx",
547
+ "snippet": "@birdycom/omniaui/snippets/status-dot.html"
548
+ },
537
549
  {
538
550
  "name": "stepper",
539
551
  "kind": "component",
@@ -606,6 +618,12 @@
606
618
  "file": "src/components/Timeline.tsx",
607
619
  "snippet": "@birdycom/omniaui/snippets/timeline.html"
608
620
  },
621
+ {
622
+ "name": "titlebar",
623
+ "kind": "component",
624
+ "file": "src/components/Titlebar.tsx",
625
+ "snippet": "@birdycom/omniaui/snippets/titlebar.html"
626
+ },
609
627
  {
610
628
  "name": "toast",
611
629
  "kind": "component",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@birdycom/omniaui-react",
3
- "version": "0.12.2",
3
+ "version": "0.12.3",
4
4
  "description": "OmniaUI for React: typed function components and page blocks that render the exact @birdycom/omniaui and @birdycom/omniaui-blocks markup.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -8,15 +8,17 @@ export interface AlertProps extends Omit<HTMLAttributes<HTMLDivElement>, "title"
8
8
  variant?: AlertVariant;
9
9
  /** Heading (`oc-alert-title`). */
10
10
  title?: ReactNode;
11
+ /** Compact message under a field or a form (an error, a hint). */
12
+ inline?: boolean;
11
13
  }
12
14
 
13
15
  /** `<div class="oc-alert oc-alert--{variant}" role="status">`; children render inside `oc-alert-description`. */
14
16
  export const Alert = forwardRef<HTMLDivElement, AlertProps>(function Alert(
15
- { variant = "info", title, children, className, role = "status", ...rest },
17
+ { variant = "info", title, inline, children, className, role = "status", ...rest },
16
18
  ref,
17
19
  ) {
18
20
  return (
19
- <div ref={ref} role={role} className={cx("oc-alert", `oc-alert--${variant}`, className)} {...rest}>
21
+ <div ref={ref} role={role} className={cx("oc-alert", `oc-alert--${variant}`, inline && "oc-alert--inline", className)} {...rest}>
20
22
  {title != null && <h4 className="oc-alert-title">{title}</h4>}
21
23
  {children != null && <p className="oc-alert-description">{children}</p>}
22
24
  </div>
@@ -14,16 +14,19 @@ interface ButtonStyleProps {
14
14
  icon?: boolean;
15
15
  /** Fully rounded pill shape. */
16
16
  pill?: boolean;
17
+ /** Inside running text (with variant="link"): no padding or height, the size of the text around it. */
18
+ inline?: boolean;
17
19
  }
18
20
 
19
21
  /** The class string of a button; reuse it on your own elements (router links etc.). */
20
- export function buttonClass({ variant = "default", size = "md", icon, pill, className }: ButtonStyleProps & { className?: string }) {
22
+ export function buttonClass({ variant = "default", size = "md", icon, pill, inline, className }: ButtonStyleProps & { className?: string }) {
21
23
  return cx(
22
24
  "oc-btn",
23
25
  variant !== "default" && `oc-btn--${variant}`,
24
26
  size !== "md" && `oc-btn--${size}`,
25
27
  icon && "oc-btn--icon",
26
28
  pill && "oc-btn--pill",
29
+ inline && "oc-btn--inline",
27
30
  className,
28
31
  );
29
32
  }
@@ -32,18 +35,18 @@ export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement>, Bu
32
35
 
33
36
  /** `<button class="oc-btn oc-btn--{variant} oc-btn--{size}">` */
34
37
  export const Button = forwardRef<HTMLButtonElement, ButtonProps>(function Button(
35
- { variant, size, icon, pill, className, ...rest },
38
+ { variant, size, icon, pill, inline, className, ...rest },
36
39
  ref,
37
40
  ) {
38
- return <button ref={ref} className={buttonClass({ variant, size, icon, pill, className })} {...rest} />;
41
+ return <button ref={ref} className={buttonClass({ variant, size, icon, pill, inline, className })} {...rest} />;
39
42
  });
40
43
 
41
44
  export interface ButtonLinkProps extends AnchorHTMLAttributes<HTMLAnchorElement>, ButtonStyleProps {}
42
45
 
43
46
  /** An anchor styled as a button. */
44
47
  export const ButtonLink = forwardRef<HTMLAnchorElement, ButtonLinkProps>(function ButtonLink(
45
- { variant, size, icon, pill, className, ...rest },
48
+ { variant, size, icon, pill, inline, className, ...rest },
46
49
  ref,
47
50
  ) {
48
- return <a ref={ref} className={buttonClass({ variant, size, icon, pill, className })} {...rest} />;
51
+ return <a ref={ref} className={buttonClass({ variant, size, icon, pill, inline, className })} {...rest} />;
49
52
  });
@@ -0,0 +1,29 @@
1
+ // File: packages/react/src/components/Glass.tsx | Map: types -> Glass (sheet: --strong, --flat) -> GlassPanel (layer: --strong)
2
+ import { forwardRef } from "react";
3
+ import type { HTMLAttributes } from "react";
4
+ import { cx } from "../utils";
5
+
6
+ export interface GlassProps extends HTMLAttributes<HTMLElement> {
7
+ /** More opaque, for text-heavy content. */
8
+ strong?: boolean;
9
+ /** Desktop window where the OS draws the blur: no own blur, no drop shadow. */
10
+ flat?: boolean;
11
+ as?: "div" | "section" | "aside" | "article" | "main";
12
+ }
13
+
14
+ /** `.oc-glass`: see-through sheet with a light top edge; .oc-card inside becomes a glass panel. */
15
+ export const Glass = forwardRef<HTMLElement, GlassProps>(function Glass(
16
+ { strong, flat, as: Tag = "div", className, ...rest },
17
+ ref,
18
+ ) {
19
+ return <Tag ref={ref as React.Ref<HTMLDivElement>} className={cx("oc-glass", strong && "oc-glass--strong", flat && "oc-glass--flat", className)} {...rest} />;
20
+ });
21
+
22
+ export interface GlassPanelProps extends HTMLAttributes<HTMLDivElement> {
23
+ strong?: boolean;
24
+ }
25
+
26
+ /** `.oc-glass-panel`: a lighter layer inside a glass sheet. */
27
+ export const GlassPanel = forwardRef<HTMLDivElement, GlassPanelProps>(function GlassPanel({ strong, className, ...rest }, ref) {
28
+ return <div ref={ref} className={cx("oc-glass-panel", strong && "oc-glass-panel--strong", className)} {...rest} />;
29
+ });
@@ -6,27 +6,44 @@ export interface ProgressProps extends HTMLAttributes<HTMLDivElement> {
6
6
  value: number;
7
7
  /** Maximum (default 100). */
8
8
  max?: number;
9
- /** Bar color. */
9
+ /** Bar color. Wins over `levels`. */
10
10
  variant?: "default" | "success" | "warning" | "danger";
11
+ /** Height: 4 px (xs), 6 px (sm) or 8 px (default). */
12
+ size?: "xs" | "sm" | "md";
13
+ /** Light top edge and a soft glow in the bar color. */
14
+ glow?: boolean;
15
+ /** Color by the value: warning from 70 %, danger from 90 %; or your own [warning, danger] limits in percent. */
16
+ levels?: boolean | [number, number];
17
+ /** A target or schedule point, in the unit of `value`: an upright line on the track. */
18
+ marker?: number;
19
+ }
20
+
21
+ /** The bar color for `levels` at a percentage. */
22
+ export function progressLevel(pct: number, levels: true | [number, number]): "default" | "warning" | "danger" {
23
+ const [warn, danger] = levels === true ? [70, 90] : levels;
24
+ return pct >= danger ? "danger" : pct >= warn ? "warning" : "default";
11
25
  }
12
26
 
13
27
  /** `<div class="oc-progress" role="progressbar">` with an `oc-progress-bar` sized in percent. */
14
28
  export const Progress = forwardRef<HTMLDivElement, ProgressProps>(function Progress(
15
- { value, max = 100, variant = "default", className, ...rest },
29
+ { value, max = 100, variant = "default", size = "md", glow, levels, marker, className, ...rest },
16
30
  ref,
17
31
  ) {
18
- const pct = max > 0 ? Math.min(100, Math.max(0, Math.round((value / max) * 100))) : 0;
32
+ const percent = (v: number) => (max > 0 ? Math.min(100, Math.max(0, (v / max) * 100)) : 0);
33
+ const pct = Math.round(percent(value));
34
+ const tone = variant === "default" && levels ? progressLevel(pct, levels) : variant;
19
35
  return (
20
36
  <div
21
37
  ref={ref}
22
- className={cx("oc-progress", className)}
38
+ className={cx("oc-progress", size !== "md" && `oc-progress--${size}`, glow && "oc-progress--glow", className)}
23
39
  role="progressbar"
24
40
  aria-valuenow={value}
25
41
  aria-valuemin={0}
26
42
  aria-valuemax={max}
27
43
  {...rest}
28
44
  >
29
- <div className={cx("oc-progress-bar", variant !== "default" && `oc-progress-bar--${variant}`)} style={{ width: `${pct}%` }} />
45
+ <div className={cx("oc-progress-bar", tone !== "default" && `oc-progress-bar--${tone}`)} style={{ width: `${pct}%` }} />
46
+ {marker != null && <span className="oc-progress-marker" style={{ left: `${percent(marker)}%` }} />}
30
47
  </div>
31
48
  );
32
49
  });
@@ -6,8 +6,8 @@ import type { HTMLAttributes, ReactNode } from "react";
6
6
  import { cx } from "../utils";
7
7
 
8
8
  export interface ProseProps extends HTMLAttributes<HTMLElement> {
9
- /** All spacing scales along. */
10
- size?: "sm" | "md" | "lg";
9
+ /** All spacing scales along; "compact" for widgets and panels (13 px, tighter, full width). */
10
+ size?: "compact" | "sm" | "md" | "lg";
11
11
  /** No max width (default 68ch). */
12
12
  full?: boolean;
13
13
  as?: "article" | "div" | "section";
@@ -0,0 +1,51 @@
1
+ // File: packages/react/src/components/StatusDot.tsx | Map: types -> StatusDot (span.oc-status-dot) -> Status (p.oc-status: dot + text + meta)
2
+ import { forwardRef } from "react";
3
+ import type { HTMLAttributes, ReactNode } from "react";
4
+ import { cx } from "../utils";
5
+
6
+ export type StatusTone = "default" | "working" | "waiting" | "done" | "error" | "idle";
7
+
8
+ export interface StatusDotProps extends HTMLAttributes<HTMLSpanElement> {
9
+ tone?: StatusTone;
10
+ size?: "sm" | "md" | "lg";
11
+ /** Says the state when the dot stands alone (role="img"); without it the dot is aria-hidden. */
12
+ label?: string;
13
+ }
14
+
15
+ /** `<span class="oc-status-dot oc-status-dot--{tone}">`: a live state light; --working pulses. */
16
+ export const StatusDot = forwardRef<HTMLSpanElement, StatusDotProps>(function StatusDot(
17
+ { tone = "default", size = "md", label, className, ...rest },
18
+ ref,
19
+ ) {
20
+ const a11y = label ? { role: "img", "aria-label": label } : { "aria-hidden": true };
21
+ return (
22
+ <span
23
+ ref={ref}
24
+ className={cx("oc-status-dot", tone !== "default" && `oc-status-dot--${tone}`, size !== "md" && `oc-status-dot--${size}`, className)}
25
+ {...a11y}
26
+ {...rest}
27
+ />
28
+ );
29
+ });
30
+
31
+ export interface StatusProps extends HTMLAttributes<HTMLParagraphElement> {
32
+ tone?: StatusTone;
33
+ /** Right-aligned extra (a time, a count) in tabular numbers. */
34
+ meta?: ReactNode;
35
+ /** The state in words; cuts off with … on one line. */
36
+ children?: ReactNode;
37
+ }
38
+
39
+ /** `<p class="oc-status">`: dot + one line of state text + optional meta. */
40
+ export const Status = forwardRef<HTMLParagraphElement, StatusProps>(function Status(
41
+ { tone = "default", meta, className, children, ...rest },
42
+ ref,
43
+ ) {
44
+ return (
45
+ <p ref={ref} className={cx("oc-status", className)} {...rest}>
46
+ <StatusDot tone={tone} />
47
+ <span className="oc-status__text">{children}</span>
48
+ {meta != null && <span className="oc-status__meta">{meta}</span>}
49
+ </p>
50
+ );
51
+ });
@@ -0,0 +1,61 @@
1
+ // File: packages/react/src/components/Titlebar.tsx
2
+ // Map: types -> TitlebarWindow (column, shared scroll timeline) -> Titlebar (main: icon, title, subtitle | actions: children)
3
+ // -> TitlebarButton (icon button, --close) -> TitlebarBody (scrolls; the bar gets its hairline)
4
+ import { forwardRef } from "react";
5
+ import type { ButtonHTMLAttributes, HTMLAttributes, ReactNode } from "react";
6
+ import { cx } from "../utils";
7
+
8
+ /** `.oc-titlebar-window`: wraps Titlebar + TitlebarBody so the bar sees the body scroll. */
9
+ export const TitlebarWindow = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(function TitlebarWindow({ className, ...rest }, ref) {
10
+ return <div ref={ref} className={cx("oc-titlebar-window", className)} {...rest} />;
11
+ });
12
+
13
+ export interface TitlebarProps extends Omit<HTMLAttributes<HTMLElement>, "title"> {
14
+ title?: ReactNode;
15
+ /** Muted text after a ·, cuts off with …. */
16
+ subtitle?: ReactNode;
17
+ /** An app icon or logo before the title. */
18
+ icon?: ReactNode;
19
+ /** Hairline all the time (no scrolling body). */
20
+ bordered?: boolean;
21
+ size?: "sm" | "md";
22
+ /** The action buttons (TitlebarButton). */
23
+ children?: ReactNode;
24
+ }
25
+
26
+ /** `<header class="oc-titlebar">`: drag area of a frameless window; the buttons stay clickable. */
27
+ export const Titlebar = forwardRef<HTMLElement, TitlebarProps>(function Titlebar(
28
+ { title, subtitle, icon, bordered, size = "md", className, children, ...rest },
29
+ ref,
30
+ ) {
31
+ return (
32
+ <header ref={ref} className={cx("oc-titlebar", bordered && "oc-titlebar--bordered", size === "sm" && "oc-titlebar--sm", className)} {...rest}>
33
+ <div className="oc-titlebar__main">
34
+ {icon != null && <span className="oc-titlebar__icon" aria-hidden="true">{icon}</span>}
35
+ {title != null && <span className="oc-titlebar__title">{title}</span>}
36
+ {subtitle != null && <span className="oc-titlebar__subtitle">{subtitle}</span>}
37
+ </div>
38
+ {children != null && <div className="oc-titlebar__actions">{children}</div>}
39
+ </header>
40
+ );
41
+ });
42
+
43
+ export interface TitlebarButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
44
+ /** The button's name for screen readers (icon-only button). */
45
+ label: string;
46
+ /** Red on hover. */
47
+ close?: boolean;
48
+ }
49
+
50
+ /** `<button class="oc-titlebar__btn">`: compact icon button in the title bar. */
51
+ export const TitlebarButton = forwardRef<HTMLButtonElement, TitlebarButtonProps>(function TitlebarButton(
52
+ { label, close, className, type = "button", ...rest },
53
+ ref,
54
+ ) {
55
+ return <button ref={ref} type={type} aria-label={label} title={label} className={cx("oc-titlebar__btn", close && "oc-titlebar__btn--close", className)} {...rest} />;
56
+ });
57
+
58
+ /** `.oc-titlebar-body`: the part of the window that scrolls. */
59
+ export const TitlebarBody = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(function TitlebarBody({ className, ...rest }, ref) {
60
+ return <div ref={ref} className={cx("oc-titlebar-body", className)} {...rest} />;
61
+ });
package/src/index.ts CHANGED
@@ -44,6 +44,7 @@ export * from "./components/FactList";
44
44
  export * from "./components/FilterCounts";
45
45
  export * from "./components/FlipCard";
46
46
  export * from "./components/FormField";
47
+ export * from "./components/Glass";
47
48
  export * from "./components/Greeting";
48
49
  export * from "./components/Heading";
49
50
  export * from "./components/Icon";
@@ -90,6 +91,7 @@ export * from "./components/SortableRow";
90
91
  export * from "./components/StackCard";
91
92
  export * from "./components/StackList";
92
93
  export * from "./components/StatCard";
94
+ export * from "./components/StatusDot";
93
95
  export * from "./components/Stepper";
94
96
  export * from "./components/Switch";
95
97
  export * from "./components/Table";
@@ -102,6 +104,7 @@ export * from "./components/Textarea";
102
104
  export * from "./components/ThemeSwitch";
103
105
  export * from "./components/TimeInput";
104
106
  export * from "./components/Timeline";
107
+ export * from "./components/Titlebar";
105
108
  export * from "./components/Toast";
106
109
  export * from "./components/ToggleField";
107
110
  export * from "./components/Tooltip";