@kolkrabbi/kol-styleguide 0.15.0 → 0.16.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kolkrabbi/kol-styleguide",
3
- "version": "0.15.0",
3
+ "version": "0.16.0",
4
4
  "private": false,
5
5
  "description": "Brand guide components and the brand tool",
6
6
  "license": "MIT",
@@ -12,20 +12,20 @@
12
12
  "./src/*": "./src/*"
13
13
  },
14
14
  "peerDependencies": {
15
- "@kolkrabbi/kol-component": ">=0.188.0",
16
- "@kolkrabbi/kol-framework": ">=0.39.0",
15
+ "@kolkrabbi/kol-component": ">=0.315.0",
16
+ "@kolkrabbi/kol-framework": ">=0.64.0",
17
17
  "@kolkrabbi/kol-icons": ">=0.18.0",
18
18
  "@kolkrabbi/kol-print": ">=0.1.0",
19
- "@kolkrabbi/kol-theme": ">=0.53.0",
19
+ "@kolkrabbi/kol-theme": ">=0.198.0",
20
20
  "react": "^18.3.0 || ^19.0.0",
21
21
  "react-dom": "^18.3.0 || ^19.0.0"
22
22
  },
23
23
  "devDependencies": {
24
- "@kolkrabbi/kol-component": "^0.313.0",
25
- "@kolkrabbi/kol-framework": "^0.62.4",
24
+ "@kolkrabbi/kol-component": "^0.315.0",
25
+ "@kolkrabbi/kol-framework": "^0.64.0",
26
26
  "@kolkrabbi/kol-icons": "^0.52.0",
27
- "@kolkrabbi/kol-print": "^0.1.1",
28
- "@kolkrabbi/kol-theme": "^0.196.0"
27
+ "@kolkrabbi/kol-theme": "^0.198.0",
28
+ "@kolkrabbi/kol-print": "^0.1.1"
29
29
  },
30
30
  "files": [
31
31
  "src",
@@ -1,5 +1,5 @@
1
1
  import { useState } from 'react'
2
- import { FullscreenOverlay, KindPreview, PageHeader, QuickLookFrame, KIND_LABEL } from '@kolkrabbi/kol-component'
2
+ import { FileQuickLook, PageHeader, KIND_LABEL } from '@kolkrabbi/kol-component'
3
3
  import { Contents } from './BrandGuide.jsx'
4
4
 
5
5
  /* DOCUMENT INDEX — a folder's pages as the brand book's contents cards (BrandGuide's `Contents`, routed), grouped by
@@ -24,22 +24,19 @@ export default function DocumentIndex({ rows, title, lede, onNavigate }) {
24
24
  const index = rows.map((r) => (r.file
25
25
  ? { ...r, href: r.file, meta: r.meta ?? fileLabel(r), onClick: (e) => { if (e.metaKey || e.ctrlKey || e.shiftKey || e.button !== 0) return; e.preventDefault(); setOpen(files.indexOf(r)) } }
26
26
  : r))
27
- const cur = open === null ? null : files[open]
28
- const step = (d) => setOpen((i) => (i + d + files.length) % files.length)
29
27
  return (
30
28
  <div className="kol-page flex flex-col gap-6">
31
29
  {title && <PageHeader title={title} subtitle={lede} size="sm" voice="mono" />}
32
30
  <Contents index={index} onNavigate={onNavigate} />
33
- {cur && (
34
- /* QUICK LOOK DIMS, it does not black out — the list stays in view (MediaLibraryPages' rule) */
35
- <FullscreenOverlay open onClose={() => setOpen(null)} closeButton={false} scrim>
36
- <QuickLookFrame title={cur.title} meta={fileLabel(cur)} onClose={() => setOpen(null)} nav={files.length > 1 ? { index: open, total: files.length, onPrev: () => step(-1), onNext: () => step(1) } : undefined}>
37
- {cur.kind === 'image' || /^(png|jpe?g|webp|gif|svg|avif)$/i.test(cur.ext ?? '')
38
- ? <img src={cur.file} alt={cur.title} className="kol-quicklook-media" />
39
- : <KindPreview key={cur.file} o={{ key: cur.file.split('?')[0], url: cur.file }} fit="sheet" urlOf={(x) => x.url} />}
40
- </QuickLookFrame>
41
- </FullscreenOverlay>
42
- )}
31
+ {/* the DS Quick Look over the folder's files (kol-component FileQuickLook, 2026-10-29 — the composition this file drew
32
+ * by hand: dims, pages ‹ ›, a download) */}
33
+ <FileQuickLook
34
+ files={files.map((f) => ({ key: f.file.split('?')[0], name: f.title, url: f.file, type: f.kind === 'image' ? 'image/' : undefined }))}
35
+ index={open}
36
+ onIndex={setOpen}
37
+ onClose={() => setOpen(null)}
38
+ meta={(x) => fileLabel(files.find((f) => f.file === x.url))}
39
+ />
43
40
  </div>
44
41
  )
45
42
  }
@@ -0,0 +1,113 @@
1
+ import { useState } from 'react'
2
+ import { Button, FileQuickLook, Table } from '@kolkrabbi/kol-component'
3
+ import { MarkdownPage, PageSection, libraryLinks } from '@kolkrabbi/kol-framework'
4
+ import DocumentIndex from './DocumentIndex.jsx'
5
+
6
+ /* DOCUMENT LIBRARY — a `documentLibrary` (kol-framework) drawn: a sortable table per folder, the title opening the text,
7
+ * the originals opening in the DS Quick Look, the markdown downloading (DocumentLibraryAndBoardParts, kol-website
8
+ * 2026-10-29 — record's DocumentsIndex · FolderTable · Files · MdDownload, made the DS's so brand and record draw their
9
+ * documents the same way: *"I already asked for tables, and download and file preview"*).
10
+ *
11
+ * <DocumentLibrary library={lib} onNavigate={navigate} urlOf={(f) => f.url} /> every folder, a section each
12
+ * <DocumentLibrary library={lib} folder="press" … /> one folder
13
+ * <DocumentView library={lib} row={row} urlOf={…} resolveLink={…} assetUrl={…} /> the open document
14
+ *
15
+ * DocumentLibrary
16
+ * library `{ rows, folders }` from documentLibrary
17
+ * folder one folder's id; omitted, every folder
18
+ * columns the table's columns — an array of keys (`title · kind · date · lang · assets · markdown`) and/or Table column
19
+ * objects, or `(defaults) => columns` to drop, reorder or add
20
+ * view `table` (default) · `cards` (DocumentIndex's contents cards, for anyone who still wants them)
21
+ * onNavigate (to) => void — a title's click; the link is a real `href` too
22
+ * urlOf (file) => string — an original's URL (default `file.url`; record serves the bucket in prod and a mirror in dev)
23
+ *
24
+ * DocumentFiles — the Assets cell on its own: a button per original, labelled by extension, two of one kind told apart by
25
+ * language (`PDF IS`, `PDF EN`); each opens Quick Look over the row's files. `DocumentMarkdown` — the source as `<stem>.md`. */
26
+ const LANGS = ['is', 'en', 'de']
27
+ const ext = (f) => (f.key ?? f.name ?? '').split('.').pop().toUpperCase()
28
+ export const fileLabel = (files, f) => (files.filter((x) => ext(x) === ext(f)).length > 1
29
+ ? `${ext(f)} ${(f.key ?? f.name).split('/').pop().split(/[-.]/).filter((t) => LANGS.includes(t)).join(' ').toUpperCase()}`.trim()
30
+ : ext(f))
31
+ const plain = (e) => !(e.metaKey || e.ctrlKey || e.shiftKey || e.button !== 0)
32
+
33
+ /* the one Quick Look a page holds: `open(files, index)` and the overlay to render */
34
+ export function useFileQuickLook(urlOf) {
35
+ const [look, setLook] = useState(null)
36
+ const overlay = look && <FileQuickLook files={look.files} index={look.index} onIndex={(index) => setLook({ ...look, index })} onClose={() => setLook(null)} urlOf={urlOf} />
37
+ return [(files, index) => setLook({ files, index }), overlay]
38
+ }
39
+
40
+ export function DocumentFiles({ row, onOpen }) {
41
+ if (!row.files?.length) return null
42
+ return (
43
+ <div className="flex flex-wrap gap-1">
44
+ {row.files.map((f, i) => <Button key={f.key} variant="nav" size="sm" onClick={() => onOpen(row.files, i)} title={f.name ?? f.key.split('/').pop()}>{fileLabel(row.files, f)}</Button>)}
45
+ </div>
46
+ )
47
+ }
48
+
49
+ export const DocumentMarkdown = ({ row }) => (
50
+ <Button variant="nav" size="sm" iconOnly="download" href={`data:text/markdown;charset=utf-8,${encodeURIComponent(row.source ?? '')}`} download={`${row.id.split('/').pop()}.md`} aria-label="Download markdown" />
51
+ )
52
+
53
+ const DEFAULT_KEYS = ['title', 'kind', 'date', 'lang', 'assets', 'markdown']
54
+
55
+ function columnsFor({ onNavigate, onOpen }) {
56
+ return {
57
+ title: { header: 'Title', accessor: 'title', wrap: true, sortable: true, render: (r) => (
58
+ <a href={r.to} className="underline-offset-2 hover:underline" onClick={onNavigate ? (e) => { if (!plain(e)) return; e.preventDefault(); onNavigate(r.to) } : undefined}>{r.title}</a>
59
+ ) },
60
+ kind: { header: 'Kind', accessor: 'kind', sortable: true },
61
+ date: { header: 'Date', accessor: 'date', sortable: true },
62
+ lang: { header: 'Language', accessor: 'lang' },
63
+ assets: { header: 'Assets', accessor: 'files', render: (r) => <DocumentFiles row={r} onOpen={onOpen} /> },
64
+ markdown: { header: 'Markdown', accessor: 'source', render: (r) => <DocumentMarkdown row={r} /> },
65
+ }
66
+ }
67
+
68
+ export default function DocumentLibrary({ library, folder, columns, view = 'table', onNavigate, urlOf = (f) => f.url }) {
69
+ const [open, overlay] = useFileQuickLook(urlOf)
70
+ const named = columnsFor({ onNavigate, onOpen: open })
71
+ const defaults = DEFAULT_KEYS.map((k) => named[k])
72
+ const resolve = (c) => (typeof c === 'string' ? named[c] : c)
73
+ const cols = (typeof columns === 'function' ? columns(defaults) : columns ?? DEFAULT_KEYS).map(resolve).filter(Boolean)
74
+ const folders = folder != null ? library.folders.filter((f) => f.id === folder) : library.folders
75
+
76
+ if (view === 'cards') {
77
+ /* the contents cards: pages as cards in their folder's part, the originals as file cards (DocumentIndex opens them) */
78
+ const rows = folders.flatMap((f) => library.rows.filter((r) => r.folder === f.id).flatMap((r) => [
79
+ { id: r.id, to: r.to, title: r.title, label: r.kind || undefined, part: f.label },
80
+ ...(r.files ?? []).map((x) => ({ id: `${r.id}:${x.key}`, file: urlOf(x), title: x.name ?? x.key.split('/').pop(), part: f.label })),
81
+ ]))
82
+ return <DocumentIndex rows={rows} onNavigate={onNavigate} />
83
+ }
84
+ return (
85
+ <div>
86
+ {folders.map((f) => (
87
+ <PageSection key={f.id || 'root'} id={f.id || 'documents'} label={f.label}>
88
+ <Table columns={cols} rows={library.rows.filter((r) => r.folder === f.id)} width="column" variant="simple" />
89
+ </PageSection>
90
+ ))}
91
+ {overlay}
92
+ </div>
93
+ )
94
+ }
95
+
96
+ /* THE OPEN DOCUMENT: the same Assets and Markdown buttons above `MarkdownPage`, then `actions` — record's DocumentsPage.
97
+ * Every other prop goes to MarkdownPage (`resolveLink`, `assetUrl`, `toc`, `numbered`, …); `resolveLink` defaults to the
98
+ * library's own (`libraryLinks`, kol-framework) when the caller passes `library` and no resolver. */
99
+ export function DocumentView({ row, library, urlOf = (f) => f.url, actions, markdown = true, ...page }) {
100
+ const [open, overlay] = useFileQuickLook(urlOf)
101
+ if (library && !page.resolveLink) page.resolveLink = libraryLinks(library)
102
+ return (
103
+ <>
104
+ <div className="kol-page flex items-center justify-end gap-1 pb-0">
105
+ <DocumentFiles row={row} onOpen={open} />
106
+ {markdown && <DocumentMarkdown row={row} />}
107
+ {actions}
108
+ </div>
109
+ <MarkdownPage key={row.id} source={row.source} {...page} />
110
+ {overlay}
111
+ </>
112
+ )
113
+ }
package/src/index.js CHANGED
@@ -27,6 +27,7 @@ export { generatePalette, fgOn, hexToHsl, hslToHex, GENERATION_MODES } from './c
27
27
  export { default as AssetCard } from './AssetCard.jsx'
28
28
  /* a folder's pages as the brand book's contents cards (plan-2026-10-28-document W5.2) */
29
29
  export { default as DocumentIndex } from './DocumentIndex.jsx'
30
+ export { default as DocumentLibrary, DocumentView, DocumentFiles, DocumentMarkdown, useFileQuickLook } from './DocumentLibrary.jsx'
30
31
  /* print (PdfToolsFromStudio, 2026-10-28): A4 sheets for any content, and markdown to PDF · DOCX · MD on @kolkrabbi/kol-print */
31
32
  export { default as A4Pages } from './A4Pages.jsx'
32
33
  export { default as PrintStudio } from './PrintStudio.jsx'