brand-intent 0.32.0 → 0.33.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
package/parser/icons.ts
CHANGED
|
@@ -138,3 +138,10 @@ export function parseListItem(item: string): { icon?: IconRef; text: string } {
|
|
|
138
138
|
if (!m) return { text: item };
|
|
139
139
|
return { icon: parseIconRef(m[1], `list item "${item}"`), text: m[2] };
|
|
140
140
|
}
|
|
141
|
+
|
|
142
|
+
/** A `labels: above` list item: `Label: value`, split at the first colon followed by a space. */
|
|
143
|
+
export function splitLabelledItem(text: string): { label?: string; value: string } {
|
|
144
|
+
const at = text.indexOf(': ');
|
|
145
|
+
if (at <= 0) return { value: text };
|
|
146
|
+
return { label: text.slice(0, at).trim(), value: text.slice(at + 2).trim() };
|
|
147
|
+
}
|
package/parser/index.ts
CHANGED
|
@@ -34,7 +34,7 @@ export type { ParsedBrand, ParsedFontDef, ParsedTypographyDef, ParsedDividerDef,
|
|
|
34
34
|
export { parseDateWindowRanges, isInDateWindow, SAMPLE_WINDOW_MARKER } from './dateWindows';
|
|
35
35
|
export type { DateWindowRange } from './dateWindows';
|
|
36
36
|
|
|
37
|
-
export { parseIconRef, parseIconSource, resolveIconRef, parseListItem, phosphorName, PHOSPHOR_WEIGHTS } from './icons';
|
|
37
|
+
export { parseIconRef, parseIconSource, resolveIconRef, parseListItem, splitLabelledItem, phosphorName, PHOSPHOR_WEIGHTS } from './icons';
|
|
38
38
|
export type { IconRef, IconSource, ParsedIconDef, ResolvedIcon, PhosphorWeight } from './icons';
|
|
39
39
|
export type {
|
|
40
40
|
ColorMode,
|
package/parser/purposeParser.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import type { SlotId } from './types';
|
|
9
|
-
import { parseIconRef, parseListItem } from './icons';
|
|
9
|
+
import { parseIconRef, parseListItem, splitLabelledItem } from './icons';
|
|
10
10
|
import type { IconRef } from './icons';
|
|
11
11
|
import { SAMPLE_WINDOW_MARKER } from './dateWindows';
|
|
12
12
|
|
|
@@ -105,6 +105,14 @@ export interface ParsedPurposeSlot {
|
|
|
105
105
|
itemGap?: number;
|
|
106
106
|
/** List layout direction: 'vertical' (default) or 'horizontal'. */
|
|
107
107
|
listDirection?: 'vertical' | 'horizontal';
|
|
108
|
+
/** Grid columns for a vertical list, filled row by row (list slots only). Default: 1 */
|
|
109
|
+
listColumns?: number;
|
|
110
|
+
/** 'above': each item is `Label: value`, the label set small above the value (list slots only). */
|
|
111
|
+
listLabels?: 'none' | 'above';
|
|
112
|
+
/** Label size relative to the item text, with `labels: above`. Default: 0.7 */
|
|
113
|
+
labelSize?: number;
|
|
114
|
+
/** Line between grid columns (list slots with `columns:` 2 or more). */
|
|
115
|
+
listDivider?: 'none' | 'hairline';
|
|
108
116
|
/** When false, this slot is locked — users cannot edit it in any mode. Default: true (editable). */
|
|
109
117
|
editable?: boolean;
|
|
110
118
|
typography: {
|
|
@@ -210,6 +218,9 @@ export function parsePurposeFile(content: string, fileName: string): ParsedPurpo
|
|
|
210
218
|
if (s.marker && s.marker !== 'icon') throw new Error(`${fileName}:${lineNum}: slot "${s.id}": iconOnly needs "marker: icon", got "${s.marker}"`);
|
|
211
219
|
s.marker = 'icon';
|
|
212
220
|
}
|
|
221
|
+
if (s.listColumns && s.listColumns > 1 && s.listDirection === 'horizontal') throw new Error(`${fileName}:${lineNum}: slot "${s.id}": columns needs a vertical list`);
|
|
222
|
+
if (s.labelSize !== undefined && s.listLabels !== 'above') throw new Error(`${fileName}:${lineNum}: slot "${s.id}": labelSize needs "labels: above"`);
|
|
223
|
+
if (s.listDivider === 'hairline' && !(s.listColumns && s.listColumns > 1)) throw new Error(`${fileName}:${lineNum}: slot "${s.id}": divider needs "columns:" 2 or more`);
|
|
213
224
|
if (s.defaultIcon && s.marker !== 'icon') throw new Error(`${fileName}:${lineNum}: slot "${s.id}": defaultIcon needs "marker: icon"`);
|
|
214
225
|
if (!s.value && s.samples) {
|
|
215
226
|
// Trailing `@name` window marker, on the sample's first line only
|
|
@@ -644,6 +655,30 @@ export function parsePurposeFile(content: string, fileName: string): ParsedPurpo
|
|
|
644
655
|
currentSlot.listDirection = d;
|
|
645
656
|
continue;
|
|
646
657
|
}
|
|
658
|
+
if (prop.startsWith('columns:')) {
|
|
659
|
+
const n = Number(prop.slice(8).trim());
|
|
660
|
+
if (!Number.isInteger(n) || n < 1 || n > 6) throw new Error(`${fileName}:${lineNum}: invalid columns "${prop.slice(8).trim()}" — must be 1 to 6`);
|
|
661
|
+
currentSlot.listColumns = n;
|
|
662
|
+
continue;
|
|
663
|
+
}
|
|
664
|
+
if (prop.startsWith('labels:')) {
|
|
665
|
+
const l = prop.slice(7).trim();
|
|
666
|
+
if (l !== 'none' && l !== 'above') throw new Error(`${fileName}:${lineNum}: invalid labels "${l}" — must be "none" or "above"`);
|
|
667
|
+
currentSlot.listLabels = l;
|
|
668
|
+
continue;
|
|
669
|
+
}
|
|
670
|
+
if (prop.startsWith('labelSize:')) {
|
|
671
|
+
const n = parseFloat(prop.slice(10).trim());
|
|
672
|
+
if (isNaN(n) || n <= 0 || n > 1) throw new Error(`${fileName}:${lineNum}: invalid labelSize "${prop.slice(10).trim()}" — must be above 0 and at most 1`);
|
|
673
|
+
currentSlot.labelSize = n;
|
|
674
|
+
continue;
|
|
675
|
+
}
|
|
676
|
+
if (prop.startsWith('divider:')) {
|
|
677
|
+
const d = prop.slice(8).trim();
|
|
678
|
+
if (d !== 'none' && d !== 'hairline') throw new Error(`${fileName}:${lineNum}: invalid list divider "${d}" — must be "none" or "hairline"`);
|
|
679
|
+
currentSlot.listDivider = d;
|
|
680
|
+
continue;
|
|
681
|
+
}
|
|
647
682
|
throw new Error(`${fileName}:${lineNum}: unknown list property "${prop}"`);
|
|
648
683
|
}
|
|
649
684
|
}
|
|
@@ -828,6 +863,7 @@ function resolveExamples(
|
|
|
828
863
|
for (const item of text.split('\n')) {
|
|
829
864
|
const { icon } = parseListItem(item);
|
|
830
865
|
if (icon && slot.marker !== 'icon') throw new Error(`${at(slotId)}: slot "${slotId}": item "${item}" names an icon, but the list has no "marker: icon"`);
|
|
866
|
+
if (slot.listLabels === 'above' && !splitLabelledItem(item).label) throw new Error(`${at(slotId)}: slot "${slotId}": item "${item}" needs a label ("Label: value") for "labels: above"`);
|
|
831
867
|
}
|
|
832
868
|
}
|
|
833
869
|
}
|
|
@@ -232,7 +232,7 @@
|
|
|
232
232
|
},
|
|
233
233
|
"list-property": {
|
|
234
234
|
"comment": "4-space indented properties under type: list blocks",
|
|
235
|
-
"match": "^\\s{4,}(marker|icons|iconOnly|iconGap|itemGap|maxItems|direction)(:)\\s+(.*)",
|
|
235
|
+
"match": "^\\s{4,}(marker|icons|iconOnly|iconGap|itemGap|maxItems|direction|columns|labels|labelSize|divider)(:)\\s+(.*)",
|
|
236
236
|
"captures": {
|
|
237
237
|
"1": { "name": "support.type.property-name.brand-intent-purpose" },
|
|
238
238
|
"2": { "name": "punctuation.separator.brand-intent-purpose" },
|