tosijs-ui 1.16.19 → 1.16.20

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 CHANGED
@@ -1,5 +1,29 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.16.20
4
+
5
+ - **A registered icon with style suffixes wins over a composition rule.** `checkCircle90r`
6
+ drew the `check` rule over a rotated `circle`; it is now `checkCircle` turned 90°, in the
7
+ renderer and in `iconSvg`. A rule still applies where the whole name is not a registered
8
+ icon with suffixes (`checkLock90r`). closes board #3295
9
+ - **`defineIcons` warns when a new name it defines is also what a composition rule would
10
+ draw** ("icon checkFoo is also a composition rule's name; your definition wins"). Only a
11
+ name that reads as the rule's warns (`checkFoo`, not `checkbox` or `unlink`), and
12
+ overriding a name that already exists, a built-in such as `unlock` included, never does.
13
+ - **A redirect to a missing icon names what was asked for.** `defineIcons({ myChevron:
14
+ 'chevronDwon' })` warned "icon chevronDwon does not exist", naming only the target; it
15
+ now adds "(requested as myChevron)", through stacks too. closes board #3294
16
+ - **Six live examples on the doc site showed nothing; a Playwright spec now fails on any
17
+ that does.**
18
+ - The table's Column Width section rendered an empty `<tosi-table>` (an `html` fence
19
+ meant as syntax). It is now a live before/after with a test.
20
+ - The router page's examples shared the page's one router, so the last to run blanked
21
+ the others. Basic Usage holds it on load and has links; Named Views routes when you
22
+ press its button, and a test checks the first example shows its route.
23
+ - Code meant to be read no longer runs as an empty example: the token-palette `css` on
24
+ highlight-block and highlight-styles, liveTheme's programmatic-control snippet, and the
25
+ migrating guide's setSchemaValidator lines are `:static`.
26
+
3
27
  ## 1.16.19
4
28
 
5
29
  - **`iconSvg()` resolves the built-in icons that rotate or flip another.** 27 built-in
@@ -948,10 +948,38 @@ by adding the `noreorder` attribute to the `<tosi-table>`.
948
948
 
949
949
  Columns are laid out at the widths you give them, so a table whose columns add up to less
950
950
  than its container leaves a strip of blank space on the right. Add the `full-width-header`
951
- attribute and the leftover space goes to the last **unpinned** column instead:
951
+ attribute (`fullWidthHeader: true` from the creator) and the leftover space goes to the last
952
+ **unpinned** column instead. The same columns, without and with it:
952
953
 
953
- ```html
954
- <tosi-table full-width-header></tosi-table>
954
+ ```js
955
+ import { tosiTable } from 'tosijs-ui'
956
+
957
+ const array = [
958
+ { id: 1, name: 'Ada', role: 'Analyst' },
959
+ { id: 2, name: 'Grace', role: 'Admiral' },
960
+ { id: 3, name: 'Margaret', role: 'Director' },
961
+ ]
962
+ const columns = () => [
963
+ { prop: 'id', name: 'ID', width: 60 },
964
+ { prop: 'name', name: 'Name', width: 120 },
965
+ { prop: 'role', name: 'Role', width: 120 },
966
+ ]
967
+ const style = { display: 'block', height: '150px' }
968
+ preview.append(
969
+ tosiTable({ array, columns: columns(), style }),
970
+ tosiTable({ array, columns: columns(), style, fullWidthHeader: true })
971
+ )
972
+ ```
973
+ ```test
974
+ const roleWidth = (table) =>
975
+ [...table.querySelectorAll('.thead .th')].at(-1).getBoundingClientRect().width
976
+ test('only the full-width table stretches its last column', async () => {
977
+ const [plain, full] = preview.querySelectorAll('tosi-table')
978
+ for (let i = 0; i < 100 && !full.querySelector('.thead .th'); i++) await waitMs(20)
979
+ expect(full.hasAttribute('full-width-header')).toBe(true)
980
+ expect(Math.round(roleWidth(plain))).toBe(120)
981
+ expect(roleWidth(full)).toBeGreaterThan(120)
982
+ })
955
983
  ```
956
984
 
957
985
  Right-pinned columns are skipped because they sit against the right edge by definition —
@@ -23,7 +23,7 @@ ensureHighlightStyles()
23
23
  Every colour is a `varDefault`, so retheme any single token type — in either mode —
24
24
  by setting its variable:
25
25
 
26
- ```css
26
+ ```css:static
27
27
  :root { --token-keyword: rebeccapurple; }
28
28
  ```
29
29
 
@@ -43,7 +43,7 @@ shipped failing WCAG AA on every token type.
43
43
 
44
44
  Every colour is a `varDefault`, so retheme one token type without touching the rest:
45
45
 
46
- ```css
46
+ ```css:static
47
47
  :root { --token-keyword: rebeccapurple; }
48
48
  ```
49
49
 
package/dist/icon-svg.js CHANGED
@@ -76,10 +76,9 @@ function resolve(asked, name, seen) {
76
76
  seen.add(key);
77
77
  return resolve(asked, value, seen);
78
78
  }
79
- // The renderer's order for a name that is not registered: a stack, then a rule, then
80
- // suffixes. So `checkCircle90r` is the `check` rule over `circle90r`, not `checkCircle`
81
- // turned, and only a name no rule claims gets the transform.
82
- const split = key.includes('$') || ruleDraws(key) ? null : splitSuffixes(key);
79
+ // The renderer's order for a name that is not registered: a stack, then suffixes on a
80
+ // registered icon (they win over a rule, #3295), then a rule.
81
+ const split = key.includes('$') ? null : splitSuffixes(key);
83
82
  if (split &&
84
83
  data[split.baseName] !== undefined &&
85
84
  split.tokens.every(isGeometry)) {
package/dist/icons.js CHANGED
@@ -568,7 +568,30 @@ import { splitSuffixes, suffixNumber } from './icon-suffixes.js';
568
568
  import { iconRules, spinRule, matchRule, canResolve } from './icon-rules.js';
569
569
  export { iconRules } from './icon-rules.js';
570
570
  export const defineIcons = (newIcons) => {
571
+ const data = iconData;
572
+ const isNew = Object.keys(newIcons).filter((name) => !data[name]);
571
573
  Object.assign(iconData, newIcons);
574
+ /*
575
+ A defined icon always wins over a composition rule that would produce the same name, but
576
+ the author may not know the rule exists. Say so once, here, where the name was chosen, and
577
+ only for a NEW name that reads as the rule's: `checkTestShadow`, not `checkbox` or `unlink`
578
+ (a lowercase run is a word, not a prefix plus an icon, so icon packs stay quiet).
579
+ Overriding a name that already existed, a built-in such as `unlock` or `checkCircle`
580
+ included, is a deliberate choice of that name and never warns, so neither does defining a
581
+ name a second time.
582
+ */
583
+ for (const name of isNew) {
584
+ const readsAsRule = iconRules.some((rule) => {
585
+ const matched = matchRule(rule, name);
586
+ if (matched == null || !canResolve(matched.baseName))
587
+ return false;
588
+ return (typeof rule.prefix !== 'string' ||
589
+ /[A-Z]/.test(name[rule.prefix.length]));
590
+ });
591
+ if (readsAsRule) {
592
+ console.warn(`icon ${name} is also a composition rule's name; your definition wins`);
593
+ }
594
+ }
572
595
  };
573
596
  // Raw-markup accessors live in ./icon-svg (DOM-free) and are re-exported here.
574
597
  export { iconSvg, iconNames } from './icon-svg.js';
@@ -872,19 +895,28 @@ export function resolveIcon(prop, parts) {
872
895
  });
873
896
  return wrapIcon(prop, parts, base, ...overlays);
874
897
  }
875
- // Try composition (spin, un, check, etc.)
876
- const composed = composeIcon(prop, parts);
877
- if (composed)
878
- return composed;
879
- // Style suffixes — strip them, resolve the base, apply after
898
+ /*
899
+ Style suffixes on a registered icon win over a composition rule (#3295): `checkCircle90r`
900
+ is `checkCircle` turned, not the `check` rule over `circle90r`. A rule still applies
901
+ when its suffixes are on an icon that only the rule makes (`spin90Loader50o`).
902
+ */
880
903
  const parsed = parseStyleSuffixes(prop);
904
+ const native = parsed != null && iconData[parsed.baseName];
905
+ if (!native) {
906
+ const composed = composeIcon(prop, parts);
907
+ if (composed)
908
+ return composed;
909
+ }
881
910
  if (parsed) {
882
911
  const icon = resolveIcon(parsed.baseName, parts);
883
912
  Object.assign(icon.style, parsed.style);
884
913
  return icon;
885
914
  }
886
915
  if (prop) {
887
- console.warn(`icon ${prop} does not exist`);
916
+ // Inside a redirect, also name what the caller asked for: a typo in a defineIcons
917
+ // target is otherwise reported under a name that appears nowhere in their code.
918
+ const via = expanding.size > 0 ? ` (requested as ${[...expanding].join(' → ')})` : '';
919
+ console.warn(`icon ${prop} does not exist${via}`);
888
920
  }
889
921
  return makeIcon(iconData.square, parts);
890
922
  }