@onioneko/boardkit-blocks 0.1.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/LICENSE +21 -0
- package/README.md +49 -0
- package/dist/chart.d.ts +19 -0
- package/dist/chart.d.ts.map +1 -0
- package/dist/chart.js +24 -0
- package/dist/chart.js.map +1 -0
- package/dist/checklist.d.ts +24 -0
- package/dist/checklist.d.ts.map +1 -0
- package/dist/checklist.js +97 -0
- package/dist/checklist.js.map +1 -0
- package/dist/form.d.ts +26 -0
- package/dist/form.d.ts.map +1 -0
- package/dist/form.js +74 -0
- package/dist/form.js.map +1 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +36 -0
- package/dist/index.js.map +1 -0
- package/dist/metric.d.ts +22 -0
- package/dist/metric.d.ts.map +1 -0
- package/dist/metric.js +34 -0
- package/dist/metric.js.map +1 -0
- package/dist/rule.d.ts +19 -0
- package/dist/rule.d.ts.map +1 -0
- package/dist/rule.js +44 -0
- package/dist/rule.js.map +1 -0
- package/dist/status.d.ts +22 -0
- package/dist/status.d.ts.map +1 -0
- package/dist/status.js +82 -0
- package/dist/status.js.map +1 -0
- package/package.json +64 -0
- package/src/chart.ts +43 -0
- package/src/checklist.ts +123 -0
- package/src/form.ts +101 -0
- package/src/index.ts +45 -0
- package/src/metric.ts +58 -0
- package/src/rule.ts +64 -0
- package/src/status.ts +103 -0
package/src/status.ts
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import type { BlockType } from "@onioneko/boardkit-core";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* starter block: status — an ordered state machine with a current value. The
|
|
5
|
+
* cross-field rule "value must be a member of states" is exactly what
|
|
6
|
+
* BlockType.validate exists for (JSON Schema cannot express it).
|
|
7
|
+
*
|
|
8
|
+
* Usage: `states` is an ordered list of allowed values; the `transition`
|
|
9
|
+
* affordance moves `value` to a new state, emitting `status.changed` with
|
|
10
|
+
* `from`/`to`. The html hook renders `<div class="status" data-state="…">`
|
|
11
|
+
* (style a state with `.status[data-state="…"]`) holding a badge and a button
|
|
12
|
+
* for every state other than the current one.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/** Attrs for the `status` block, mirroring its JSON Schema. */
|
|
16
|
+
export type StatusAttrs = {
|
|
17
|
+
readonly id: string;
|
|
18
|
+
readonly title?: string;
|
|
19
|
+
readonly states: readonly string[];
|
|
20
|
+
readonly value: string;
|
|
21
|
+
readonly note?: string;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export const statusBlock: BlockType<StatusAttrs> = {
|
|
25
|
+
type: "status",
|
|
26
|
+
schema: {
|
|
27
|
+
type: "object",
|
|
28
|
+
required: ["id", "states", "value"],
|
|
29
|
+
properties: {
|
|
30
|
+
id: { type: "string" },
|
|
31
|
+
title: { type: "string" },
|
|
32
|
+
states: { type: "array", items: { type: "string" }, minItems: 1 },
|
|
33
|
+
value: { type: "string" },
|
|
34
|
+
note: { type: "string" },
|
|
35
|
+
},
|
|
36
|
+
additionalProperties: false,
|
|
37
|
+
},
|
|
38
|
+
// Declared `attrs: unknown` on purpose, wider than `Readonly<StatusAttrs>`
|
|
39
|
+
// (a function accepting `unknown` is assignable wherever a narrower-param
|
|
40
|
+
// function is expected, so this still satisfies `BlockType<StatusAttrs>`).
|
|
41
|
+
// `validate` is called directly by hosts and tests with arbitrary/possibly
|
|
42
|
+
// malformed data (its own doc contract says "never throws" — see
|
|
43
|
+
// BlockType.validate in core), so the null/non-object case is guarded
|
|
44
|
+
// before any property access, not just trusted from the generic.
|
|
45
|
+
validate: (attrs: unknown) => {
|
|
46
|
+
if (typeof attrs !== "object" || attrs === null) return [];
|
|
47
|
+
const { value, states } = attrs as { value?: unknown; states?: unknown };
|
|
48
|
+
if (Array.isArray(states) && typeof value === "string" && !states.includes(value)) {
|
|
49
|
+
return [{ code: "E_STATUS_VALUE", message: `value "${value}" is not in states` }];
|
|
50
|
+
}
|
|
51
|
+
return [];
|
|
52
|
+
},
|
|
53
|
+
transitions: [{ attr: "value", event: "status.changed" }],
|
|
54
|
+
affordances: [
|
|
55
|
+
{
|
|
56
|
+
name: "transition",
|
|
57
|
+
params: {
|
|
58
|
+
type: "object",
|
|
59
|
+
required: ["to"],
|
|
60
|
+
properties: { to: { type: "string" } },
|
|
61
|
+
additionalProperties: false,
|
|
62
|
+
},
|
|
63
|
+
patch: (_attrs, params) => ({ value: (params as { to: string }).to }),
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
project: {
|
|
67
|
+
text: (attrs) => {
|
|
68
|
+
const value = String(attrs.value);
|
|
69
|
+
const title = String(attrs.title ?? "").trim();
|
|
70
|
+
return title === "" ? `**STATUS**: ${value}` : `**STATUS**: ${value} — ${title}`;
|
|
71
|
+
},
|
|
72
|
+
html: (attrs): import("hast").Nodes => {
|
|
73
|
+
const states = attrs.states;
|
|
74
|
+
const value = String(attrs.value);
|
|
75
|
+
const buttons: import("hast").ElementContent[] = states
|
|
76
|
+
.filter((state) => state !== value)
|
|
77
|
+
.map((state): import("hast").ElementContent => ({
|
|
78
|
+
type: "element",
|
|
79
|
+
tagName: "button",
|
|
80
|
+
properties: {
|
|
81
|
+
"data-intent": JSON.stringify({ affordance: "transition", params: { to: state } }),
|
|
82
|
+
},
|
|
83
|
+
children: [{ type: "text", value: state }],
|
|
84
|
+
}));
|
|
85
|
+
return {
|
|
86
|
+
type: "element",
|
|
87
|
+
tagName: "div",
|
|
88
|
+
// The state is data, not styling: one attribute holds it whatever it
|
|
89
|
+
// contains, and the class list stays fixed.
|
|
90
|
+
properties: { className: ["status"], "data-state": value },
|
|
91
|
+
children: [
|
|
92
|
+
{
|
|
93
|
+
type: "element",
|
|
94
|
+
tagName: "span",
|
|
95
|
+
properties: { className: ["badge"] },
|
|
96
|
+
children: [{ type: "text", value }],
|
|
97
|
+
},
|
|
98
|
+
...buttons,
|
|
99
|
+
],
|
|
100
|
+
};
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
};
|