@keyfunc/keybricks-ds 2.4.0 → 2.4.2

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/DEPLOY.md CHANGED
@@ -55,7 +55,7 @@ deploys the documentation site. Watch it under *Build → Pipelines*.
55
55
  **5. Cut the first release.**
56
56
 
57
57
  ```bash
58
- git tag v2.4.0 && git push --tags
58
+ git tag v2.4.2 && git push --tags
59
59
  ```
60
60
 
61
61
  That tag runs `generated:check` → `version:check` → `npm publish`. Confirm under *Deploy →
@@ -112,7 +112,7 @@ import tokens from '@keyfunc/keybricks-ds/tokens.json' // cross-
112
112
  ```
113
113
 
114
114
  Deep imports are the better default in Vue and in any app that cares about tree-shaking —
115
- a barrel of 129 exports asks the bundler to prove each one is unused.
115
+ a barrel of 130 exports asks the bundler to prove each one is unused.
116
116
 
117
117
  ## 3. Publishing (maintainers)
118
118
 
@@ -162,7 +162,7 @@ GitLab injects `CI_JOB_TOKEN` — use that instead and no secret is stored anywh
162
162
  **Install — straight from git** (no registry, no publish; works today)
163
163
 
164
164
  ```json
165
- "@keyfunc/keybricks-ds": "git+ssh://git@gitlab.epm2demo.cn/Felix/keybricks-ds.git#v2.4.0"
165
+ "@keyfunc/keybricks-ds": "git+ssh://git@gitlab.epm2demo.cn/Felix/keybricks-ds.git#v2.4.2"
166
166
  ```
167
167
 
168
168
  Because the package has no build step, a git dependency needs no `prepare` script — npm
@@ -212,7 +212,7 @@ semantic layer. That is what makes dark mode cost nothing.
212
212
  ## Runtime checks
213
213
 
214
214
  `_tests/` holds browser pages that exercise the real components: `react-runtime.html` (the compiled
215
- bundle), `table-selection.html` (Table loaded from source: checkbox spacing at every width, code chips, two-line rows, sorting), `vue-runtime.html` (the `.vue` files, loaded with vue3-sfc-loader), `cards-interaction*.html`
215
+ bundle), `table-selection.html` (Table loaded from source: checkbox spacing at every width, code chips, two-line rows, sorting), `smart-table-density.html` (SmartTable from source: default / compact rows, the Row height menu, controlled density), `relative-time-picker.html` (RelativeTimePicker from source: helpers, tiers, dialog, keyboard), `vue-runtime.html` (the `.vue` files, loaded with vue3-sfc-loader), `cards-interaction*.html`
216
216
  (clicks through the documentation demos) and `icon-rules.html` (the Keybricks glyphs against the drawing rules). Serve the repo root (`npx serve .`), open a page and read the
217
217
  console — every line starts with PASS or FAIL. Run them after changing a component in either framework.
218
218
  `measure-cards.html` is a tool, not a test: it measures each documentation page at its card width so the
package/DESIGN.md CHANGED
@@ -319,6 +319,12 @@ Where each tier belongs:
319
319
 
320
320
  - **sm** — dense data grids (`SmartTable`, `Spreadsheet` and `DataPreview` set it themselves),
321
321
  controls inside pipeline nodes, a toolbar that must fit a 32px row.
322
+ `SmartTable` adds a **row density** on top of the tier: `default` rows are the tier's `--bar-h`
323
+ (36 / 40 / 48) with `--cell-px`; `compact` rows are the tier's `--control-h` (28 / 32 / 40 — the
324
+ same as a Spreadsheet cell) with the Spreadsheet's 8px cell padding — `--cell-px` is already 8 at
325
+ sm, so there only the height changes. Header and footer rows follow; the toolbar does not. Users switch it from the toolbar's *Row height* menu (a single-choice menu); `density`
326
+ controls it, `defaultDensity` seeds it. Pills (20), checkboxes (16) and avatars (20) all centre in
327
+ a 28px row. There is no taller option: long text truncates in every density.
322
328
  - **md** — everything else: app toolbars, filter bars, tables, long forms, settings pages.
323
329
  - **lg** — dialogs (`Modal` sets it), short forms, wizards, onboarding, a page with one task.
324
330
 
@@ -455,7 +461,7 @@ The list that prevents the most rework. These are absolute.
455
461
  - **Do not show a drop indicator where the drop is impossible.** No half-row boxes on a
456
462
  container, no insert rules on a node that can't take children.
457
463
  - **Do not build a new primitive when one exists.** Check the component index in
458
- `INTEGRATION.md` first — there are 120.
464
+ `INTEGRATION.md` first — there are 121.
459
465
  - **Do not use emoji as icons.** Use `<Icon name="..." />` (330 glyphs) or `<Unicon>` (1,206).
460
466
  - **Do not invent new component variants.** If `Button` lacks the variant you want, that is a
461
467
  design-system request, not a local override.
package/INTEGRATION.md CHANGED
@@ -31,7 +31,7 @@ Not using a registry yet? A git dependency works with no publishing at all — t
31
31
  has no build step, so npm can use the checkout as-is:
32
32
 
33
33
  ```json
34
- "@keyfunc/keybricks-ds": "git+ssh://git@gitlab.epm2demo.cn/Felix/keybricks-ds.git#v2.4.0"
34
+ "@keyfunc/keybricks-ds": "git+ssh://git@gitlab.epm2demo.cn/Felix/keybricks-ds.git#v2.4.2"
35
35
  ```
36
36
 
37
37
  **Source-only package.** `.jsx` and `.vue` files are published as authored, so your
@@ -162,7 +162,7 @@ Text, weight and tracking follow the tier through `--control-text` / `--control-
162
162
  weight becomes 600 and all tracking is 0 — nothing to configure. `DESIGN.md` has the full token
163
163
  table and the rules behind it.
164
164
 
165
- ## Component index (120 components)
165
+ ## Component index (121 components)
166
166
  Import path is `@keyfunc/keybricks-ds/components/<group>/<Name>.jsx` (React) or `.vue` (Vue 3).
167
167
  Every component has a sibling `.d.ts` with its full props contract, and each group has a
168
168
  `.prompt.md` with usage notes.
@@ -220,6 +220,7 @@ import Textarea from '@keyfunc/keybricks-ds/components/forms/Textarea.vue'
220
220
  | `Slider` | Range input with a filled track, drag value bubble and optional min/max labels. |
221
221
  | `DatePicker` | Text field plus a month-grid popover. Values are `YYYY-MM-DD` strings; `locale` localises the month title, weekday initials and field text. |
222
222
  | `Dropzone` | File drop target with hover, drag-over, uploaded and error states. |
223
+ | `RelativeTimePicker` | "How long ago" filter for logs, monitoring and inboxes: a field-shaped trigger opening a small dialog with an amount input, a unit select (minute … month, optional "Any time" → `null`) and a live "From" preview. Pure helpers in `forms/relative-time.js` (`resolveRelativeTime`, `normalizeRelativeTime` …). `labels` localises every string. |
223
224
 
224
225
  ### `data/` — tables and structured values
225
226
  | Component | What it's for |
@@ -519,6 +520,14 @@ Pin a minor range and update deliberately:
519
520
  - **Tokens outside CSS.** `tokens.json` / `tokens.js` / `tokens.d.ts` gained the tier tokens
520
521
  (`--control-*`, `--bar-h`, `--group-gap`, …) — 352 light tokens in all (2.0.2 adds `--danger-soft-hover`).
521
522
 
523
+ ### 2.4.2
524
+
525
+ - **New `RelativeTimePicker`** (forms) — the relative-time filter from the product, contributed by the app team and brought onto the system rules: inline token styling (the separate `relative-time.css` is gone), `Field` labels in the dialog, `data-field` hover, `.5` disabled opacity like every other field. Unit labels may be `{ one, other }` and follow `Intl.PluralRules` of `locale` ("1 hour ago" / "2 hours ago"; a CJK label stays one string); the amount field is a plain numeric text field (`inputmode="numeric"`, no native spinners). Props unchanged: `value` / `onChange(value, date)` (Vue `v-model` + `@change`), `allowUnlimited`, `labels`, `locale`, `referenceDate`, `formatDate`, `formatValue`, `size`. Helpers in `forms/relative-time.js`. Also brought in from the 2.4.1 merge: the app team's original files (`relative-time.js`, types, prompt) are kept; only the styling moved inline.
526
+
527
+ ### 2.4.1
528
+
529
+ - **`SmartTable` row density.** `density: "default" | "compact"` (+ `defaultDensity`, `onDensityChange`; Vue `v-model:density` / `@density-change`). Default rows stay the tier's `--bar-h`; compact rows are the tier's `--control-h` (28 at sm, the Spreadsheet's cell height) with 8px cell padding, header and footer included. The toolbar gains a *Row height* single-choice menu. `rowHeight` still works and overrides density.
530
+
522
531
  ### 2.4.0
523
532
 
524
533
  - **App Chrome matches the product.** `Sidebar`: 56px brand head without a rule, `--text-md` brand, a 2px-gap list, `trailing: true` items with a chevron (they open a side panel), Collapse / Expand item (chevron-left / panel-left), pinned footer and an optional `version` line. New `tone` prop — `"recessed"` (grey rail, active item a white tile with a hairline ring; **the new default**), `"flush"` (the old white look), `"contrast"` (dark-scoped rail), `"custom"` + `color` (`SIDEBAR_COLORS` or any hex). `KEYBRICKS_NAV` is the product's nav (Home … Task Execution); new `KEYBRICKS_NAV_FOOTER` (Management, Knowledge Base) is the default footer (was Help / Settings / Dark Mode) and `KEYBRICKS_MGMT` holds the Management panel groups. To keep the old look pass `tone="flush"` and your own `footerItems`.
@@ -405,6 +405,10 @@
405
405
  "selector": "JSXOpeningElement[name.name='Radio'] > JSXAttribute[name.name='size'] > Literal[value!=/^(?:sm|md|lg)$/]",
406
406
  "message": "<Radio> size must be one of 'sm' | 'md' | 'lg'."
407
407
  },
408
+ {
409
+ "selector": "JSXOpeningElement[name.name='RelativeTimeValue'] > JSXAttribute > JSXIdentifier[name!=/^(?:amount|unit|key|ref|className|style|children)$/]",
410
+ "message": "<RelativeTimeValue> doesn't accept that prop. Declared props: amount, unit."
411
+ },
408
412
  {
409
413
  "selector": "JSXOpeningElement[name.name='Ribbon3DSeries'] > JSXAttribute > JSXIdentifier[name!=/^(?:name|values|color|key|ref|className|style|children)$/]",
410
414
  "message": "<Ribbon3DSeries> doesn't accept that prop. Declared props: name, values, color."
@@ -785,6 +789,9 @@
785
789
  "Radio": {
786
790
  "replaces": []
787
791
  },
792
+ "RelativeTimeValue": {
793
+ "replaces": []
794
+ },
788
795
  "Ribbon3DSeries": {
789
796
  "replaces": []
790
797
  },
@@ -0,0 +1,45 @@
1
+ <!-- @dsCard group="Components" viewport="700x720" name="Relative Time Picker" subtitle="Filter by how long ago · md / sm / lg · unlimited · disabled · localised labels" -->
2
+ <!DOCTYPE html>
3
+ <html><head><meta charset="utf-8"><script src="../_shared/frame-theme.js"></script><link rel="stylesheet" href="../../styles.css"><link rel="stylesheet" href="../../tokens/hover.css"><link rel="stylesheet" href="../_shared/card-kit.css">
4
+ <script src="https://unpkg.com/react@18.3.1/umd/react.development.js" integrity="sha384-hD6/rw4ppMLGNu3tX5cjIb+uRZ7UkRJ6BPkLpg4hAu/6onKUg4lLsHAs9EBPT82L" crossorigin="anonymous"></script>
5
+ <script src="https://unpkg.com/react-dom@18.3.1/umd/react-dom.development.js" integrity="sha384-u6aeetuaXnQ38mYT8rp6sbXaQe3NL9t+IBXmnYxwkUI2Hw4bsp2Wvmx4yRQF1uAm" crossorigin="anonymous"></script>
6
+ <script src="https://unpkg.com/@babel/standalone@7.29.0/babel.min.js" integrity="sha384-m08KidiNqLdpJqLq95G/LEi8Qvjl/xUYll3QILypMoQ65QorJ9Lvtp2RXYGBFj1y" crossorigin="anonymous"></script>
7
+ <script src="../../_ds_bundle.js"></script>
8
+ <script src="../_shared/card-kit.js"></script>
9
+ <script src="../_shared/view-source.js"></script>
10
+ <style>body{margin:0;background:var(--bg-surface)}</style></head>
11
+ <body><div id="root"></div>
12
+ <script type="text/babel">
13
+ const { RelativeTimePicker, Field, Button, SearchInput } = window.KeybricksDesignSystem_71231a;
14
+ const ZH = { title: "相对时间", amount: "数量", unit: "单位", preview: "起始时间", unlimited: "不限时间", Minute: "分钟前", Hour: "小时前", Day: "天前", Week: "周前", Month: "个月前" };
15
+ function Sheet() {
16
+ const [a, setA] = React.useState({ amount: 1, unit: "Hour" });
17
+ const [b, setB] = React.useState({ amount: 7, unit: "Day" });
18
+ const [c, setC] = React.useState({ amount: 0, unit: "None" });
19
+ const [z, setZ] = React.useState({ amount: 30, unit: "Minute" });
20
+ const [last, setLast] = React.useState(null);
21
+ const on = (set) => (v, d) => { set(v); setLast(d ? d.toLocaleString() : "null (any time)"); };
22
+ return <div className="kb-sheet">
23
+ <KBSec title="Trigger — md · sm · lg" note="A field-shaped trigger at the tier height; click it to open the dialog (amount + unit, live “From” preview). Omit size to inherit the region's tier.">
24
+ <KBRow k="md"><div style={{ width: 220 }}><RelativeTimePicker value={a} onChange={on(setA)} /></div></KBRow>
25
+ <KBRow k="sm"><div data-size="sm" style={{ width: 200 }}><RelativeTimePicker value={b} onChange={on(setB)} /></div></KBRow>
26
+ <KBRow k="lg"><div data-size="lg" style={{ width: 240 }}><RelativeTimePicker value={a} onChange={on(setA)} /></div></KBRow>
27
+ </KBSec>
28
+ <KBSec title="States" note="Unlimited reads “Any time” and resolves to null. allowUnlimited={false} removes that option; 0 still means unlimited for legacy filters.">
29
+ <KBRow k="unlimited"><div style={{ width: 220 }}><RelativeTimePicker value={c} onChange={on(setC)} /></div></KBRow>
30
+ <KBRow k="no unlimited"><div style={{ width: 220 }}><RelativeTimePicker value={b} onChange={on(setB)} allowUnlimited={false} /></div></KBRow>
31
+ <KBRow k="disabled"><div style={{ width: 220 }}><RelativeTimePicker value={a} disabled /></div></KBRow>
32
+ </KBSec>
33
+ <KBSec title="In a filter bar · localised" note="labels overrides every string; the dialog copies the region's tier, language and theme. onChange(value, date) only fires on edits, never on mount.">
34
+ <KBRow k="zh-CN" style={{ display: "block" }}><div lang="zh-CN" style={{ display: "flex", alignItems: "flex-end", gap: "var(--group-gap)" }}>
35
+ <Field label="时间范围" style={{ width: 200 }}><RelativeTimePicker value={z} onChange={on(setZ)} labels={ZH} locale="zh-CN" /></Field>
36
+ <Field label="关键字" style={{ width: 200 }}><SearchInput placeholder="搜索日志" /></Field>
37
+ <Button variant="outline">查询</Button>
38
+ </div></KBRow>
39
+ <div className="note">Last change resolved to: <code>{last || "—"}</code></div>
40
+ </KBSec>
41
+ </div>;
42
+ }
43
+ ReactDOM.createRoot(document.getElementById("root")).render(<Sheet />);
44
+ if (window.KBViewSource) setTimeout(function () { KBViewSource.mount("#root", [{ "name": "RelativeTimePicker.jsx", "path": "../forms/RelativeTimePicker.jsx" }, { "name": "relative-time.js", "path": "../forms/relative-time.js", "vue": "../forms/relative-time.js", "vueName": "relative-time.js" }]); }, 0);
45
+ </script></body></html>
@@ -1,4 +1,4 @@
1
- <!-- @dsCard group="Components" viewport="700x500" name="Smart Table (Database Grid)" subtitle="Notion/Airtable-style grid — typed fields · hue pills · aggregates · + New" -->
1
+ <!-- @dsCard group="Components" viewport="700x900" name="Smart Table (Database Grid)" subtitle="Default and compact row density · Notion/Airtable-style grid — typed fields · hue pills · aggregates · + New" -->
2
2
  <!DOCTYPE html>
3
3
  <html><head><meta charset="utf-8"><script src="../_shared/frame-theme.js"></script><link rel="stylesheet" href="../../styles.css"><link rel="stylesheet" href="../../tokens/hover.css"><link rel="stylesheet" href="../_shared/card-kit.css">
4
4
  <script src="https://unpkg.com/react@18.3.1/umd/react.development.js" integrity="sha384-hD6/rw4ppMLGNu3tX5cjIb+uRZ7UkRJ6BPkLpg4hAu/6onKUg4lLsHAs9EBPT82L" crossorigin="anonymous"></script>
@@ -30,6 +30,9 @@ const rows=[
30
30
  ];
31
31
  function Sheet(){return <div>
32
32
  <SmartTable fields={fields} rows={rows} title="Launch tracker"/>
33
+ <div className="hint" style={{marginBottom:20}}>Default density — rows are the tier's bar height (36px at sm). The toolbar's <b>Row height</b> menu switches between Default and Compact.</div>
34
+ <SmartTable fields={fields} rows={rows.slice(0,4)} title="Launch tracker — compact" defaultDensity="compact"/>
35
+ <div className="hint" style={{marginBottom:20}}>Compact density — rows are the tier's control height (28px at sm, the same as a Spreadsheet cell) with the Spreadsheet's 8px cell padding (the same as sm's <code>--cell-px</code>, so here only the height changes); header and footer follow. <code>density="compact"</code> controls it, <code>defaultDensity</code> just seeds it.</div>
33
36
  <div className="hint">Typed fields with per-type header icons; select/multi-select render theme-aware hue pills; hover a row number for the expand affordance; double-click text/number/date cells to edit; “+ New” appends a row; the footer aggregates per column (COUNT / SUM / AVG). Scrolls horizontally like Airtable when fields overflow.</div>
34
37
  </div>;}
35
38
  ReactDOM.createRoot(document.getElementById("root")).render(<Sheet/>);