@vectoriox/iox-ui 5.41.0 → 5.42.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.
@@ -159,7 +159,7 @@ declare const SPEC_SITE_PATTERNS: {
159
159
  }];
160
160
  };
161
161
  };
162
- readonly "footer.columns": {
162
+ readonly "footer.three-column": {
163
163
  readonly family: "footer";
164
164
  readonly label: "Footer, three columns";
165
165
  readonly global: "after";
@@ -341,7 +341,7 @@ declare const SPEC_SITE_PATTERNS: {
341
341
  }];
342
342
  };
343
343
  };
344
- readonly "projects.grid": {
344
+ readonly "collection.card-grid": {
345
345
  readonly family: "collection";
346
346
  readonly label: "Project cards, three across";
347
347
  readonly node: {
@@ -408,8 +408,8 @@ declare const SPEC_SITE_PATTERNS: {
408
408
  };
409
409
  };
410
410
  };
411
- readonly "services.three": {
412
- readonly family: "features";
411
+ readonly "feature.triad": {
412
+ readonly family: "feature";
413
413
  readonly label: "Three services";
414
414
  readonly node: {
415
415
  readonly t: "Section";
@@ -503,7 +503,7 @@ declare const SPEC_SITE_PATTERNS: {
503
503
  };
504
504
  };
505
505
  };
506
- readonly "cta.band": {
506
+ readonly "cta.banner": {
507
507
  readonly family: "cta";
508
508
  readonly label: "Call to action band";
509
509
  readonly node: {
@@ -914,6 +914,9 @@ declare const SPEC_SITE_PATTERNS: {
914
914
  readonly slot: "eyebrow";
915
915
  readonly ph: "copy";
916
916
  readonly text: "Pricing";
917
+ readonly lay: {
918
+ readonly textAlign: "center";
919
+ };
917
920
  }, {
918
921
  readonly t: "Heading";
919
922
  readonly es: "h2";
@@ -921,6 +924,9 @@ declare const SPEC_SITE_PATTERNS: {
921
924
  readonly slot: "title";
922
925
  readonly ph: "copy";
923
926
  readonly text: "Plans";
927
+ readonly lay: {
928
+ readonly textAlign: "center";
929
+ };
924
930
  }];
925
931
  }, {
926
932
  readonly t: "Container";
@@ -934,6 +940,7 @@ declare const SPEC_SITE_PATTERNS: {
934
940
  readonly position: "sticky";
935
941
  readonly top: "calc(12px + env(safe-area-inset-top, 0px))";
936
942
  readonly zIndex: "2";
943
+ readonly width: "100%";
937
944
  };
938
945
  };
939
946
  }, {
@@ -982,10 +989,16 @@ declare const SPEC_SITE_PATTERNS: {
982
989
  readonly es: "price";
983
990
  readonly ph: "price";
984
991
  readonly text: "Monthly price";
992
+ readonly lay: {
993
+ readonly width: "auto";
994
+ };
985
995
  }, {
986
996
  readonly t: "Text";
987
997
  readonly es: "small";
988
998
  readonly text: "per month";
999
+ readonly lay: {
1000
+ readonly width: "auto";
1001
+ };
989
1002
  }];
990
1003
  }, {
991
1004
  readonly t: "Container";
@@ -1003,10 +1016,16 @@ declare const SPEC_SITE_PATTERNS: {
1003
1016
  readonly es: "price";
1004
1017
  readonly ph: "price";
1005
1018
  readonly text: "Yearly price";
1019
+ readonly lay: {
1020
+ readonly width: "auto";
1021
+ };
1006
1022
  }, {
1007
1023
  readonly t: "Text";
1008
1024
  readonly es: "small";
1009
1025
  readonly text: "per year";
1026
+ readonly lay: {
1027
+ readonly width: "auto";
1028
+ };
1010
1029
  }];
1011
1030
  }, {
1012
1031
  readonly t: "Container";
@@ -1024,7 +1043,7 @@ declare const SPEC_SITE_PATTERNS: {
1024
1043
  readonly display: "flex";
1025
1044
  readonly flexWrap: "nowrap";
1026
1045
  readonly gap: "8px";
1027
- readonly alignItems: "flex-start";
1046
+ readonly alignItems: "center";
1028
1047
  };
1029
1048
  readonly c: readonly [{
1030
1049
  readonly t: "Icon";
@@ -1043,7 +1062,7 @@ declare const SPEC_SITE_PATTERNS: {
1043
1062
  readonly display: "flex";
1044
1063
  readonly flexWrap: "nowrap";
1045
1064
  readonly gap: "8px";
1046
- readonly alignItems: "flex-start";
1065
+ readonly alignItems: "center";
1047
1066
  };
1048
1067
  readonly c: readonly [{
1049
1068
  readonly t: "Icon";
@@ -1062,7 +1081,7 @@ declare const SPEC_SITE_PATTERNS: {
1062
1081
  readonly display: "flex";
1063
1082
  readonly flexWrap: "nowrap";
1064
1083
  readonly gap: "8px";
1065
- readonly alignItems: "flex-start";
1084
+ readonly alignItems: "center";
1066
1085
  };
1067
1086
  readonly c: readonly [{
1068
1087
  readonly t: "Icon";
@@ -1103,6 +1122,7 @@ declare const SPEC_SITE_PATTERNS: {
1103
1122
  readonly ph: "copy";
1104
1123
  readonly scheme: "accent";
1105
1124
  readonly badge: "neutral";
1125
+ readonly icon: "ph ph-sparkle";
1106
1126
  readonly parts: {
1107
1127
  readonly media: false;
1108
1128
  readonly eyebrow: false;
@@ -1130,10 +1150,16 @@ declare const SPEC_SITE_PATTERNS: {
1130
1150
  readonly es: "price";
1131
1151
  readonly ph: "price";
1132
1152
  readonly text: "Monthly price";
1153
+ readonly lay: {
1154
+ readonly width: "auto";
1155
+ };
1133
1156
  }, {
1134
1157
  readonly t: "Text";
1135
1158
  readonly es: "small";
1136
1159
  readonly text: "per month";
1160
+ readonly lay: {
1161
+ readonly width: "auto";
1162
+ };
1137
1163
  }];
1138
1164
  }, {
1139
1165
  readonly t: "Container";
@@ -1151,10 +1177,16 @@ declare const SPEC_SITE_PATTERNS: {
1151
1177
  readonly es: "price";
1152
1178
  readonly ph: "price";
1153
1179
  readonly text: "Yearly price";
1180
+ readonly lay: {
1181
+ readonly width: "auto";
1182
+ };
1154
1183
  }, {
1155
1184
  readonly t: "Text";
1156
1185
  readonly es: "small";
1157
1186
  readonly text: "per year";
1187
+ readonly lay: {
1188
+ readonly width: "auto";
1189
+ };
1158
1190
  }];
1159
1191
  }, {
1160
1192
  readonly t: "Container";
@@ -1172,7 +1204,7 @@ declare const SPEC_SITE_PATTERNS: {
1172
1204
  readonly display: "flex";
1173
1205
  readonly flexWrap: "nowrap";
1174
1206
  readonly gap: "8px";
1175
- readonly alignItems: "flex-start";
1207
+ readonly alignItems: "center";
1176
1208
  };
1177
1209
  readonly c: readonly [{
1178
1210
  readonly t: "Icon";
@@ -1191,7 +1223,7 @@ declare const SPEC_SITE_PATTERNS: {
1191
1223
  readonly display: "flex";
1192
1224
  readonly flexWrap: "nowrap";
1193
1225
  readonly gap: "8px";
1194
- readonly alignItems: "flex-start";
1226
+ readonly alignItems: "center";
1195
1227
  };
1196
1228
  readonly c: readonly [{
1197
1229
  readonly t: "Icon";
@@ -1210,7 +1242,7 @@ declare const SPEC_SITE_PATTERNS: {
1210
1242
  readonly display: "flex";
1211
1243
  readonly flexWrap: "nowrap";
1212
1244
  readonly gap: "8px";
1213
- readonly alignItems: "flex-start";
1245
+ readonly alignItems: "center";
1214
1246
  };
1215
1247
  readonly c: readonly [{
1216
1248
  readonly t: "Icon";
@@ -1275,10 +1307,16 @@ declare const SPEC_SITE_PATTERNS: {
1275
1307
  readonly es: "price";
1276
1308
  readonly ph: "price";
1277
1309
  readonly text: "Monthly price";
1310
+ readonly lay: {
1311
+ readonly width: "auto";
1312
+ };
1278
1313
  }, {
1279
1314
  readonly t: "Text";
1280
1315
  readonly es: "small";
1281
1316
  readonly text: "per month";
1317
+ readonly lay: {
1318
+ readonly width: "auto";
1319
+ };
1282
1320
  }];
1283
1321
  }, {
1284
1322
  readonly t: "Container";
@@ -1296,10 +1334,16 @@ declare const SPEC_SITE_PATTERNS: {
1296
1334
  readonly es: "price";
1297
1335
  readonly ph: "price";
1298
1336
  readonly text: "Yearly price";
1337
+ readonly lay: {
1338
+ readonly width: "auto";
1339
+ };
1299
1340
  }, {
1300
1341
  readonly t: "Text";
1301
1342
  readonly es: "small";
1302
1343
  readonly text: "per year";
1344
+ readonly lay: {
1345
+ readonly width: "auto";
1346
+ };
1303
1347
  }];
1304
1348
  }, {
1305
1349
  readonly t: "Container";
@@ -1317,7 +1361,7 @@ declare const SPEC_SITE_PATTERNS: {
1317
1361
  readonly display: "flex";
1318
1362
  readonly flexWrap: "nowrap";
1319
1363
  readonly gap: "8px";
1320
- readonly alignItems: "flex-start";
1364
+ readonly alignItems: "center";
1321
1365
  };
1322
1366
  readonly c: readonly [{
1323
1367
  readonly t: "Icon";
@@ -1336,7 +1380,7 @@ declare const SPEC_SITE_PATTERNS: {
1336
1380
  readonly display: "flex";
1337
1381
  readonly flexWrap: "nowrap";
1338
1382
  readonly gap: "8px";
1339
- readonly alignItems: "flex-start";
1383
+ readonly alignItems: "center";
1340
1384
  };
1341
1385
  readonly c: readonly [{
1342
1386
  readonly t: "Icon";
@@ -1355,7 +1399,7 @@ declare const SPEC_SITE_PATTERNS: {
1355
1399
  readonly display: "flex";
1356
1400
  readonly flexWrap: "nowrap";
1357
1401
  readonly gap: "8px";
1358
- readonly alignItems: "flex-start";
1402
+ readonly alignItems: "center";
1359
1403
  };
1360
1404
  readonly c: readonly [{
1361
1405
  readonly t: "Icon";
@@ -1638,12 +1682,12 @@ declare const SPEC_SITE_TEMPLATES: {
1638
1682
  readonly sections: readonly [{
1639
1683
  readonly pattern: "hero.split";
1640
1684
  }, {
1641
- readonly pattern: "projects.grid";
1685
+ readonly pattern: "collection.card-grid";
1642
1686
  readonly binding: "projectsLatest";
1643
1687
  }, {
1644
- readonly pattern: "services.three";
1688
+ readonly pattern: "feature.triad";
1645
1689
  }, {
1646
- readonly pattern: "cta.band";
1690
+ readonly pattern: "cta.banner";
1647
1691
  }];
1648
1692
  }, {
1649
1693
  readonly key: "work";
@@ -1658,7 +1702,7 @@ declare const SPEC_SITE_TEMPLATES: {
1658
1702
  readonly title: "Work";
1659
1703
  };
1660
1704
  }, {
1661
- readonly pattern: "projects.grid";
1705
+ readonly pattern: "collection.card-grid";
1662
1706
  readonly binding: "projects";
1663
1707
  readonly without: readonly ["header"];
1664
1708
  }];
@@ -1677,7 +1721,7 @@ declare const SPEC_SITE_TEMPLATES: {
1677
1721
  }, {
1678
1722
  readonly pattern: "project.next";
1679
1723
  }, {
1680
- readonly pattern: "cta.band";
1724
+ readonly pattern: "cta.banner";
1681
1725
  }];
1682
1726
  }, {
1683
1727
  readonly key: "about";
@@ -1694,7 +1738,7 @@ declare const SPEC_SITE_TEMPLATES: {
1694
1738
  }, {
1695
1739
  readonly pattern: "about.story";
1696
1740
  }, {
1697
- readonly pattern: "cta.band";
1741
+ readonly pattern: "cta.banner";
1698
1742
  }];
1699
1743
  }, {
1700
1744
  readonly key: "contact";
@@ -1708,7 +1752,7 @@ declare const SPEC_SITE_TEMPLATES: {
1708
1752
  }];
1709
1753
  readonly globals: {
1710
1754
  readonly header: "nav.inline-cta";
1711
- readonly footer: "footer.columns";
1755
+ readonly footer: "footer.three-column";
1712
1756
  };
1713
1757
  };
1714
1758
  };
@@ -1905,8 +1949,9 @@ interface SitePlan {
1905
1949
  * CC4b (`pricing.tiers`): `recipe: 'segmented'` stamps a Segmented control (Monthly · Yearly, named by `name`, its own
1906
1950
  * `lay` / `resp`) · `shownWhen` hides a node at base and shows it while the pattern's segmented option of that key is
1907
1951
  * on (the targeted `trigger-on`) · `ph: 'price'` a price the studio types (a publish-blocking placeholder showing its
1908
- * label) · on a card recipe: `words` (title / text / badge), `badge` (the badge's tone), `scheme` (the Brand card), and
1909
- * `c` — nodes joining its Content column · `icon` an Icon's glyph · `layer` the node's name in the layer tree.
1952
+ * label) · on a card recipe: `words` (title / text / badge), `badge` (the badge's tone) and `icon` (its glyph), `scheme`
1953
+ * (the Brand card), and `c` — nodes joining its Content column · `icon` an Icon's glyph · `layer` the node's name in the
1954
+ * layer tree.
1910
1955
  */
1911
1956
  interface PatternNode {
1912
1957
  readonly t: string;
@@ -1951,7 +1996,7 @@ interface PatternNode {
1951
1996
  readonly words?: Readonly<Record<string, string>>;
1952
1997
  /** CC4b: a card recipe's badge tone (`neutral` · `success` · `warning` · `accent`). */
1953
1998
  readonly badge?: string;
1954
- /** CC4b: an Icon's glyph (`ph ph-check`). */
1999
+ /** CC4b: an Icon's glyph (`ph ph-check`) — on a card recipe, its badge's glyph. */
1955
2000
  readonly icon?: string;
1956
2001
  /** CC4b: the node's name in the layer tree (`Price · Monthly`). */
1957
2002
  readonly layer?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vectoriox/iox-ui",
3
- "version": "5.41.0",
3
+ "version": "5.42.0",
4
4
  "peerDependencies": {
5
5
  "@angular/common": ">=20.0.0",
6
6
  "@angular/core": ">=20.0.0",
@@ -230,6 +230,8 @@ export interface CardOptions extends CardEditOptions {
230
230
  parts?: Partial<Record<CardPart, boolean>>;
231
231
  words?: Partial<CardWords>;
232
232
  badgeTone?: BadgeTone;
233
+ /** The badge's glyph (else its tone's — `BADGE_TONE_ICONS`): the pricing pattern's "Most popular" sparkle (CC4b). */
234
+ badgeIcon?: string;
233
235
  }
234
236
 
235
237
 
@@ -249,7 +251,7 @@ function buttonLabel(create: CompositeNodeFactory, key: string, words: string):
249
251
  }
250
252
 
251
253
  /** Each optional part (and the locked title) as the recipe builds it. */
252
- const PART_BUILDERS: Readonly<Record<CardPart, (create: CompositeNodeFactory, words: CardWords, tone: BadgeTone) => CompositeNode>> = {
254
+ const PART_BUILDERS: Readonly<Record<CardPart, (create: CompositeNodeFactory, words: CardWords, tone: BadgeTone, badgeIcon?: string) => CompositeNode>> = {
253
255
  [CardPart.Media]: (create) => {
254
256
  const media = make(create, 'media', Type.Container, CardRole.Media, Style.Box, {}, 'Media');
255
257
  const image = make(create, 'image', Type.Image, null, Style.Media, { src: '', alt: '' });
@@ -257,8 +259,8 @@ const PART_BUILDERS: Readonly<Record<CardPart, (create: CompositeNodeFactory, wo
257
259
  media.children = [image];
258
260
  return media;
259
261
  },
260
- [CardPart.Badge]: (create, words, tone) => {
261
- const badge = buildBadge(create, { tone, text: words.badge, key: 'badge' });
262
+ [CardPart.Badge]: (create, words, tone, badgeIcon) => {
263
+ const badge = buildBadge(create, { tone, text: words.badge, key: 'badge', ...(badgeIcon ? { icon: badgeIcon } : {}) });
262
264
  badge.role = CardRole.Badge;
263
265
  badge.label = 'Badge';
264
266
  return badge;
@@ -296,7 +298,7 @@ export function buildCardRecipe(create: CompositeNodeFactory, options: CardOptio
296
298
  ...(on(CardPart.Actions) ? [PART_BUILDERS[CardPart.Actions](create, words, tone)] : []),
297
299
  ];
298
300
  card.children = [...(on(CardPart.Media) ? [PART_BUILDERS[CardPart.Media](create, words, tone)] : []), content];
299
- if (on(CardPart.Badge)) placeBadge(card, PART_BUILDERS[CardPart.Badge](create, words, tone), true);
301
+ if (on(CardPart.Badge)) placeBadge(card, PART_BUILDERS[CardPart.Badge](create, words, tone, options.badgeIcon), true);
300
302
  applyCardLayout(card, options.layout ?? CardLayout.Top, options);
301
303
  return card;
302
304
  }
@@ -68,10 +68,12 @@ enum Style {
68
68
  const ZERO_BOX = '0px 0px 0px 0px';
69
69
 
70
70
  /** What each part holds — layout only; its LOOK is its element style (the track's fill, border, radius and pad; the
71
- * option's ink, pad, radius and 44px target). The options sit in a flex row a gap token apart (board 10). */
71
+ * option's ink, pad, radius and 44px target). The options sit in a flex row a gap token apart (board 10); each GROWS,
72
+ * which changes nothing while the track hugs them and shares the row when the track is stretched (the pricing
73
+ * pattern's phone layer: board 10's full-width control). */
72
74
  const PART_BASE: Readonly<Record<string, Readonly<Record<string, string>>>> = {
73
75
  [SegmentedRole.Segmented]: { display: 'inline-flex', flexDirection: 'row', alignItems: 'stretch', gap: 'var(--iox-segmented-gap)', width: 'auto', maxWidth: '100%', height: 'auto' },
74
- [SegmentedRole.Option]: { display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: '8px', width: 'auto', height: 'auto', cursor: 'pointer' },
76
+ [SegmentedRole.Option]: { display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: '8px', width: 'auto', height: 'auto', cursor: 'pointer', flexGrow: '1' },
75
77
  };
76
78
  const LABEL_BASE: Readonly<Record<string, string>> = { width: 'auto', minHeight: 'auto', padding: ZERO_BOX, margin: ZERO_BOX };
77
79
  /** The badge inside an option keeps to the option's row (never its own line). */
@@ -672,6 +672,7 @@ function composeRecipe(pn: PatternNode, id: string, repeater: RepeaterScope | nu
672
672
  for (const key of Object.keys(pn.words ?? {})) if (!CARD_WORD_KEYS.includes(key)) throw new ComposeError(`${id}: a card has no words "${key}"`);
673
673
  const card = buildCardRecipe(factory, {
674
674
  layout, parts: pn.parts as Partial<Record<CardPart, boolean>>, words: pn.words as Partial<CardWords> | undefined, badgeTone: tone,
675
+ ...(pn.icon ? { badgeIcon: pn.icon } : {}),
675
676
  }) as SerializedNode;
676
677
  if (pn.scheme) {
677
678
  if (!COLOR_SCHEMES.some(c => c.id === pn.scheme)) throw new ComposeError(`${id}: no colour scheme "${pn.scheme}"`);
@@ -25,8 +25,9 @@ import { SPEC_SITE_PATTERNS } from './site-spec.generated';
25
25
  * CC4b (`pricing.tiers`): `recipe: 'segmented'` stamps a Segmented control (Monthly · Yearly, named by `name`, its own
26
26
  * `lay` / `resp`) · `shownWhen` hides a node at base and shows it while the pattern's segmented option of that key is
27
27
  * on (the targeted `trigger-on`) · `ph: 'price'` a price the studio types (a publish-blocking placeholder showing its
28
- * label) · on a card recipe: `words` (title / text / badge), `badge` (the badge's tone), `scheme` (the Brand card), and
29
- * `c` — nodes joining its Content column · `icon` an Icon's glyph · `layer` the node's name in the layer tree.
28
+ * label) · on a card recipe: `words` (title / text / badge), `badge` (the badge's tone) and `icon` (its glyph), `scheme`
29
+ * (the Brand card), and `c` — nodes joining its Content column · `icon` an Icon's glyph · `layer` the node's name in the
30
+ * layer tree.
30
31
  */
31
32
  export interface PatternNode {
32
33
  readonly t: string;
@@ -71,7 +72,7 @@ export interface PatternNode {
71
72
  readonly words?: Readonly<Record<string, string>>;
72
73
  /** CC4b: a card recipe's badge tone (`neutral` · `success` · `warning` · `accent`). */
73
74
  readonly badge?: string;
74
- /** CC4b: an Icon's glyph (`ph ph-check`). */
75
+ /** CC4b: an Icon's glyph (`ph ph-check`) — on a card recipe, its badge's glyph. */
75
76
  readonly icon?: string;
76
77
  /** CC4b: the node's name in the layer tree (`Price · Monthly`). */
77
78
  readonly layer?: string;