@kolkrabbi/kol-component 0.246.0 → 0.248.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-component",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.248.0",
|
|
4
4
|
"description": "The core component library",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -26,8 +26,8 @@
|
|
|
26
26
|
"embla-carousel-react": "^8.6.0",
|
|
27
27
|
"pdfjs-dist": "^6.3.289",
|
|
28
28
|
"react-syntax-highlighter": "^16.1.1",
|
|
29
|
-
"@kolkrabbi/kol-
|
|
30
|
-
"@kolkrabbi/kol-
|
|
29
|
+
"@kolkrabbi/kol-search": "^0.3.0",
|
|
30
|
+
"@kolkrabbi/kol-markdown": "^0.1.3"
|
|
31
31
|
},
|
|
32
32
|
"peerDependencies": {
|
|
33
33
|
"@kolkrabbi/kol-icons": ">=0.22.0",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"react-dom": "^18.3.0 || ^19.0.0"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
|
-
"@kolkrabbi/kol-icons": "^0.
|
|
41
|
+
"@kolkrabbi/kol-icons": "^0.35.0"
|
|
42
42
|
},
|
|
43
43
|
"files": [
|
|
44
44
|
"src",
|
|
@@ -6,8 +6,8 @@ import ContentText from './ContentText.jsx'
|
|
|
6
6
|
* the variant has one) beside the ruled text. Box values — thumb size, gap,
|
|
7
7
|
* padding, frame — default per variant to the RULED structures from the live
|
|
8
8
|
* review (06-content-card-system.md §2 boxes): default is a bare table-like
|
|
9
|
-
* line with a 48px thumb; catalog/print are framed between-headers with
|
|
10
|
-
* thumb; article rides a 120px 16:9 thumb; work a framed 64px row; typeface a
|
|
9
|
+
* line with a 48px thumb; catalog/print are framed between-headers with a
|
|
10
|
+
* 36 square thumb when `media` is passed (none without); article rides a 120px 16:9 thumb; work a framed 64px row; typeface a
|
|
11
11
|
* framed no-thumb block.
|
|
12
12
|
*
|
|
13
13
|
* @param {string} variant file | catalog | print | article | work | typeface | roster
|
|
@@ -57,7 +57,10 @@ const BOX = {
|
|
|
57
57
|
/* catalog/print rows render AT 36px — the shipped GridCard list row is a
|
|
58
58
|
* fixed 36 and the Y padding was what pushed it past that. X padding stays;
|
|
59
59
|
* `minH` is now the whole height budget and the row centres inside it. */
|
|
60
|
-
catalog
|
|
60
|
+
/* catalog carries a thumb (2026-10-09 — user: "catalog should change its default"): the full
|
|
61
|
+
* 36 rung, square, and ONLY when `media` is passed — every catalog row that never had one keeps
|
|
62
|
+
* none, rather than growing a MISSING plate (`mediaOnly`). */
|
|
63
|
+
catalog: { thumb: 36, mediaOnly: true, ratio: '1 / 1', pad: `0 ${S3}`, gap: S3, frame: 'var(--kol-fg-04)', bg: 'var(--kol-surface-tertiary)', minH: 36, align: 'items-center', hover: 'var(--kol-oq-04)' },
|
|
61
64
|
/* ListingCard's row thumb is `bg-fg-12` bare — a tint, no border. */
|
|
62
65
|
/* article had NO hover of any kind — the only row in the family you could
|
|
63
66
|
* point at and get nothing back. It has no surface of its own, so it takes
|
|
@@ -188,7 +191,7 @@ export default function ContentRow({
|
|
|
188
191
|
* consumer listing coverless documents should not have to say it two
|
|
189
192
|
* different ways on the card and the row. `media={false}` reads the same in
|
|
190
193
|
* both; `thumb` still wins when it is passed explicitly. */
|
|
191
|
-
const thumbPx = thumb ?? (media === false ? 0 : box.thumb)
|
|
194
|
+
const thumbPx = thumb ?? (media === false || (box.mediaOnly && media == null) ? 0 : box.thumb)
|
|
192
195
|
const padY = (paddingY != null ? `${paddingY}px` : String(box.pad)).trim().split(/\s+/)[0]
|
|
193
196
|
|
|
194
197
|
/* The md: STEP is a custom property, not a Tailwind variant. Tailwind cannot
|
package/src/molecules/Modal.jsx
CHANGED
|
@@ -2,6 +2,7 @@ import { createContext, useCallback, useContext, useEffect, useRef, useState } f
|
|
|
2
2
|
import { createPortal } from 'react-dom'
|
|
3
3
|
import Button from '../atoms/Button.jsx'
|
|
4
4
|
import Input from '../atoms/Input.jsx'
|
|
5
|
+
import ToggleCheckbox from '../atoms/ToggleCheckbox.jsx'
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Modal — promise-based prompt + confirm + alert dialogs.
|
|
@@ -21,10 +22,26 @@ import Input from '../atoms/Input.jsx'
|
|
|
21
22
|
* Returned promise resolves to:
|
|
22
23
|
* - prompt → string (value) on submit, `null` on cancel
|
|
23
24
|
* - confirm → boolean: `true` on confirm, `false` on cancel
|
|
25
|
+
* - confirm with `options` → `{ ok, values: { [id]: boolean } }` (below)
|
|
24
26
|
* - alert → undefined, once dismissed. One button; `okLabel` names it.
|
|
25
27
|
* Added 2026-09-22 (media-pages-route-dialogs-through-usemodal) so an
|
|
26
28
|
* error report needs no native `alert()` beside DS prompts.
|
|
27
29
|
*
|
|
30
|
+
* TOGGLES (upload-dialog-optimise-and-keep-originals, kol-website 2026-10-09): `confirm` takes
|
|
31
|
+
* `options: [{ id, label, hint?, defaultValue? }]`, drawn as `ToggleCheckbox` rows between the
|
|
32
|
+
* title and the buttons, so one dialog asks a question and its settings:
|
|
33
|
+
*
|
|
34
|
+
* const { ok, values } = await confirm('Web-optimise raster images?', {
|
|
35
|
+
* okLabel: 'Upload',
|
|
36
|
+
* options: [
|
|
37
|
+
* { id: 'optimise', label: 'Optimise', hint: '≤2560 px, ≤500 KB', defaultValue: true },
|
|
38
|
+
* { id: 'keepOriginals', label: 'Keep originals', hint: 'original/<name>', defaultValue: true },
|
|
39
|
+
* ],
|
|
40
|
+
* })
|
|
41
|
+
*
|
|
42
|
+
* `values` comes back on cancel too (the toggles as left), so a consumer can remember them either
|
|
43
|
+
* way. Without `options`, confirm still resolves a plain boolean — existing callers untouched.
|
|
44
|
+
*
|
|
28
45
|
* Mounted once at the app root (BrandLayout). Renders into `document.body`
|
|
29
46
|
* via portal, so it floats above any rail / scroll-context.
|
|
30
47
|
*/
|
|
@@ -45,8 +62,8 @@ export function ModalProvider({ children }) {
|
|
|
45
62
|
new Promise((resolve) => setState({ kind: 'prompt', title, defaultValue, okLabel, cancelLabel, resolve })),
|
|
46
63
|
[])
|
|
47
64
|
|
|
48
|
-
const confirm = useCallback((title, { okLabel, cancelLabel } = {}) =>
|
|
49
|
-
new Promise((resolve) => setState({ kind: 'confirm', title, okLabel, cancelLabel, resolve })),
|
|
65
|
+
const confirm = useCallback((title, { okLabel, cancelLabel, options } = {}) =>
|
|
66
|
+
new Promise((resolve) => setState({ kind: 'confirm', title, okLabel, cancelLabel, options: options?.length ? options : null, resolve })),
|
|
50
67
|
[])
|
|
51
68
|
|
|
52
69
|
const alert = useCallback((title, { okLabel } = {}) =>
|
|
@@ -66,11 +83,14 @@ export function ModalProvider({ children }) {
|
|
|
66
83
|
|
|
67
84
|
function ModalView({ state, closeWith }) {
|
|
68
85
|
const [val, setVal] = useState(state.defaultValue ?? '')
|
|
86
|
+
const [values, setValues] = useState(() => Object.fromEntries((state.options ?? []).map((o) => [o.id, !!o.defaultValue])))
|
|
69
87
|
const inputRef = useRef(null)
|
|
70
88
|
|
|
71
|
-
/* alert resolves `undefined` whichever way it is dismissed
|
|
72
|
-
|
|
73
|
-
const
|
|
89
|
+
/* alert resolves `undefined` whichever way it is dismissed; a confirm with toggles resolves
|
|
90
|
+
* `{ ok, values }` */
|
|
91
|
+
const answer = (ok) => (state.options ? { ok, values } : ok)
|
|
92
|
+
const submit = () => closeWith(state.kind === 'prompt' ? val : state.kind === 'alert' ? undefined : answer(true))
|
|
93
|
+
const cancel = () => closeWith(state.kind === 'prompt' ? null : state.kind === 'alert' ? undefined : answer(false))
|
|
74
94
|
|
|
75
95
|
useEffect(() => {
|
|
76
96
|
if (state.kind === 'prompt') inputRef.current?.focus()
|
|
@@ -80,7 +100,7 @@ function ModalView({ state, closeWith }) {
|
|
|
80
100
|
}
|
|
81
101
|
window.addEventListener('keydown', onKey)
|
|
82
102
|
return () => window.removeEventListener('keydown', onKey)
|
|
83
|
-
}, [state.kind, val, closeWith]) // eslint-disable-line react-hooks/exhaustive-deps
|
|
103
|
+
}, [state.kind, val, values, closeWith]) // eslint-disable-line react-hooks/exhaustive-deps
|
|
84
104
|
|
|
85
105
|
return (
|
|
86
106
|
<div
|
|
@@ -118,6 +138,19 @@ function ModalView({ state, closeWith }) {
|
|
|
118
138
|
className="w-full"
|
|
119
139
|
/>
|
|
120
140
|
)}
|
|
141
|
+
{state.options && (
|
|
142
|
+
<div className="flex flex-col gap-3">
|
|
143
|
+
{state.options.map((o) => (
|
|
144
|
+
<ToggleCheckbox
|
|
145
|
+
key={o.id}
|
|
146
|
+
label={o.label}
|
|
147
|
+
hint={o.hint}
|
|
148
|
+
checked={values[o.id]}
|
|
149
|
+
onChange={(next) => setValues((v) => ({ ...v, [o.id]: next }))}
|
|
150
|
+
/>
|
|
151
|
+
))}
|
|
152
|
+
</div>
|
|
153
|
+
)}
|
|
121
154
|
<div className="flex gap-2 justify-end">
|
|
122
155
|
{state.kind !== 'alert' && <Button size="sm" onClick={cancel}>{state.cancelLabel ?? 'Cancel'}</Button>}
|
|
123
156
|
<Button tone="primary" size="sm" onClick={submit}>{state.okLabel ?? 'OK'}</Button>
|
|
@@ -147,9 +180,10 @@ export function useModal() {
|
|
|
147
180
|
const v = window.prompt(title, def)
|
|
148
181
|
return v
|
|
149
182
|
},
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
183
|
+
/* the fallback cannot draw toggles: options come back at their defaults */
|
|
184
|
+
confirm: async (title, { options } = {}) => {
|
|
185
|
+
const ok = typeof window !== 'undefined' && window.confirm(title)
|
|
186
|
+
return options?.length ? { ok, values: Object.fromEntries(options.map((o) => [o.id, !!o.defaultValue])) } : ok
|
|
153
187
|
},
|
|
154
188
|
alert: async (title) => {
|
|
155
189
|
if (typeof window !== 'undefined') window.alert(title)
|