@exegia/corpora-ui 0.26.0 → 0.27.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.
Files changed (40) hide show
  1. package/dist-lib/components/icons/__tests__/icons.test.d.ts +1 -0
  2. package/dist-lib/components/icons/file-badge-cfm.d.ts +7 -0
  3. package/dist-lib/components/icons/file-badge-corpus.d.ts +7 -0
  4. package/dist-lib/components/icons/file-badge-pdf.d.ts +7 -0
  5. package/dist-lib/components/icons/file-badge-tei.d.ts +7 -0
  6. package/dist-lib/components/icons/file-badge-tf.d.ts +7 -0
  7. package/dist-lib/components/icons/file-badge-txt.d.ts +7 -0
  8. package/dist-lib/components/icons/file-badge-xml.d.ts +7 -0
  9. package/dist-lib/components/icons/file-wordmark-cfm.d.ts +7 -0
  10. package/dist-lib/components/icons/file-wordmark-corpus.d.ts +7 -0
  11. package/dist-lib/components/icons/file-wordmark-pdf.d.ts +7 -0
  12. package/dist-lib/components/icons/file-wordmark-tei.d.ts +7 -0
  13. package/dist-lib/components/icons/file-wordmark-tf.d.ts +7 -0
  14. package/dist-lib/components/icons/file-wordmark-txt.d.ts +7 -0
  15. package/dist-lib/components/icons/file-wordmark-xml.d.ts +7 -0
  16. package/dist-lib/components/icons/index.d.ts +15 -0
  17. package/dist-lib/components/icons/types.d.ts +7 -0
  18. package/dist-lib/index.d.ts +1 -0
  19. package/dist-lib/index.js +8201 -995
  20. package/dist-lib/index.js.map +1 -1
  21. package/package.json +1 -1
  22. package/src/components/icons/README.md +91 -0
  23. package/src/components/icons/__tests__/icons.test.tsx +58 -0
  24. package/src/components/icons/file-badge-cfm.tsx +615 -0
  25. package/src/components/icons/file-badge-corpus.tsx +604 -0
  26. package/src/components/icons/file-badge-pdf.tsx +570 -0
  27. package/src/components/icons/file-badge-tei.tsx +524 -0
  28. package/src/components/icons/file-badge-tf.tsx +620 -0
  29. package/src/components/icons/file-badge-txt.tsx +607 -0
  30. package/src/components/icons/file-badge-xml.tsx +576 -0
  31. package/src/components/icons/file-wordmark-cfm.tsx +424 -0
  32. package/src/components/icons/file-wordmark-corpus.tsx +454 -0
  33. package/src/components/icons/file-wordmark-pdf.tsx +470 -0
  34. package/src/components/icons/file-wordmark-tei.tsx +424 -0
  35. package/src/components/icons/file-wordmark-tf.tsx +406 -0
  36. package/src/components/icons/file-wordmark-txt.tsx +421 -0
  37. package/src/components/icons/file-wordmark-xml.tsx +424 -0
  38. package/src/components/icons/index.ts +16 -0
  39. package/src/components/icons/types.ts +8 -0
  40. package/src/index.ts +3 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exegia/corpora-ui",
3
- "version": "0.26.0",
3
+ "version": "0.27.0",
4
4
  "description": "shadcn-ready React UI library for the corpora manuscript-research apps.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -0,0 +1,91 @@
1
+ # Corpora file icons
2
+
3
+ Fourteen React SVG components covering seven file formats in two visual families.
4
+ Every icon carries **both** a light and a dark artwork layer and picks one with
5
+ Tailwind's `dark` variant — no props, no context, no JavaScript.
6
+
7
+ Generated from `exegia-prod.sketch` → page `icons`, then simplified (dead defs
8
+ removed, light/dark-identical defs shared, ids stripped). Do not hand-edit the
9
+ `.tsx` files; regenerate them from Sketch instead (see
10
+ [Regenerating](#regenerating)).
11
+
12
+ ## Usage
13
+
14
+ ```tsx
15
+ import { FileBadgeTei, FileWordmarkPdf } from '@/components/icons';
16
+
17
+ <FileBadgeTei /> // 64×64, follows the app's dark class
18
+ <FileBadgeTei size={128} /> // any size
19
+ <FileWordmarkPdf title={null} /> // decorative: aria-hidden, no accessible name
20
+ <FileBadgeTei title="TEI source file" /> // custom accessible name
21
+ <FileBadgeTei className="shrink-0" /> // merged with the internal class
22
+ ```
23
+
24
+ All remaining props are forwarded to the root `<svg>`.
25
+
26
+ ## Components
27
+
28
+ | Format | Badge | Wordmark |
29
+ | --- | --- | --- |
30
+ | TEI | `FileBadgeTei` | `FileWordmarkTei` |
31
+ | XML | `FileBadgeXml` | `FileWordmarkXml` |
32
+ | TXT | `FileBadgeTxt` | `FileWordmarkTxt` |
33
+ | TF | `FileBadgeTf` | `FileWordmarkTf` |
34
+ | CFM | `FileBadgeCfm` | `FileWordmarkCfm` |
35
+ | PDF | `FileBadgePdf` | `FileWordmarkPdf` |
36
+ | Corpus | `FileBadgeCorpus` | `FileWordmarkCorpus` |
37
+
38
+ **Badge** — colour-coded pill overlapping the left edge of the sheet.
39
+ **Wordmark** — `.EXT` set along the bottom of the sheet.
40
+
41
+ ## Theming
42
+
43
+ Each icon renders two `<g data-theme-layer>` groups and shows exactly one via
44
+ Tailwind's `dark` variant: the light layer is `dark:hidden`, the dark layer
45
+ `hidden dark:inline`. Whatever drives `dark:` in the app drives the icons —
46
+ here that is `@custom-variant dark (&:is(.dark *))` in `index.css`, so the
47
+ icons follow the `.dark` class exactly like every other component, with no
48
+ inline stylesheet and no media query of their own.
49
+
50
+ To force a theme on one subtree, scope the class:
51
+
52
+ ```tsx
53
+ <div className="dark">
54
+ <FileBadgeTei /> {/* dark artwork regardless of the app theme */}
55
+ </div>
56
+ ```
57
+
58
+ Because the switch is pure CSS it is SSR-safe and cannot flash the wrong theme
59
+ on hydration. The `data-theme-layer` attributes stay on the groups as styling
60
+ and test hooks.
61
+
62
+ ## Notes
63
+
64
+ - **Text is outlined.** Labels are vector paths, not live `<text>`, so the
65
+ icons do not depend on *TikTok Sans Display* being installed.
66
+ - **IDs are namespaced** per component and per theme layer (`tei-badge-l-…`),
67
+ so gradients and filters never collide when several icons share a page.
68
+ Only referenced defs carry ids; decorative group ids were stripped.
69
+ - **Both layers ship in every component,** but defs that are identical in
70
+ both themes (sheet geometry, badge plates, label glyphs) exist once and are
71
+ referenced from both layers — a `<defs>` entry resolves regardless of which
72
+ layer it sits in, hidden or not. Theme-specific gradients and filters stay
73
+ per layer, so the two themes remain pixel-faithful to Sketch.
74
+ - **Filter regions were widened** to the SVG default (`-50% / 200%`). Sketch
75
+ exports tight regions such as `height="76.2%"` that clip real geometry in
76
+ spec-compliant renderers — the third sheet line disappears without this.
77
+
78
+ ## Regenerating
79
+
80
+ 1. In Sketch, edit the symbols on the `icons` page of `exegia-prod`.
81
+ 2. Re-export each symbol to SVG, and export each label inside an exact-size
82
+ bounding box to PDF (the PDF round-trip is what outlines the type).
83
+ 3. Run the generator, which splices the outlined labels into the SVG, namespaces
84
+ the IDs, widens the filter regions and emits the `.tsx` files.
85
+ 4. Run `bun scripts/simplify-icons.mjs` (from `react/`), which swaps the layer
86
+ switching onto Tailwind's `dark` variant, drops dead and duplicated defs,
87
+ strips unreferenced ids and default-value attributes, and verifies that
88
+ every remaining `url(#…)` / `href="#…"` reference resolves.
89
+
90
+ Colours are driven by the `file/*` swatches in the Sketch document, so a palette
91
+ change there propagates to every symbol before export.
@@ -0,0 +1,58 @@
1
+ import { describe, expect, test } from "bun:test"
2
+ import { render } from "@testing-library/react"
3
+
4
+ import * as icons from ".."
5
+
6
+ const components = Object.entries(icons).filter(
7
+ ([name]) => name.startsWith("FileBadge") || name.startsWith("FileWordmark")
8
+ ) as [string, (props: Record<string, unknown>) => React.ReactElement][]
9
+
10
+ describe("file icons", () => {
11
+ test("exports all fourteen components", () => {
12
+ expect(components).toHaveLength(14)
13
+ })
14
+
15
+ for (const [name, Icon] of components) {
16
+ test(`${name} renders both theme layers on Tailwind's dark variant`, () => {
17
+ const { container } = render(<Icon />)
18
+ const svg = container.querySelector("svg")
19
+ expect(svg?.getAttribute("width")).toBe("64")
20
+ expect(svg?.getAttribute("role")).toBe("img")
21
+
22
+ const light = svg?.querySelector('[data-theme-layer="light"]')
23
+ const dark = svg?.querySelector('[data-theme-layer="dark"]')
24
+ expect(light?.getAttribute("class")).toBe("dark:hidden")
25
+ expect(dark?.getAttribute("class")).toBe("hidden dark:inline")
26
+ // No inline stylesheet — switching belongs to the app's Tailwind build.
27
+ expect(svg?.querySelector("style")).toBeNull()
28
+ })
29
+
30
+ test(`${name}'s internal references all resolve`, () => {
31
+ const { container } = render(<Icon />)
32
+ const svg = container.querySelector("svg")!
33
+ const ids = new Set(
34
+ [...svg.querySelectorAll("[id]")].map((el) => el.getAttribute("id"))
35
+ )
36
+ const refs = [
37
+ ...[...svg.querySelectorAll("use")].map((el) =>
38
+ el.getAttribute("href")?.slice(1)
39
+ ),
40
+ ...[...svg.outerHTML.matchAll(/url\(#([^)]+)\)/g)].map((m) => m[1]),
41
+ ]
42
+ expect(refs.length).toBeGreaterThan(0)
43
+ for (const ref of refs) expect(ids.has(ref ?? "")).toBe(true)
44
+ })
45
+ }
46
+
47
+ test("title becomes the accessible name; null marks decorative", () => {
48
+ const named = render(<icons.FileBadgeTei title="TEI source" />)
49
+ expect(
50
+ named.container.querySelector("svg")?.getAttribute("aria-label")
51
+ ).toBe("TEI source")
52
+
53
+ const decorative = render(<icons.FileWordmarkPdf title={null} />)
54
+ const svg = decorative.container.querySelector("svg")
55
+ expect(svg?.getAttribute("role")).toBe("presentation")
56
+ expect(svg?.getAttribute("aria-hidden")).toBe("true")
57
+ })
58
+ })