@dumiflex/wp-kit 0.6.0 → 0.7.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/CHANGELOG.md +15 -0
- package/package.json +1 -1
- package/src/BundlePreview.vue +254 -0
- package/src/CombinePreview.vue +150 -0
- package/src/ConstraintMatrixPreview.vue +6 -22
- package/src/ConstraintPreview.vue +259 -0
- package/src/DerivationCondition.vue +68 -0
- package/src/DerivationPreview.vue +345 -0
- package/src/FixedValuesPreview.vue +184 -0
- package/src/IconPicker.vue +31 -102
- package/src/IconPickerPanel.vue +132 -0
- package/src/LoadingMark.vue +6 -61
- package/src/PayloadPreview.vue +154 -0
- package/src/PostDetailSkeleton.vue +65 -0
- package/src/PostGridSkeleton.vue +61 -0
- package/src/PreviewGroup.vue +75 -0
- package/src/ProfileSkeleton.vue +73 -0
- package/src/RichText.vue +61 -0
- package/src/SelectionBar.vue +90 -0
- package/src/SettingGroup.vue +57 -0
- package/src/SettingRow.vue +127 -0
- package/src/WildcardPreview.vue +607 -0
- package/src/WorkingLine.vue +38 -124
- package/src/canvas-motion.ts +251 -0
- package/src/kind-colour.ts +24 -1
- package/src/linkify.ts +142 -0
- package/src/loading-mark-motion.ts +12 -155
- package/src/matrix-axis.ts +28 -0
- package/src/pi-icons.ts +51 -0
- package/src/preview-readers.ts +401 -0
- package/src/preview.css +194 -0
- package/src/tokens.ts +8 -2
- package/src/working-line-motion.ts +135 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.7.0](https://github.com/DumiFlex/wp-kit/compare/v0.6.0...v0.7.0) (2026-11-03)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* IconPicker all (every PrimeIcons glyph, searchable), inline, color and allow-none ([#70](https://github.com/DumiFlex/wp-kit/issues/70)) ([a0dc837](https://github.com/DumiFlex/wp-kit/commit/a0dc83716441c8061428e69faf751ab7b52ed8ae))
|
|
9
|
+
* RichText, the module payload previews and the post loading shapes move into the kit ([#71](https://github.com/DumiFlex/wp-kit/issues/71)) ([b92d62f](https://github.com/DumiFlex/wp-kit/commit/b92d62f87ecd44cb205d2265d7d56c9018e29144))
|
|
10
|
+
* SelectionBar, the one bulk-action bar over a list of ticked rows ([#69](https://github.com/DumiFlex/wp-kit/issues/69)) ([13a51e2](https://github.com/DumiFlex/wp-kit/commit/13a51e22b0e27b404700dc6f3fb2f34fd912d10a))
|
|
11
|
+
* SettingGroup and SettingRow, the settings page rows both apps use ([#68](https://github.com/DumiFlex/wp-kit/issues/68)) ([20f4ff8](https://github.com/DumiFlex/wp-kit/commit/20f4ff8a8c28da63fcabfd9f2c0c2bf28a4f60dd))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### Bug Fixes
|
|
15
|
+
|
|
16
|
+
* draw WorkingLine's marks on a canvas so they glide in Firefox ([#67](https://github.com/DumiFlex/wp-kit/issues/67)) ([6565f15](https://github.com/DumiFlex/wp-kit/commit/6565f1575ecb224ad9e94efdef611f5c31b12849))
|
|
17
|
+
|
|
3
18
|
## [0.6.0](https://github.com/DumiFlex/wp-kit/compare/v0.5.0...v0.6.0) (2026-11-02)
|
|
4
19
|
|
|
5
20
|
|
package/package.json
CHANGED
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* Bundle: what the bundle needs from outside vs what it sets (folded from
|
|
4
|
+
* its children), then a child list beside the selected child's own full
|
|
5
|
+
* preview. Narrow hosts stack the list above the preview.
|
|
6
|
+
*/
|
|
7
|
+
import { computed, ref } from "vue";
|
|
8
|
+
import PayloadPreview from "./PayloadPreview.vue";
|
|
9
|
+
import ListRow from "./ListRow.vue";
|
|
10
|
+
import Chip from "./Chip.vue";
|
|
11
|
+
import { itemType, kindColour, kindIcon, moduleSubtype } from "./kind-colour";
|
|
12
|
+
import {
|
|
13
|
+
derivationRules,
|
|
14
|
+
fixedValues,
|
|
15
|
+
innerOf,
|
|
16
|
+
readArray,
|
|
17
|
+
readGroups,
|
|
18
|
+
readObject,
|
|
19
|
+
readString,
|
|
20
|
+
varFlow,
|
|
21
|
+
wildcardOptions,
|
|
22
|
+
type Obj,
|
|
23
|
+
} from "./preview-readers";
|
|
24
|
+
|
|
25
|
+
const props = defineProps<{
|
|
26
|
+
payload: Obj;
|
|
27
|
+
refNames: Record<string, string>;
|
|
28
|
+
refGroups: Record<string, Record<string, string[]>>;
|
|
29
|
+
}>();
|
|
30
|
+
|
|
31
|
+
interface Child {
|
|
32
|
+
key: string;
|
|
33
|
+
id: string;
|
|
34
|
+
name: string;
|
|
35
|
+
type: string;
|
|
36
|
+
enabled: boolean;
|
|
37
|
+
raw: Obj;
|
|
38
|
+
}
|
|
39
|
+
function childName(c: unknown): string | null {
|
|
40
|
+
const meta = readObject(c, "meta");
|
|
41
|
+
return (
|
|
42
|
+
readString(meta, "name") ??
|
|
43
|
+
readString(meta, "library_name") ??
|
|
44
|
+
readString(c, "name") ??
|
|
45
|
+
readString(readObject(c, "payload"), "var_binding")
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
const children = computed<Child[]>(() =>
|
|
49
|
+
readArray(props.payload, "children").map((c, i) => ({
|
|
50
|
+
key: readString(c, "id") ?? String(i),
|
|
51
|
+
id: readString(c, "id") ?? "",
|
|
52
|
+
name: childName(c) ?? "—",
|
|
53
|
+
type: readString(c, "type") ?? "—",
|
|
54
|
+
enabled: (c as Obj | null)?.["enabled"] !== false,
|
|
55
|
+
raw: (c && typeof c === "object" ? c : {}) as Obj,
|
|
56
|
+
})),
|
|
57
|
+
);
|
|
58
|
+
/** Sibling names + wildcard axes so a child constraint resolves its
|
|
59
|
+
* wildcards the same way the list does. */
|
|
60
|
+
const childRefNames = computed(() => {
|
|
61
|
+
const out: Record<string, string> = { ...props.refNames };
|
|
62
|
+
for (const c of children.value) if (c.id) out[c.id] = c.name;
|
|
63
|
+
return out;
|
|
64
|
+
});
|
|
65
|
+
const childRefGroups = computed(() => {
|
|
66
|
+
const out: Record<string, Record<string, string[]>> = { ...props.refGroups };
|
|
67
|
+
for (const c of children.value) {
|
|
68
|
+
const g = readGroups(innerOf(c.raw), "tag_groups");
|
|
69
|
+
if (c.id && g) out[c.id] = g;
|
|
70
|
+
}
|
|
71
|
+
return out;
|
|
72
|
+
});
|
|
73
|
+
function summary(c: Child): string {
|
|
74
|
+
const inner = innerOf(c.raw);
|
|
75
|
+
const label = c.type.replace(/_/g, " ");
|
|
76
|
+
switch (c.type) {
|
|
77
|
+
case "wildcard": {
|
|
78
|
+
const n = wildcardOptions(inner).length;
|
|
79
|
+
const b = readString(inner, "var_binding");
|
|
80
|
+
return `${label} · ${n} option${n === 1 ? "" : "s"}${b ? ` · $${b}` : ""}`;
|
|
81
|
+
}
|
|
82
|
+
case "fixed_values": {
|
|
83
|
+
const n = fixedValues(inner).length;
|
|
84
|
+
return `${label} · ${n} value${n === 1 ? "" : "s"}`;
|
|
85
|
+
}
|
|
86
|
+
case "derivation": {
|
|
87
|
+
const n = derivationRules(inner).length;
|
|
88
|
+
return `${label} · ${n} rule${n === 1 ? "" : "s"}`;
|
|
89
|
+
}
|
|
90
|
+
case "combine": {
|
|
91
|
+
const out = readString(inner, "output_var");
|
|
92
|
+
return out ? `${label} · $${out}` : label;
|
|
93
|
+
}
|
|
94
|
+
case "constraint": {
|
|
95
|
+
const s = readString(inner, "source_wildcard_id");
|
|
96
|
+
const t = readString(inner, "target_wildcard_id");
|
|
97
|
+
const name = (id: string | null): string => (id ? (childRefNames.value[id] ?? id) : "?");
|
|
98
|
+
return `${label} · ${name(s)} → ${name(t)}`;
|
|
99
|
+
}
|
|
100
|
+
default:
|
|
101
|
+
return label;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
const flow = computed(() => varFlow(props.payload));
|
|
105
|
+
const provide = computed(() => flow.value.reads.filter((v) => !flow.value.sets.includes(v)));
|
|
106
|
+
|
|
107
|
+
const selected = ref<string | null>(null);
|
|
108
|
+
const current = computed(
|
|
109
|
+
() => children.value.find((c) => c.key === selected.value) ?? children.value[0] ?? null,
|
|
110
|
+
);
|
|
111
|
+
const counts = computed(() => {
|
|
112
|
+
const out: Record<string, number> = {};
|
|
113
|
+
for (const c of children.value) out[c.type] = (out[c.type] ?? 0) + 1;
|
|
114
|
+
return out;
|
|
115
|
+
});
|
|
116
|
+
</script>
|
|
117
|
+
|
|
118
|
+
<template>
|
|
119
|
+
<div class="wp-pv wp-bundle">
|
|
120
|
+
<div class="wp-pv-stats">
|
|
121
|
+
<div class="wp-pv-stat">
|
|
122
|
+
<b>{{ children.length }}</b><span>module{{ children.length === 1 ? "" : "s" }}</span>
|
|
123
|
+
</div>
|
|
124
|
+
<div
|
|
125
|
+
v-for="(n, t) in counts"
|
|
126
|
+
:key="t"
|
|
127
|
+
class="wp-pv-stat wp-pv-hide-narrow"
|
|
128
|
+
>
|
|
129
|
+
<b>{{ n }}</b><span>{{ String(t).replace(/_/g, " ") }}</span>
|
|
130
|
+
</div>
|
|
131
|
+
</div>
|
|
132
|
+
<div
|
|
133
|
+
v-if="provide.length || flow.sets.length"
|
|
134
|
+
class="wp-bd-flow"
|
|
135
|
+
>
|
|
136
|
+
<template v-if="provide.length">
|
|
137
|
+
<span class="wp-pv-label">You provide</span>
|
|
138
|
+
<div>
|
|
139
|
+
<code
|
|
140
|
+
v-for="v in provide"
|
|
141
|
+
:key="`p-${v}`"
|
|
142
|
+
class="wp-bd-var"
|
|
143
|
+
>${{ v }}</code>
|
|
144
|
+
<span class="wp-bd-hint">read inside, not set by any module here</span>
|
|
145
|
+
</div>
|
|
146
|
+
</template>
|
|
147
|
+
<template v-if="flow.sets.length">
|
|
148
|
+
<span class="wp-pv-label">It sets</span>
|
|
149
|
+
<div>
|
|
150
|
+
<code
|
|
151
|
+
v-for="v in flow.sets"
|
|
152
|
+
:key="`s-${v}`"
|
|
153
|
+
class="wp-bd-var"
|
|
154
|
+
>${{ v }}</code>
|
|
155
|
+
</div>
|
|
156
|
+
</template>
|
|
157
|
+
</div>
|
|
158
|
+
<div class="wp-bd-split">
|
|
159
|
+
<nav
|
|
160
|
+
class="wp-bd-kids wp-bundle-children"
|
|
161
|
+
data-wp-rows
|
|
162
|
+
>
|
|
163
|
+
<ListRow
|
|
164
|
+
v-for="c in children"
|
|
165
|
+
:key="c.key"
|
|
166
|
+
class="wp-bd-kid wp-bundle-child"
|
|
167
|
+
:selected="current?.key === c.key"
|
|
168
|
+
:icon="kindIcon(c.type)"
|
|
169
|
+
:description="`${summary(c)}${c.enabled ? '' : ' · disabled'}`"
|
|
170
|
+
:muted="!c.enabled"
|
|
171
|
+
data-test="bundle-child"
|
|
172
|
+
@click="selected = c.key"
|
|
173
|
+
>
|
|
174
|
+
{{ c.name }}
|
|
175
|
+
</ListRow>
|
|
176
|
+
</nav>
|
|
177
|
+
<div
|
|
178
|
+
v-if="current"
|
|
179
|
+
class="wp-bd-pane"
|
|
180
|
+
data-test="bundle-child-body"
|
|
181
|
+
>
|
|
182
|
+
<div class="wp-bd-pane__head">
|
|
183
|
+
<Chip v-if="moduleSubtype(current.type)" :color="kindColour(itemType('module', moduleSubtype(current.type)))">{{ itemType('module', moduleSubtype(current.type)) }}</Chip>
|
|
184
|
+
<Chip v-else>{{ current.type }}</Chip>
|
|
185
|
+
<b>{{ current.name }}</b>
|
|
186
|
+
</div>
|
|
187
|
+
<PayloadPreview
|
|
188
|
+
:payload="current.raw"
|
|
189
|
+
:ref-names="childRefNames"
|
|
190
|
+
:ref-groups="childRefGroups"
|
|
191
|
+
nested
|
|
192
|
+
/>
|
|
193
|
+
</div>
|
|
194
|
+
</div>
|
|
195
|
+
</div>
|
|
196
|
+
</template>
|
|
197
|
+
|
|
198
|
+
<style scoped src="./preview.css"></style>
|
|
199
|
+
<style scoped>
|
|
200
|
+
.wp-bd-flow {
|
|
201
|
+
display: grid;
|
|
202
|
+
grid-template-columns: auto minmax(0, 1fr);
|
|
203
|
+
gap: 6px 14px;
|
|
204
|
+
align-items: baseline;
|
|
205
|
+
padding: 12px 16px;
|
|
206
|
+
border-bottom: 1px solid var(--wp-border);
|
|
207
|
+
}
|
|
208
|
+
.wp-bd-flow > div {
|
|
209
|
+
display: flex;
|
|
210
|
+
flex-wrap: wrap;
|
|
211
|
+
gap: 6px;
|
|
212
|
+
align-items: baseline;
|
|
213
|
+
}
|
|
214
|
+
.wp-bd-var {
|
|
215
|
+
font-family: var(--wp-font-mono);
|
|
216
|
+
font-size: 12px;
|
|
217
|
+
color: var(--wp-success);
|
|
218
|
+
}
|
|
219
|
+
.wp-bd-hint {
|
|
220
|
+
font-size: 11.5px;
|
|
221
|
+
color: var(--wp-text-dim);
|
|
222
|
+
}
|
|
223
|
+
.wp-bd-split {
|
|
224
|
+
display: grid;
|
|
225
|
+
grid-template-columns: 240px minmax(0, 1fr);
|
|
226
|
+
}
|
|
227
|
+
@container (max-width: 700px) {
|
|
228
|
+
.wp-bd-split {
|
|
229
|
+
grid-template-columns: minmax(0, 1fr);
|
|
230
|
+
}
|
|
231
|
+
.wp-bd-kids {
|
|
232
|
+
border-right: 0;
|
|
233
|
+
border-bottom: 1px solid var(--wp-border);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
.wp-bd-kids {
|
|
237
|
+
display: flex;
|
|
238
|
+
flex-direction: column;
|
|
239
|
+
gap: 2px;
|
|
240
|
+
padding: 8px;
|
|
241
|
+
border-right: 1px solid var(--wp-border);
|
|
242
|
+
}
|
|
243
|
+
.wp-bd-pane {
|
|
244
|
+
min-width: 0;
|
|
245
|
+
}
|
|
246
|
+
.wp-bd-pane__head {
|
|
247
|
+
display: flex;
|
|
248
|
+
align-items: center;
|
|
249
|
+
gap: 10px;
|
|
250
|
+
padding: 12px 16px;
|
|
251
|
+
border-bottom: 1px solid var(--wp-border);
|
|
252
|
+
font-size: 13px;
|
|
253
|
+
}
|
|
254
|
+
</style>
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/** Combine: the template in its own block, its negative under it, and a
|
|
3
|
+
* list of the variables it reads with where each one is used. */
|
|
4
|
+
import { computed, ref } from "vue";
|
|
5
|
+
import TokenizedValue from "./TokenizedValue.vue";
|
|
6
|
+
import Button from "./Button.vue";
|
|
7
|
+
import { readString, readStrings, scanText, type Obj } from "./preview-readers";
|
|
8
|
+
|
|
9
|
+
const props = defineProps<{
|
|
10
|
+
inner: Obj | null;
|
|
11
|
+
refMap: ReadonlyMap<string, string>;
|
|
12
|
+
}>();
|
|
13
|
+
|
|
14
|
+
const PAGE = 6;
|
|
15
|
+
|
|
16
|
+
const template = computed(() => readString(props.inner, "template") ?? "");
|
|
17
|
+
const negative = computed(() => readString(props.inner, "negative") ?? "");
|
|
18
|
+
const output = computed(() => readString(props.inner, "output_var"));
|
|
19
|
+
const tplScan = computed(() => scanText(template.value));
|
|
20
|
+
const negScan = computed(() => scanText(negative.value));
|
|
21
|
+
|
|
22
|
+
interface InputRow {
|
|
23
|
+
name: string;
|
|
24
|
+
where: string;
|
|
25
|
+
}
|
|
26
|
+
const inputs = computed<InputRow[]>(() => {
|
|
27
|
+
const names = [
|
|
28
|
+
...new Set([
|
|
29
|
+
...readStrings(props.inner, "input_vars"),
|
|
30
|
+
...tplScan.value.vars,
|
|
31
|
+
...negScan.value.vars,
|
|
32
|
+
...negScan.value.negVars,
|
|
33
|
+
]),
|
|
34
|
+
];
|
|
35
|
+
return names.map((name) => {
|
|
36
|
+
const where: string[] = [];
|
|
37
|
+
if (tplScan.value.vars.includes(name)) where.push("template");
|
|
38
|
+
if (negScan.value.vars.includes(name)) where.push("negative");
|
|
39
|
+
if (negScan.value.negVars.includes(name) || tplScan.value.negVars.includes(name))
|
|
40
|
+
where.push("negative (.neg)");
|
|
41
|
+
return { name, where: where.join(", ") || "declared, not used" };
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
const showAll = ref(false);
|
|
45
|
+
const shown = computed(() => (showAll.value ? inputs.value : inputs.value.slice(0, PAGE)));
|
|
46
|
+
</script>
|
|
47
|
+
|
|
48
|
+
<template>
|
|
49
|
+
<div class="wp-pv">
|
|
50
|
+
<div class="wp-pv-stats">
|
|
51
|
+
<div class="wp-pv-stat">
|
|
52
|
+
<b>{{ inputs.length }}</b><span>input{{ inputs.length === 1 ? "" : "s" }}</span>
|
|
53
|
+
</div>
|
|
54
|
+
<div
|
|
55
|
+
v-if="tplScan.picks"
|
|
56
|
+
class="wp-pv-stat"
|
|
57
|
+
>
|
|
58
|
+
<b>{{ tplScan.picks }}</b><span>inline pick{{ tplScan.picks === 1 ? "" : "s" }}</span>
|
|
59
|
+
</div>
|
|
60
|
+
<div class="wp-pv-stat">
|
|
61
|
+
<b>{{ negative ? "yes" : "no" }}</b><span>negative</span>
|
|
62
|
+
</div>
|
|
63
|
+
</div>
|
|
64
|
+
<div class="wp-pv-sec">
|
|
65
|
+
Template<template v-if="output">
|
|
66
|
+
→ <span class="wp-cb__out">${{ output }}</span>
|
|
67
|
+
</template>
|
|
68
|
+
</div>
|
|
69
|
+
<div
|
|
70
|
+
class="wp-pv-block wp-combine__template"
|
|
71
|
+
data-test="combine-template"
|
|
72
|
+
>
|
|
73
|
+
<TokenizedValue
|
|
74
|
+
:value="template"
|
|
75
|
+
:uuid-to-name="refMap"
|
|
76
|
+
/>
|
|
77
|
+
</div>
|
|
78
|
+
<template v-if="negative">
|
|
79
|
+
<div class="wp-pv-sec wp-cb__neglabel">
|
|
80
|
+
Negative
|
|
81
|
+
</div>
|
|
82
|
+
<div
|
|
83
|
+
class="wp-pv-block wp-pv-block--neg"
|
|
84
|
+
data-test="combine-negative"
|
|
85
|
+
>
|
|
86
|
+
<TokenizedValue
|
|
87
|
+
:value="negative"
|
|
88
|
+
:uuid-to-name="refMap"
|
|
89
|
+
/>
|
|
90
|
+
</div>
|
|
91
|
+
</template>
|
|
92
|
+
<template v-if="inputs.length">
|
|
93
|
+
<div class="wp-pv-sec">
|
|
94
|
+
Inputs
|
|
95
|
+
</div>
|
|
96
|
+
<table class="wp-pv-table">
|
|
97
|
+
<colgroup>
|
|
98
|
+
<col style="width: 180px">
|
|
99
|
+
<col>
|
|
100
|
+
</colgroup>
|
|
101
|
+
<tbody>
|
|
102
|
+
<tr
|
|
103
|
+
v-for="i in shown"
|
|
104
|
+
:key="i.name"
|
|
105
|
+
data-test="combine-input"
|
|
106
|
+
>
|
|
107
|
+
<td class="wp-pv-var">
|
|
108
|
+
${{ i.name }}
|
|
109
|
+
</td>
|
|
110
|
+
<td :class="{ 'wp-cb__unused': i.where === 'declared, not used' }">
|
|
111
|
+
{{ i.where }}
|
|
112
|
+
</td>
|
|
113
|
+
</tr>
|
|
114
|
+
</tbody>
|
|
115
|
+
</table>
|
|
116
|
+
<div
|
|
117
|
+
v-if="inputs.length > PAGE"
|
|
118
|
+
class="wp-pv-more"
|
|
119
|
+
>
|
|
120
|
+
<Button
|
|
121
|
+
variant="link"
|
|
122
|
+
size="sm"
|
|
123
|
+
@click="showAll = !showAll"
|
|
124
|
+
>
|
|
125
|
+
{{ showAll ? "Show fewer" : `Show ${inputs.length - PAGE} more` }}
|
|
126
|
+
</Button>
|
|
127
|
+
</div>
|
|
128
|
+
</template>
|
|
129
|
+
<div class="wp-cb__pad" />
|
|
130
|
+
</div>
|
|
131
|
+
</template>
|
|
132
|
+
|
|
133
|
+
<style scoped src="./preview.css"></style>
|
|
134
|
+
<style scoped>
|
|
135
|
+
.wp-cb__out {
|
|
136
|
+
font-family: var(--wp-font-mono);
|
|
137
|
+
color: var(--wp-success);
|
|
138
|
+
text-transform: none;
|
|
139
|
+
letter-spacing: 0;
|
|
140
|
+
}
|
|
141
|
+
.wp-cb__neglabel {
|
|
142
|
+
color: var(--wp-danger-text, var(--wp-danger));
|
|
143
|
+
}
|
|
144
|
+
.wp-cb__unused {
|
|
145
|
+
color: var(--wp-text-dim);
|
|
146
|
+
}
|
|
147
|
+
.wp-cb__pad {
|
|
148
|
+
height: 6px;
|
|
149
|
+
}
|
|
150
|
+
</style>
|
|
@@ -29,8 +29,12 @@ import {
|
|
|
29
29
|
groupHue as tagHue,
|
|
30
30
|
groupLabel,
|
|
31
31
|
isBucket,
|
|
32
|
+
MODE_GLYPH,
|
|
33
|
+
MODE_LABEL,
|
|
34
|
+
normalizeMode,
|
|
32
35
|
orderByGroups,
|
|
33
36
|
toGroups,
|
|
37
|
+
type CellMode,
|
|
34
38
|
} from "./matrix-axis";
|
|
35
39
|
|
|
36
40
|
const props = defineProps<{
|
|
@@ -53,27 +57,7 @@ const colGroups = computed(() => toGroups(orderedCols.value));
|
|
|
53
57
|
const hasColBands = computed(() => colGroups.value.some((g) => !isBucket(g)));
|
|
54
58
|
const hasRowGroups = computed(() => rowGroups.value.some((g) => !isBucket(g)));
|
|
55
59
|
|
|
56
|
-
|
|
57
|
-
// rule of their own in that row are excluded.
|
|
58
|
-
type CnMode = "allow" | "boost" | "reduce" | "exclude" | "only";
|
|
59
|
-
const MODE_GLYPH: Record<string, string> = {
|
|
60
|
-
only: "✓",
|
|
61
|
-
boost: "↑",
|
|
62
|
-
reduce: "↓",
|
|
63
|
-
exclude: "×",
|
|
64
|
-
allow: "·",
|
|
65
|
-
};
|
|
66
|
-
const MODE_LABEL: Record<CnMode, string> = {
|
|
67
|
-
boost: "Boost",
|
|
68
|
-
reduce: "Reduce",
|
|
69
|
-
exclude: "Exclude",
|
|
70
|
-
allow: "Neutral",
|
|
71
|
-
only: "Only",
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
function normalizeMode(raw: string | null | undefined): CnMode {
|
|
75
|
-
return raw === "exclude" || raw === "boost" || raw === "reduce" || raw === "only" ? raw : "allow";
|
|
76
|
-
}
|
|
60
|
+
type CnMode = CellMode;
|
|
77
61
|
|
|
78
62
|
/** A row holding an `only` cell shuts out its neutral cells at runtime; the
|
|
79
63
|
* preview marks them so the link reads the same as in the extension. */
|
|
@@ -296,7 +280,7 @@ function fmtFactor(f: number): string {
|
|
|
296
280
|
class="wp-cn-exc__mode"
|
|
297
281
|
:class="`wp-cn-exc__mode--${exc.mode}`"
|
|
298
282
|
>
|
|
299
|
-
<span class="wp-cn-exc__glyph">{{ MODE_GLYPH[exc.mode] }}</span>
|
|
283
|
+
<span class="wp-cn-exc__glyph">{{ MODE_GLYPH[normalizeMode(exc.mode)] }}</span>
|
|
300
284
|
{{ MODE_LABEL[normalizeMode(exc.mode)] }}
|
|
301
285
|
</span>
|
|
302
286
|
<span
|