@tristan2828/ui-foundation 4.30.0 → 4.31.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/README.md CHANGED
@@ -18,7 +18,7 @@ than installing this by hand:
18
18
 
19
19
  | Import | What |
20
20
  |---|---|
21
- | `@tristan2828/ui-foundation` | `AppShell`, `DataTable`, `EntityView` (with `EditableValue`, `editInPlace`), `EntityForm`, `Markdown`, `RichTextEditor`, `ErrorState`, `MultiChoice`, `MultiReference`, `ListEditor`, `PasswordInput`, `RouteErrorBoundary`, `FoundationProviders`, `DataEnvironmentBanner`, `LoginRoute`, `RegisterRoute`, `useAuth`, `useTableUrlState`, `useRecordUpdate`, `useDebouncedValue`, `createQueryClient`, `cn`; types `Page`, `AppError`, `QuerySpec`. For timed screens (`docs/foundation/timed-screens.md`): `SessionView`, `useSessionClock`, `useWakeLock`, `useMuted`, `usePrefersReducedMotion`, and the audio cues `unlockAudio`, `chime`, `sequence`, `say`, `hush`, `vibrate`. For reminders (`docs/foundation/reminders.md`): `AllowReminders`, `usePushPermission` |
21
+ | `@tristan2828/ui-foundation` | `AppShell`, `DataTable`, `EntityView` (with `EditableValue`, `editInPlace`), `EntityForm`, `Markdown`, `RichTextEditor`, `ErrorState`, `MultiChoice`, `MultiReference`, `ListEditor`, `PasswordInput`, `RouteErrorBoundary`, `FoundationProviders`, `DataEnvironmentBanner`, `LoginRoute`, `RegisterRoute`, `useAuth`, `useTableUrlState`, `useRecordUpdate`, `useDebouncedValue`, `createQueryClient`, `cn`; types `Page`, `AppError`, `QuerySpec`. For timed screens (`docs/foundation/timed-screens.md`): `SessionView`, `useSessionClock`, `useWakeLock`, `useMuted`, `usePrefersReducedMotion`, and the audio cues `unlockAudio`, `chime`, `sequence`, `cancelCues`, `onCuesRecovered`, `cuesInSync`, `keepAudioAlive`, `say`, `hush`, `vibrate`, `canVibrate`. For reminders (`docs/foundation/reminders.md`): `AllowReminders`, `usePushPermission` |
22
22
  | `@tristan2828/ui-foundation/ui/<name>` | shadcn primitives: badge, button, calendar, card, checkbox, combobox, dialog, empty, field, input, input-group, label, popover, select, separator, sheet, sidebar, skeleton, sonner, spinner, switch, table, textarea, tooltip |
23
23
  | `@tristan2828/ui-foundation/gateway` | `safeFetch`, `toAppError`, `networkError` — for an app's `src/api/gateway/` only |
24
24
  | `@tristan2828/ui-foundation/mocks` | MSW: `authHandlers`, `environmentHandlers`, `resetMockAuth`, `getMockCurrentUser`, `MOCK_USER`, `exposeMswForE2E` |
@@ -390,8 +390,11 @@ is a filled-in example while the Widgets demo is still here).
390
390
  - **A source credit** (`footer`), only when the record's data comes
391
391
  from an outside service whose terms ask for one (many free APIs
392
392
  do: a film database, a games catalogue): `<SourceCredit sources={[{
393
- name, href, note }]} />`, with `note` the sentence the terms ask for,
394
- word for word. Logos and their rules are the app's own.
393
+ name, href, note, icon }]} />`, with `note` the sentence the terms ask
394
+ for, word for word, and `icon` the service's mark when the app has
395
+ one (the same component it shows for that service elsewhere; drawn
396
+ before the name, which stays the link's text). Logos and their rules
397
+ are the app's own.
395
398
  - **Every value renders the way its table cell does**: badges, names
396
399
  of linked records, a done-count, from the shared lookups in
397
400
  `<entity>-format.ts`. Never re-derive one for the view.
@@ -104,7 +104,8 @@ lower; say so in a comment.
104
104
  ## Logos and brand marks
105
105
 
106
106
  An app's own logo (`AppShell`'s `logo`) and the marks of the services it
107
- links to (a video site's mark on "Open on …", on a `WidgetCard` about it)
107
+ links to (a video site's mark on "Open on …", on a `WidgetCard` about it,
108
+ beside its name in a `SourceCredit`)
108
109
  are **icon components** (`IconComponent`): sized by a `size-*` class,
109
110
  `aria-hidden` passed through to the `<svg>`, with the same props as a
110
111
  Lucide icon, so they go wherever an icon goes. Lucide has no brand logos,
@@ -143,7 +144,8 @@ Colour them in one of two ways:
143
144
  one-colour mark there.
144
145
 
145
146
  The package's own stories show the slots with made-up marks in
146
- `currentColor` (`app/AppShell` With logo, `app/WidgetCard` Brand mark).
147
+ `currentColor` (`app/AppShell` With logo, `app/WidgetCard` Brand mark,
148
+ `app/EntityView` Notice and footer).
147
149
 
148
150
  ## Categorical colour
149
151
 
@@ -1,3 +1,4 @@
1
+ import type { IconComponent } from './icon-component.js';
1
2
  export type Source = {
2
3
  /** The service's name, the link's text: "TMDB". */
3
4
  name: string;
@@ -5,6 +6,12 @@ export type Source = {
5
6
  href: string;
6
7
  /** A sentence the service's terms ask for, shown after the list as written. */
7
8
  note?: string;
9
+ /**
10
+ * The service's mark, drawn before its name inside the link and hidden
11
+ * from screen readers: the name stays the link's text. The app's own
12
+ * component, the one it shows for this service elsewhere.
13
+ */
14
+ icon?: IconComponent;
8
15
  };
9
16
  export type SourceCreditProps = {
10
17
  sources: Source[];
@@ -2,8 +2,8 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  // Credits the outside services a record's data comes from, as many free
3
3
  // APIs require on every screen showing their data ("This product uses the
4
4
  // TMDB API but is not endorsed or certified by TMDB"). EntityView's
5
- // `footer` is its usual home. Text and links only: a service's logo and
6
- // its usage rules are the app's.
5
+ // `footer` is its usual home. A source's mark is the app's own icon
6
+ // component (`icon`), drawn before its name; its usage rules are the app's.
7
7
  import { Fragment } from 'react';
8
8
  import { ExternalLinkIcon } from 'lucide-react';
9
9
  function joined(index, count) {
@@ -17,5 +17,5 @@ export function SourceCredit({ sources, lead = 'Data from' }) {
17
17
  if (sources.length === 0)
18
18
  return null;
19
19
  const notes = sources.flatMap((source) => (source.note ? [source.note] : []));
20
- return (_jsxs("p", { "data-slot": "source-credit", className: "type-caption text-muted-foreground", children: [lead, ' ', sources.map((source, index) => (_jsxs(Fragment, { children: [joined(index, sources.length), _jsxs("a", { href: source.href, target: "_blank", rel: "noreferrer", className: "rounded-sm underline underline-offset-2 outline-none hover:text-foreground focus-visible:ring-3 focus-visible:ring-ring/50", children: [source.name, _jsx(ExternalLinkIcon, { "aria-hidden": "true", className: "ml-0.5 inline size-3 align-baseline" }), _jsx("span", { className: "sr-only", children: " (opens in a new tab)" })] })] }, source.name))), ".", notes.map((note) => ` ${note}`).join('')] }));
20
+ return (_jsxs("p", { "data-slot": "source-credit", className: "type-caption text-muted-foreground", children: [lead, ' ', sources.map(({ icon: Icon, ...source }, index) => (_jsxs(Fragment, { children: [joined(index, sources.length), _jsxs("a", { href: source.href, target: "_blank", rel: "noreferrer", className: "rounded-sm underline underline-offset-2 outline-none hover:text-foreground focus-visible:ring-3 focus-visible:ring-ring/50", children: [Icon && _jsx(Icon, { "aria-hidden": "true", className: "mr-1 inline size-3.5 align-text-bottom" }), source.name, _jsx(ExternalLinkIcon, { "aria-hidden": "true", className: "ml-0.5 inline size-3 align-baseline" }), _jsx("span", { className: "sr-only", children: " (opens in a new tab)" })] })] }, source.name))), ".", notes.map((note) => ` ${note}`).join('')] }));
21
21
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tristan2828/ui-foundation",
3
- "version": "4.30.0",
3
+ "version": "4.31.0",
4
4
  "description": "Shared UI foundation for contract-first, database-backed React apps: shadcn primitives, DataTable/EntityForm composites, auth, the gateway error seam, design tokens, lint rules and the AI playbook.",
5
5
  "license": "MIT",
6
6
  "repository": {