Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .changeset/dark-mode-follows-system.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
'@wirecore/wirelayer-tokens': minor
'@wirecore/wirelayer-css': minor
'@wirecore/wirelayer-vue': patch
'@wirecore/wirelayer-react': patch
---

**Behavior change:** without `data-mode` the page now follows the system setting (`prefers-color-scheme`) instead
of always being light. `data-mode="light"` or `data-mode="dark"` forces a mode as before, also for a part of the page
(a forced light part stays light on a dark system).

- The `dark:` variant follows the same rules, and so do the dark colors of the built-in themes and of custom themes
generated with `themeToCss`.
- Charts (Chart, ChartCanvas, Sparkline) recolor when the system setting changes.
- Apps that relied on "no attribute = light" and want to keep it: set `data-mode="light"` on `<html>`. Apps that set
`data-mode` from `prefers-color-scheme` with JavaScript can drop that.
11 changes: 11 additions & 0 deletions .changeset/datatable-csv-separator.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
'@wirecore/wirelayer-recipes': minor
'@wirecore/wirelayer-vue': minor
'@wirecore/wirelayer-react': minor
'@wirecore/wirelayer-native': minor
---

DataTable: new prop `csvSeparator` for the CSV export, also for the built-in export button. By default the separator
now follows the locale like a spreadsheet does (`csvSeparator(locale)` from the recipes): `;` where numbers have a
decimal comma (de-DE, fr-FR …), `,` otherwise (en-US …). Before, it was always `;`. `exportCsv({ separator })` still
wins over both.
22 changes: 22 additions & 0 deletions .changeset/datatable-feature-modules.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
'@wirecore/wirelayer-vue': minor
'@wirecore/wirelayer-react': minor
---

**Breaking:** DataTable feature modules. Parts of the DataTable that bring a lot of code along are imported on their own
and passed in `features` – a minimal table is about a fifth smaller (gzip), and `@tanstack/vue-virtual` or
`@tanstack/react-virtual` is only needed for virtual rows.

- `virtual` needs the feature `virtualRows` from `/virtual`; without it all rows are rendered (with a hint in
development).
- Date and number filters and editors use the browser's inputs; the DatePicker and NumberInput come with the features
`dateInput` and `numberInput` from `/data-table`.

```tsx
import { DataTable, dateInput, numberInput } from '@wirecore/wirelayer-react/data-table'; // or -vue
import { virtualRows } from '@wirecore/wirelayer-react/virtual';

const features = [virtualRows, dateInput, numberInput];

<DataTable data={rows} columns={columns} features={features} scrollHeight="480px" virtual />;
```
10 changes: 10 additions & 0 deletions .changeset/docs-english-default.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
'@wirecore/wirelayer-mcp': patch
'@wirecore/wirelayer-vue': patch
'@wirecore/wirelayer-react': patch
'@wirecore/wirelayer-native': patch
'@wirecore/wirelayer-recipes': patch
---

Documentation says English is the default language, as it is in the code: `useLocale` and the MCP server/`llms.txt`
no longer claim German without a `LocaleProvider`.
21 changes: 21 additions & 0 deletions .changeset/german-language-module.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
'@wirecore/wirelayer-recipes': minor
'@wirecore/wirelayer-vue': minor
'@wirecore/wirelayer-react': minor
'@wirecore/wirelayer-native': minor
---

**Breaking:** German is a language module like French or Spanish and no longer part of the default bundle (about half
of the built-in texts). Only English is built in. Apps that use German register it once at startup, before the first
render:

```ts
import { registerLocale } from '@wirecore/wirelayer-vue'; // or -react, -native
import { de } from '@wirecore/wirelayer-vue/locales/de';

registerLocale('de', de);
```

`messages` only contains `en` now; the German texts are `de` from `…/locales/de`. The German names and search words of
the EmojiPicker moved there as well (`emojiPicker.emojis`); the built-in emojis are English. `localeEmojis(texts)` no
longer takes a locale – pass the lines of the language module (`localeEmojis(de.emojiPicker.emojis)`).
10 changes: 10 additions & 0 deletions .changeset/reexport-recipe-types.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
'@wirecore/wirelayer-vue': minor
'@wirecore/wirelayer-react': minor
'@wirecore/wirelayer-native': minor
---

The types that appear in the props are exported by the component packages themselves, so apps no longer need
`@wirecore/wirelayer-recipes` (or `NonNullable<BadgeProps['variant']>`) for them – e.g. `BadgeVariant`,
`ButtonVariant`, `ButtonSize`, `AlertStatus`, `DialogPosition`, `OverlaySize`, `StatusDotStatus`, `DataSource`,
`RecipeClasses`. `/data-table` adds `PivotValue`, `/virtual` `ItemAccessor`, React Native's `/pdf` `PdfZoom`.
7 changes: 7 additions & 0 deletions .changeset/vue-bind-input-types.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@wirecore/wirelayer-vue': patch
---

`bindInput` from `/form` fits `Input` and `Textarea` in compiled SFC templates: `onUpdate:modelValue` accepts
`string | undefined` (what the components emit) and `modelValue` falls back to `''`, like the React variant. Before,
`<Input v-bind="bindInput(field)" />` was a vue-tsc error.
30 changes: 16 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,36 +73,38 @@ All components and props: Storybook and `llms.txt`, or the MCP server `@wirecore
What a large library needs lives in its own subpaths – it only ends up in the bundle when the app imports it, and then
needs the respective peer dependency:

| Subpath | Vue / React | React Native (`@wirecore/wirelayer-native/…`) |
| ----------------- | --------------------------------------- | ---------------------------------------------------- |
| `/form` | TanStack Form, validators, FormList | TanStack Form |
| `/data-table` | DataTable (TanStack Table + Virtual) | DataTable without TanStack (table or cards) |
| `/virtual` | VirtualSelect, VirtualList, VirtualTree | – |
| `/chart` | Charts on Chart.js | Charts on react-native-svg |
| `/editor` | Rich text editor on Tiptap | – |
| `/code` | CodeHighlight on Shiki (`shiki/core`) | CodeHighlight on Shiki (JavaScript RegExp engine) |
| `/pdf` | PdfViewer on pdf.js (`pdfjs-dist`) | PdfViewer on react-native-pdf (development build) |
| `/locales/<code>` | Language modules (see below) | Language modules |
| `/expo-*` | – | `expo-sharing`, `expo-clipboard`, file/image pickers |
| Subpath | Vue / React | React Native (`@wirecore/wirelayer-native/…`) |
| ----------------- | ------------------------------------------------------------------ | ---------------------------------------------------- |
| `/form` | TanStack Form, validators, FormList | TanStack Form |
| `/data-table` | DataTable (TanStack Table) | DataTable without TanStack (table or cards) |
| `/virtual` | VirtualSelect, VirtualList, VirtualTree, `virtualRows` (DataTable) | – |
| `/chart` | Charts on Chart.js | Charts on react-native-svg |
| `/editor` | Rich text editor on Tiptap | – |
| `/code` | CodeHighlight on Shiki (`shiki/core`) | CodeHighlight on Shiki (JavaScript RegExp engine) |
| `/pdf` | PdfViewer on pdf.js (`pdfjs-dist`) | PdfViewer on react-native-pdf (development build) |
| `/locales/<code>` | Language modules (see below) | Language modules |
| `/expo-*` | – | `expo-sharing`, `expo-clipboard`, file/image pickers |

## Languages

English (default) and German are built in. More languages are available as separate modules and only end up in the
bundle when an app imports them: `fr`, `es`, `it`, `pt` (Portugal), `pt-BR`, `nl`, `pl`, `cs`, `hu`, `lv`, `ar` and
English (default) is built in. More languages are available as separate modules and only end up in the bundle when an
app imports them: `de`, `fr`, `es`, `it`, `pt` (Portugal), `pt-BR`, `nl`, `pl`, `cs`, `hu`, `lv`, `ar` and
`tr` – each under `@wirecore/wirelayer-vue/locales/<code>`, `@wirecore/wirelayer-react/locales/<code>`,
`@wirecore/wirelayer-native/locales/<code>` (or `@wirecore/wirelayer-recipes/locales/<code>`).

```ts
import { registerLocale } from '@wirecore/wirelayer-vue'; // or @wirecore/wirelayer-react, @wirecore/wirelayer-native
import { de } from '@wirecore/wirelayer-vue/locales/de';
import { fr } from '@wirecore/wirelayer-vue/locales/fr';
import { ptBR } from '@wirecore/wirelayer-vue/locales/pt-BR';

// Once at startup, before the first render
registerLocale('de', de); // applies to de-DE, de-AT, de-CH …
registerLocale('fr', fr); // applies to fr-FR, fr-CA, fr-CH …
registerLocale('pt-BR', ptBR); // a region takes precedence over its language
```

After that, `<LocaleProvider locale="fr-CA">` is enough (`<LocaleProvider locale="de-DE">` for the built-in German).
After that, `<LocaleProvider locale="de-DE">` or `<LocaleProvider locale="fr-CA">` is enough.
Alternatively, `LocaleProvider` receives the texts directly (`translations={fr}`). Unregistered languages fall back to
English; the writing direction (`ar`, `he`, `fa`, `ur` → right to left) follows from the locale. Custom languages are a
complete `Translations` object – the TypeScript type reports missing texts. The bundled translations should be reviewed
Expand Down
5 changes: 5 additions & 0 deletions apps/next/app/shell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,22 @@ import {
AppShell,
ConfirmDialog,
LocaleProvider,
registerLocale,
SegmentedControl,
SegmentedControlItem,
SidebarNav,
Toaster,
} from '@wirecore/wirelayer-react';
import { de } from '@wirecore/wirelayer-react/locales/de';
import { Gauge, Send } from 'lucide-react';
import Link from 'next/link';
import { usePathname, useRouter } from 'next/navigation';
import { createContext, useContext, useState, type ReactNode } from 'react';
import { isDemoLocale, texts, type DemoLocale } from './texts';

// German is a separate language module – registered once, before the first render (server and browser)
registerLocale('de', de);

const TextsContext = createContext(texts['en-US']);

/** Texts of the page in the chosen language */
Expand Down
8 changes: 8 additions & 0 deletions apps/nuxt/app/plugins/locale.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { registerLocale } from '@wirecore/wirelayer-vue';
import { de } from '@wirecore/wirelayer-vue/locales/de';
import { defineNuxtPlugin } from '#imports';

// German is a separate language module – registered once, before the first render (server and browser)
export default defineNuxtPlugin(() => {
registerLocale('de', de);
});
2 changes: 1 addition & 1 deletion apps/storybook/shared/locale.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ export const localeGlobalTypes = {
icon: 'globe',
items: [
{ value: 'en-US', title: 'English' },
{ value: 'de-DE', title: 'Deutsch' },
// Not built in – each preview registers them (registerLocale) from its package's locales/<code>
{ value: 'de-DE', title: 'Deutsch' },
{ value: 'fr-FR', title: 'Français' },
{ value: 'es-ES', title: 'Español' },
{ value: 'it-IT', title: 'Italiano' },
Expand Down
2 changes: 1 addition & 1 deletion apps/storybook/src/Accessibility.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ and all of it is checked on every test run.
- Render clickable cards with `asChild` as `<a>` or `<button>`, never as a clickable `<div>`.
- Add router links (RouterLink, Next.js Link) to the breadcrumb through `BreadcrumbItem` with `asChild`.
- If a `StatusDot` changes live, give the element `role="status"` so that the change is announced.
- Default texts (`Loading`, `Close`) come from the `LocaleProvider` (English without a provider, German with `locale="de-DE"`); single texts can be overridden through props like `label` / `dismissLabel`.
- Default texts (`Loading`, `Close`) come from the `LocaleProvider` (English without a provider, e.g. German with `locale="de-DE"` after `registerLocale('de', de)`); single texts can be overridden through props like `label` / `dismissLabel`.
- Set `lang` and `dir` on the `<html>` element – screen readers choose the pronunciation from it, browsers the writing direction.
- Set the focus sensibly after an `Alert` is closed.

Expand Down
47 changes: 39 additions & 8 deletions apps/storybook/src/Introduction.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,10 @@ Light/dark is switched by the `data-mode` attribute, the color theme by the `dat
<html data-mode="dark" data-theme="ocean"></html>
```

Without attributes, the light theme “WireLayer” (`data-theme="wirecore"`) applies. With Tailwind you can style
specifically with `dark:`. More under _Foundations › Themes_.
Without `data-mode` the page follows the system setting (`prefers-color-scheme`) – light or dark, without any
JavaScript. `data-mode="light"` or `data-mode="dark"` forces a mode, also for a part of the page. Without `data-theme`
the theme “WireLayer” (`data-theme="wirecore"`) applies. With Tailwind you can style specifically with `dark:` – it
follows the same rules. More under _Foundations › Themes_.

## Customizing: defaults and classes per part

Expand Down Expand Up @@ -195,6 +197,33 @@ import { DataTable } from '@wirecore/wirelayer-react/data-table';
</DataTable>
```

## DataTable feature modules

Parts of the DataTable that bring a lot of code along are imported on their own – an app only bundles the ones it passes
in `features` (fixed for the life of a table). The DatePicker alone is about 40 KB (gzip).

| Feature | Import | Adds | Without it |
| ------------- | ---------------------------------------------------------------------- | ---------------------------------------------------- | -------------------------------------------- |
| `virtualRows` | `@wirecore/wirelayer-react/virtual` (peer dependency TanStack Virtual) | only the visible rows are rendered with `virtual` | all rows are rendered, a hint in development |
| `dateInput` | `@wirecore/wirelayer-react/data-table` | DatePicker for `date` and `datetime` filters/editors | the browser's date input |
| `numberInput` | `@wirecore/wirelayer-react/data-table` | NumberInput for `number` filters and editors | the browser's number input |

```tsx
import { DataTable, dateInput, numberInput } from '@wirecore/wirelayer-react/data-table';
import { virtualRows } from '@wirecore/wirelayer-react/virtual';

// Once for all tables of the app
const features = [virtualRows, dateInput, numberInput];

<DataTable data={rows} columns={columns} features={features} scrollHeight="480px" virtual />;
```

```vue
<DataTable :data="rows" :columns="columns" :features="features" scroll-height="480px" virtual />
```

Vue: the same imports from `@wirecore/wirelayer-vue/data-table` and `@wirecore/wirelayer-vue/virtual`.

## AI assistants (MCP, llms.txt)

So that Claude, Cursor and others use the components correctly, the API is available as text and as an MCP server:
Expand All @@ -216,11 +245,11 @@ up to date.
## Language and writing direction

By default the components speak English (`en-US`). `LocaleProvider` switches texts, number and date formats and the
writing direction for everything below it – English and German are built in; other languages fall back to English,
are registered (see below) or get their texts through `translations`:
writing direction for everything below it – English is built in; other languages are registered once (see below),
get their texts through `translations` or fall back to English:

```vue
<LocaleProvider locale="de-DE">
<LocaleProvider locale="de-DE"><!-- after registerLocale('de', de) -->
<App />
</LocaleProvider>
```
Expand All @@ -236,20 +265,22 @@ import { messages } from '@wirecore/wirelayer-recipes';
The app sets `lang` and `dir` on the `<html>` element itself. For languages like Arabic or Hebrew (`dir="rtl"`),
spacing, borders, directional icons and the arrow keys are mirrored. Your own components read the texts with
`useTranslations()` and the locale and direction with `useLocale()`. In Storybook the **Language** toolbar item
switches between all built-in and registered languages (Arabic also shows the right-to-left layout).
switches between English and the registered languages (Arabic also shows the right-to-left layout).

### Registering languages

French, Spanish, Italian, Portuguese (Portugal and Brazil), Dutch, Polish, Czech, Hungarian, Latvian, Arabic and
Turkish ship as separate modules – they cost nothing as long as an app does not import them. Registered once at
German, French, Spanish, Italian, Portuguese (Portugal and Brazil), Dutch, Polish, Czech, Hungarian, Latvian, Arabic
and Turkish ship as separate modules – they cost nothing as long as an app does not import them. Registered once at
startup, they apply to every region of the language (`fr` also for `fr-CA`):

```ts
import { registerLocale } from '@wirecore/wirelayer-vue'; // likewise @wirecore/wirelayer-react and @wirecore/wirelayer-native
import { ar } from '@wirecore/wirelayer-vue/locales/ar';
import { de } from '@wirecore/wirelayer-vue/locales/de';
import { fr } from '@wirecore/wirelayer-vue/locales/fr';
import { ptBR } from '@wirecore/wirelayer-vue/locales/pt-BR';

registerLocale('de', de); // de-DE, de-AT, de-CH …
registerLocale('fr', fr);
registerLocale('ar', ar); // ar-EG, ar-MA … – right to left
registerLocale('pt-BR', ptBR); // a region takes precedence over its language (pt)
Expand Down
5 changes: 3 additions & 2 deletions apps/storybook/src/Themes.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ Light/dark is independent of it and combines with every theme.
<html data-theme="ocean" data-mode="dark"></html>
```

Both attributes also work on parts of the page. Without attributes, the theme
“WireLayer” (`data-theme="wirecore"`) applies in light mode.
Both attributes also work on parts of the page. Without `data-mode` the page follows the system setting (light or
dark), `data-mode="light"` or `"dark"` forces a mode. Without `data-theme` the theme “WireLayer”
(`data-theme="wirecore"`) applies. Custom themes from `themeToCss` follow the system setting the same way.

<ThemeTable />

Expand Down
Loading
Loading