@lateralus-ai/shipping-ui 2.0.0-dev.16 → 2.0.0-dev.18

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.
@@ -82,6 +82,10 @@ export declare const theme: {
82
82
  md: "6px";
83
83
  sm: "4px";
84
84
  };
85
+ /** Extends Tailwind’s max-width scale (7xl = 1280px). */
86
+ maxWidth: {
87
+ "8xl": string;
88
+ };
85
89
  colors: {
86
90
  grey: {
87
91
  readonly 50: "#fdfdfc";
@@ -0,0 +1 @@
1
+ "use strict";const n={50:"#fdfdfc",100:"#f4f2ea",200:"#e1ddce",300:"#bfb9a5",400:"#969080",500:"#6e6960",600:"#524e47",700:"#3a3733",800:"#312e2a",900:"#262420"},o={50:"#f5f8f2",100:"#dde6d2",200:"#c5d4b3",300:"#a8bf8f",400:"#7a9a5e",500:"#5a7a42",600:"#456332",700:"#304624",800:"#223018",900:"#141c0e"},d={50:"#eef1f8",100:"#dae0ef",200:"#b8c4e0",300:"#8fa3cc",400:"#5274ae",500:"#3a5d96",600:"#154199",700:"#103480",800:"#0c2860",900:"#081c42"},c={50:"#faf3f1",100:"#f0d3cc",200:"#e4b0a5",300:"#c97a6b",400:"#b85a48",500:"#a0392a",600:"#802c20",700:"#621f16",800:"#45150f",900:"#2a0c08"},l={50:"#faf1de",100:"#f5e4c0",200:"#efd49a",300:"#e4bc6a",400:"#d9a03e",500:"#c48820",600:"#8f6318",700:"#5a3d0d",800:"#3d2a09",900:"#261a05"},s={50:"#f6f2f8",100:"#e2d9e8",200:"#cbbad6",300:"#b09bc3",400:"#957cb0",500:"#7a5d9d",600:"#5e4680",700:"#3d2e4a",800:"#2a2033",900:"#18121c"},p="#ffffff",a={grey:n,green:o,blue:d,red:c,orange:l,purple:s},t={raise1:"0 1px 2px rgba(38, 36, 32, 0.06), 0 1px 3px rgba(38, 36, 32, 0.04)",raise2:"0 2px 8px rgba(38, 36, 32, 0.08), 0 1px 2px rgba(38, 36, 32, 0.04)",raise3:"0 8px 24px rgba(38, 36, 32, 0.12), 0 2px 8px rgba(38, 36, 32, 0.06)"},r={control:"8px",lg:"8px",md:"6px",sm:"4px"},e=(v,i)=>[v,{lineHeight:i.lineHeight,letterSpacing:i.letterSpacing,...i.fontWeight?{fontWeight:String(i.fontWeight)}:{}}],g={fontFamily:{heading:['"TRY Vesterbro"',"Libre Baskerville","Georgia","serif"],sans:['"Matter-TRIAL"',"DM Sans","system-ui","sans-serif"],mono:["Roboto Mono","monospace"]},fontSize:{title:e("28px",{lineHeight:"34px",letterSpacing:"-0.02em"}),"title-em":e("28px",{lineHeight:"34px",letterSpacing:"-0.02em",fontWeight:500}),heading:e("22px",{lineHeight:"28px",letterSpacing:"0",fontWeight:500}),subheader:e("18px",{lineHeight:"28px",letterSpacing:"-0.01em"}),"subheader-em":e("18px",{lineHeight:"28px",letterSpacing:"-0.01em",fontWeight:500}),body:e("16px",{lineHeight:"24px",letterSpacing:"-0.01em"}),"body-em":e("16px",{lineHeight:"24px",letterSpacing:"-0.01em",fontWeight:500}),"caption-1":e("16px",{lineHeight:"22px",letterSpacing:"0.02em"}),"caption-1-em":e("16px",{lineHeight:"22px",letterSpacing:"0.02em",fontWeight:500}),"caption-2":e("14px",{lineHeight:"20px",letterSpacing:"0.01em"}),"caption-2-em":e("14px",{lineHeight:"20px",letterSpacing:"0.01em",fontWeight:500}),footnote:e("13px",{lineHeight:"17px",letterSpacing:"0"}),"footnote-em":e("13px",{lineHeight:"17px",letterSpacing:"0",fontWeight:500})},boxShadow:{raise1:t.raise1,raise2:t.raise2,raise3:t.raise3},borderRadius:{control:r.control,lg:r.lg,md:r.md,sm:r.sm},maxWidth:{"8xl":"1640px"},colors:{grey:a.grey,gray:a.grey,green:a.green,blue:a.blue,red:a.red,orange:a.orange,purple:a.purple,white:"#ffffff",action:{primary:{idle:"var(--action-primary-idle)","on-idle":"var(--action-primary-on-idle)",hover:"var(--action-primary-hover)","on-hover":"var(--action-primary-on-hover)",disabled:"var(--action-primary-disabled)","on-disabled":"var(--action-primary-on-disabled)"},secondary:{idle:"var(--action-secondary-idle)","on-idle":"var(--action-secondary-on-idle)",hover:"var(--action-secondary-hover)","on-hover":"var(--action-secondary-on-hover)",disabled:"var(--action-secondary-disabled)","on-disabled":"var(--action-secondary-on-disabled)"},tertiary:{idle:"var(--action-tertiary-idle)","on-idle":"var(--action-tertiary-on-idle)",hover:"var(--action-tertiary-hover)","on-hover":"var(--action-tertiary-on-hover)",disabled:"var(--action-tertiary-disabled)","on-disabled":"var(--action-tertiary-on-disabled)"},quaternary:{idle:"var(--action-quaternary-idle)","on-idle":"var(--action-quaternary-on-idle)",hover:"var(--action-quaternary-hover)","on-hover":"var(--action-quaternary-on-hover)",disabled:"var(--action-quaternary-disabled)","on-disabled":"var(--action-quaternary-on-disabled)"},destructive:{idle:"var(--action-destructive-idle)","on-idle":"var(--action-destructive-on-idle)",hover:"var(--action-destructive-hover)","on-hover":"var(--action-destructive-on-hover)",disabled:"var(--action-destructive-disabled)","on-disabled":"var(--action-destructive-on-disabled)"}},background:{primary:"var(--background-primary)",secondary:"var(--background-secondary)",tertiary:"var(--background-tertiary)",hover:"var(--background-hover)",selected:"var(--background-selected)",overlay:"var(--background-overlay)"},display:{"on-light":{primary:"var(--display-on-light-primary)",secondary:"var(--display-on-light-secondary)",tertiary:"var(--display-on-light-tertiary)",quaternary:"var(--display-on-light-quaternary)"},"on-dark":{primary:"var(--display-on-dark-primary)",secondary:"var(--display-on-dark-secondary)",tertiary:"var(--display-on-dark-tertiary)",quaternary:"var(--display-on-dark-quaternary)"}},accent:{"on-light":"var(--accent-on-light)","on-dark":"var(--accent-on-dark)","bg-light":"var(--accent-bg-light)","bg-lighter":"var(--accent-bg-lighter)"},divider:{primary:"var(--divider-primary)",secondary:"var(--divider-secondary)"},meta:{green:"var(--meta-green)",onGreen:"var(--meta-on-green)",orange:"var(--meta-orange)",onOrange:"var(--meta-on-orange)",red:"var(--meta-red)",onRed:"var(--meta-on-red)",blue:"var(--meta-blue)",onBlue:"var(--meta-on-blue)",purple:"var(--meta-purple)",onPurple:"var(--meta-on-purple)"},"text-primary":"var(--display-on-light-primary)","text-secondary":"var(--display-on-light-secondary)","text-tertiary":"var(--display-on-light-tertiary)","surface-primary":"var(--background-primary)","surface-secondary":"var(--background-secondary)","surface-hover":"var(--background-secondary)","border-light":"var(--divider-primary)","border-medium":"var(--divider-secondary)"}};exports.blue=d;exports.borderRadius=r;exports.colorScales=a;exports.elevation=t;exports.green=o;exports.grey=n;exports.orange=l;exports.purple=s;exports.red=c;exports.theme=g;exports.white=p;
@@ -53,7 +53,7 @@ const o = {
53
53
  700: "#5a3d0d",
54
54
  800: "#3d2a09",
55
55
  900: "#261a05"
56
- }, v = {
56
+ }, p = {
57
57
  50: "#f6f2f8",
58
58
  100: "#e2d9e8",
59
59
  200: "#cbbad6",
@@ -64,7 +64,7 @@ const o = {
64
64
  700: "#3d2e4a",
65
65
  800: "#2a2033",
66
66
  900: "#18121c"
67
- }, p = "#ffffff", a = { grey: o, green: d, blue: c, red: l, orange: s, purple: v }, t = {
67
+ }, v = "#ffffff", a = { grey: o, green: d, blue: c, red: l, orange: s, purple: p }, t = {
68
68
  raise1: "0 1px 2px rgba(38, 36, 32, 0.06), 0 1px 3px rgba(38, 36, 32, 0.04)",
69
69
  raise2: "0 2px 8px rgba(38, 36, 32, 0.08), 0 1px 2px rgba(38, 36, 32, 0.04)",
70
70
  raise3: "0 8px 24px rgba(38, 36, 32, 0.12), 0 2px 8px rgba(38, 36, 32, 0.06)"
@@ -140,6 +140,10 @@ const o = {
140
140
  md: i.md,
141
141
  sm: i.sm
142
142
  },
143
+ /** Extends Tailwind’s max-width scale (7xl = 1280px). */
144
+ maxWidth: {
145
+ "8xl": "1640px"
146
+ },
143
147
  colors: {
144
148
  grey: a.grey,
145
149
  gray: a.grey,
@@ -254,8 +258,8 @@ export {
254
258
  t as e,
255
259
  o as g,
256
260
  s as o,
257
- v as p,
261
+ p,
258
262
  l as r,
259
263
  g as t,
260
- p as w
264
+ v as w
261
265
  };
package/dist/theme.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./theme-entry-ygTpGaNC.js");exports.tailwindTheme=e.theme;exports.theme=e.theme;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./theme-entry-Dwr2mV7_.js");exports.tailwindTheme=e.theme;exports.theme=e.theme;
package/dist/theme.esm.js CHANGED
@@ -1,4 +1,4 @@
1
- import { t as a, t as m } from "./theme-entry-BUK3MJUJ.mjs";
1
+ import { t as a, t as m } from "./theme-entry-tLBc6zGT.mjs";
2
2
  export {
3
3
  a as tailwindTheme,
4
4
  m as theme
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lateralus-ai/shipping-ui",
3
- "version": "2.0.0-dev.16",
3
+ "version": "2.0.0-dev.18",
4
4
  "description": "Shared UI theme and components for Lateralus shipping applications",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.esm.js",
@@ -0,0 +1,132 @@
1
+ import {
2
+ type ComponentPropsWithoutRef,
3
+ type MouseEvent,
4
+ type ReactNode,
5
+ } from "react";
6
+ import { cn } from "../utils/cn";
7
+
8
+ export type PageHeaderCrumb = {
9
+ label: string;
10
+ /** Renders a real `<a href>` so native browser controls work. */
11
+ href: string;
12
+ /**
13
+ * Optional SPA handler. Callers should `preventDefault` only for unmodified
14
+ * primary clicks; modifier / middle-click keep native anchor behavior.
15
+ */
16
+ onClick?: (event: MouseEvent<HTMLAnchorElement>) => void;
17
+ };
18
+
19
+ export type PageHeaderProps = {
20
+ /** Leading icon (Figma 24×24). */
21
+ icon?: ReactNode;
22
+ /** Standard title, or current (last) segment when `crumbs` is set. */
23
+ title: ReactNode;
24
+ /**
25
+ * Parent breadcrumb links. When present, renders Nested: icon + parents + `/`
26
+ * + current `title` (non-link).
27
+ */
28
+ crumbs?: PageHeaderCrumb[];
29
+ /** Right-hand actions slot (buttons, menus, etc.). */
30
+ actions?: ReactNode;
31
+ className?: string;
32
+ };
33
+
34
+ export type PageHeaderShellProps = ComponentPropsWithoutRef<"div">;
35
+
36
+ export type PageHeaderBodyProps = ComponentPropsWithoutRef<"div">;
37
+
38
+ /**
39
+ * Page title row — Figma `Header` (389:8850).
40
+ * Variants: Standard (icon + title) | Nested (icon + crumb trail).
41
+ */
42
+ function PageHeaderRoot({
43
+ icon,
44
+ title,
45
+ crumbs,
46
+ actions,
47
+ className,
48
+ }: PageHeaderProps) {
49
+ const isNested = Boolean(crumbs?.length);
50
+
51
+ return (
52
+ <div
53
+ data-variant={isNested ? "nested" : "standard"}
54
+ className={cn(
55
+ "flex w-full shrink-0 items-center justify-between gap-4",
56
+ "min-h-10",
57
+ className,
58
+ )}
59
+ >
60
+ <div className="flex min-w-0 flex-1 items-center gap-2">
61
+ {icon != null && (
62
+ <span className="inline-flex size-6 shrink-0 items-center justify-center [&>svg]:size-6">
63
+ {icon}
64
+ </span>
65
+ )}
66
+
67
+ {isNested ? (
68
+ <nav aria-label="Breadcrumb" className="min-w-0">
69
+ <ol className="flex min-w-0 flex-wrap items-center gap-2">
70
+ {crumbs!.map((crumb) => (
71
+ <li key={`${crumb.href}-${crumb.label}`} className="contents">
72
+ <a
73
+ href={crumb.href}
74
+ onClick={crumb.onClick}
75
+ className="truncate text-caption-1-em text-display-on-light-secondary transition-colors hover:text-display-on-light-primary"
76
+ >
77
+ {crumb.label}
78
+ </a>
79
+ <span
80
+ aria-hidden
81
+ className="text-caption-1-em text-display-on-light-secondary"
82
+ >
83
+ /
84
+ </span>
85
+ </li>
86
+ ))}
87
+ <li
88
+ aria-current="page"
89
+ className="min-w-0 truncate text-caption-1-em text-display-on-light-primary"
90
+ >
91
+ {title}
92
+ </li>
93
+ </ol>
94
+ </nav>
95
+ ) : (
96
+ <h1 className="min-w-0 truncate text-caption-1-em text-display-on-light-primary">
97
+ {title}
98
+ </h1>
99
+ )}
100
+ </div>
101
+
102
+ {actions != null && (
103
+ <div className="flex shrink-0 items-center gap-2">{actions}</div>
104
+ )}
105
+ </div>
106
+ );
107
+ }
108
+
109
+ /** Flex column shell — pin header, scroll body (avoids brittle CSS sticky). */
110
+ function PageHeaderShell({ className, ...props }: PageHeaderShellProps) {
111
+ return (
112
+ <div
113
+ className={cn("flex h-full min-h-0 flex-col", className)}
114
+ {...props}
115
+ />
116
+ );
117
+ }
118
+
119
+ /** Scrollable page content under a pinned PageHeader. */
120
+ function PageHeaderBody({ className, ...props }: PageHeaderBodyProps) {
121
+ return (
122
+ <div
123
+ className={cn("min-h-0 flex-1 overflow-y-auto", className)}
124
+ {...props}
125
+ />
126
+ );
127
+ }
128
+
129
+ export const PageHeader = Object.assign(PageHeaderRoot, {
130
+ Shell: PageHeaderShell,
131
+ Body: PageHeaderBody,
132
+ });
@@ -18,6 +18,13 @@ export {
18
18
  type TabsAppearance,
19
19
  } from "./Tabs";
20
20
  export { Header, type HeaderProps, type HeaderVariant } from "./Header";
21
+ export {
22
+ PageHeader,
23
+ type PageHeaderProps,
24
+ type PageHeaderCrumb,
25
+ type PageHeaderShellProps,
26
+ type PageHeaderBodyProps,
27
+ } from "./PageHeader";
21
28
  export { Entry, type EntryProps, type EntryType, type EntryState, type EntryVariant } from "./Entry";
22
29
  export { EmptyState, type EmptyStateProps } from "./EmptyState";
23
30
  export {
package/src/index.ts CHANGED
@@ -19,6 +19,7 @@ export {
19
19
  TabsTrigger,
20
20
  TabsContent,
21
21
  Header,
22
+ PageHeader,
22
23
  Entry,
23
24
  EmptyState,
24
25
  Modal,
@@ -59,6 +60,10 @@ export type {
59
60
  TabsAppearance,
60
61
  HeaderProps,
61
62
  HeaderVariant,
63
+ PageHeaderProps,
64
+ PageHeaderCrumb,
65
+ PageHeaderShellProps,
66
+ PageHeaderBodyProps,
62
67
  EntryProps,
63
68
  EntryType,
64
69
  EntryState,
@@ -1,15 +1,16 @@
1
- import { Badge, Button } from "../../primitives";
1
+ import { Badge, Button, IconButton } from "../../primitives";
2
2
  import {
3
3
  EmptyState,
4
4
  Entry,
5
5
  Header,
6
+ PageHeader,
6
7
  Tab,
7
8
  Tabs,
8
9
  TabsContent,
9
10
  TabsList,
10
11
  TabsTrigger,
11
12
  } from "../../components";
12
- import { TickIcon } from "../../icons";
13
+ import { MoreIcon, ShipIcon, TickIcon } from "../../icons";
13
14
  import { WorkflowsIllustration } from "../../illustrations";
14
15
  import {
15
16
  FigmaContent,
@@ -20,6 +21,15 @@ import {
20
21
  } from "../_layout";
21
22
  import { FIGMA_WIDTHS } from "./figma-widths";
22
23
 
24
+ const demoActions = (
25
+ <>
26
+ <Button hierarchy="secondary">Button</Button>
27
+ <IconButton hierarchy="tertiary" size="small" aria-label="More">
28
+ <MoreIcon size="small" />
29
+ </IconButton>
30
+ </>
31
+ );
32
+
23
33
  export const ContentCanvas = () => (
24
34
  <FigmaPage title="Content" width={FIGMA_WIDTHS.content}>
25
35
  <FigmaContent>
@@ -40,13 +50,22 @@ export const ContentCanvas = () => (
40
50
  <TabsTrigger value="reports">Reports</TabsTrigger>
41
51
  <TabsTrigger value="issues">Issues</TabsTrigger>
42
52
  </TabsList>
43
- <TabsContent value="all" className="pt-3 text-caption-2 text-display-on-light-secondary">
53
+ <TabsContent
54
+ value="all"
55
+ className="pt-3 text-caption-2 text-display-on-light-secondary"
56
+ >
44
57
  All content
45
58
  </TabsContent>
46
- <TabsContent value="reports" className="pt-3 text-caption-2 text-display-on-light-secondary">
59
+ <TabsContent
60
+ value="reports"
61
+ className="pt-3 text-caption-2 text-display-on-light-secondary"
62
+ >
47
63
  Reports content
48
64
  </TabsContent>
49
- <TabsContent value="issues" className="pt-3 text-caption-2 text-display-on-light-secondary">
65
+ <TabsContent
66
+ value="issues"
67
+ className="pt-3 text-caption-2 text-display-on-light-secondary"
68
+ >
50
69
  Issues content
51
70
  </TabsContent>
52
71
  </Tabs>
@@ -60,10 +79,16 @@ export const ContentCanvas = () => (
60
79
  <Badge color="blue">3</Badge>
61
80
  </TabsTrigger>
62
81
  </TabsList>
63
- <TabsContent value="active" className="pt-3 text-caption-2 text-display-on-light-secondary">
82
+ <TabsContent
83
+ value="active"
84
+ className="pt-3 text-caption-2 text-display-on-light-secondary"
85
+ >
64
86
  Active content
65
87
  </TabsContent>
66
- <TabsContent value="completed" className="pt-3 text-caption-2 text-display-on-light-secondary">
88
+ <TabsContent
89
+ value="completed"
90
+ className="pt-3 text-caption-2 text-display-on-light-secondary"
91
+ >
67
92
  Completed content
68
93
  </TabsContent>
69
94
  </Tabs>
@@ -92,7 +117,44 @@ export const ContentCanvas = () => (
92
117
  </div>
93
118
  </FigmaSection>
94
119
 
95
- <FigmaSection label="Header">
120
+ <FigmaSection label="PageHeader">
121
+ <div className="max-w-[1120px] space-y-8">
122
+ <FigmaVariant label="Standard">
123
+ <PageHeader
124
+ icon={<ShipIcon size="large" />}
125
+ title="Title"
126
+ actions={demoActions}
127
+ />
128
+ </FigmaVariant>
129
+ <FigmaVariant label="Nested">
130
+ <PageHeader
131
+ icon={<ShipIcon size="large" />}
132
+ title="Title"
133
+ crumbs={[{ label: "Parent", href: "#parent" }]}
134
+ actions={demoActions}
135
+ />
136
+ </FigmaVariant>
137
+ <FigmaVariant label="Shell (pinned header)">
138
+ <div className="h-64 overflow-hidden rounded-control border border-divider-primary">
139
+ <PageHeader.Shell className="bg-background-primary p-4">
140
+ <PageHeader
141
+ icon={<ShipIcon size="large" />}
142
+ title="Fleet"
143
+ crumbs={[{ label: "Ships", href: "#ships" }]}
144
+ actions={demoActions}
145
+ />
146
+ <PageHeader.Body className="mt-4 space-y-2 text-caption-2 text-display-on-light-secondary">
147
+ {Array.from({ length: 20 }, (_, i) => (
148
+ <p key={i}>Scrollable body line {i + 1}</p>
149
+ ))}
150
+ </PageHeader.Body>
151
+ </PageHeader.Shell>
152
+ </div>
153
+ </FigmaVariant>
154
+ </div>
155
+ </FigmaSection>
156
+
157
+ <FigmaSection label="Header (legacy)">
96
158
  <div className="max-w-xl space-y-4">
97
159
  <Header title="Fleet overview" />
98
160
  <Header
@@ -115,7 +177,9 @@ export const ContentCanvas = () => (
115
177
  state="active"
116
178
  title="Ballast Pump Not Starting"
117
179
  subtitle="The fuel pump won’t start consistently."
118
- trailing={<span className="text-display-on-light-secondary">···</span>}
180
+ trailing={
181
+ <span className="text-display-on-light-secondary">···</span>
182
+ }
119
183
  />
120
184
  <Entry
121
185
  variant="issue"
@@ -145,7 +209,9 @@ export const ContentCanvas = () => (
145
209
  <div className="w-80">
146
210
  <EmptyState
147
211
  title="No reports"
148
- illustration={<WorkflowsIllustration className="mx-auto h-24 w-32" />}
212
+ illustration={
213
+ <WorkflowsIllustration className="mx-auto h-24 w-32" />
214
+ }
149
215
  action={{ children: "Create report", hierarchy: "primary" }}
150
216
  />
151
217
  </div>