tosijs-ui 1.16.19 → 1.16.21

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,43 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.16.21
4
+
5
+ - **Expanding or collapsing a table row's detail no longer corrects `scrollTop` itself.**
6
+ tosijs 1.10.9's variable-height list keeps the row in place across the height change, so
7
+ `<tosi-table>` stopped measuring the row and nudging the scroll position up to three
8
+ times. closes board #3326
9
+ - **tosijs peer floor raised to `^1.10.9`**, the release that anchors the list. On an older
10
+ tosijs the toggled row now jumps when its detail opens or closes; upgrade tosijs alongside
11
+ (`dist/iife.js` already bundles 1.10.9).
12
+ - **The icons page points to [lucide](https://lucide.dev)** in its introduction and under
13
+ Icon Sources: a fork and expansion of Feather, and the place to start for more icons in
14
+ the same style.
15
+
16
+
17
+ ## 1.16.20
18
+
19
+ - **A registered icon with style suffixes wins over a composition rule.** `checkCircle90r`
20
+ drew the `check` rule over a rotated `circle`; it is now `checkCircle` turned 90°, in the
21
+ renderer and in `iconSvg`. A rule still applies where the whole name is not a registered
22
+ icon with suffixes (`checkLock90r`). closes board #3295
23
+ - **`defineIcons` warns when a new name it defines is also what a composition rule would
24
+ draw** ("icon checkFoo is also a composition rule's name; your definition wins"). Only a
25
+ name that reads as the rule's warns (`checkFoo`, not `checkbox` or `unlink`), and
26
+ overriding a name that already exists, a built-in such as `unlock` included, never does.
27
+ - **A redirect to a missing icon names what was asked for.** `defineIcons({ myChevron:
28
+ 'chevronDwon' })` warned "icon chevronDwon does not exist", naming only the target; it
29
+ now adds "(requested as myChevron)", through stacks too. closes board #3294
30
+ - **Six live examples on the doc site showed nothing; a Playwright spec now fails on any
31
+ that does.**
32
+ - The table's Column Width section rendered an empty `<tosi-table>` (an `html` fence
33
+ meant as syntax). It is now a live before/after with a test.
34
+ - The router page's examples shared the page's one router, so the last to run blanked
35
+ the others. Basic Usage holds it on load and has links; Named Views routes when you
36
+ press its button, and a test checks the first example shows its route.
37
+ - Code meant to be read no longer runs as an empty example: the token-palette `css` on
38
+ highlight-block and highlight-styles, liveTheme's programmatic-control snippet, and the
39
+ migrating guide's setSchemaValidator lines are `:static`.
40
+
3
41
  ## 1.16.19
4
42
 
5
43
  - **`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 —
@@ -1742,24 +1770,11 @@ export class TosiTable extends withAttributes({
1742
1770
  this._detailEls.delete(key);
1743
1771
  }
1744
1772
  const rowEl = this.stampedRow(raw);
1745
- const top = rowEl?.getBoundingClientRect().top;
1746
1773
  if (rowEl)
1747
1774
  this.syncDetail(rowEl, raw);
1775
+ // The list anchors itself across a height change (tosijs 1.10.9), so the toggled row
1776
+ // keeps its place without a scrollTop correction here.
1748
1777
  this.remeasureRows();
1749
- /*
1750
- Keep the toggled row where it was. A variable-height list places its rows by
1751
- interpolation, so a row that changes height moves every row, itself included, by an
1752
- amount that depends on how far down the list is scrolled.
1753
- */
1754
- if (top !== undefined && this._scrollArea) {
1755
- for (let i = 0; i < 3; i += 1) {
1756
- const now = this.stampedRow(raw)?.getBoundingClientRect().top;
1757
- if (now === undefined || Math.abs(now - top) < 1)
1758
- break;
1759
- this._scrollArea.scrollTop += now - top;
1760
- this.remeasureRows();
1761
- }
1762
- }
1763
1778
  this.dispatchEvent(new CustomEvent(want ? 'rowexpand' : 'rowcollapse', {
1764
1779
  bubbles: true,
1765
1780
  detail: { row: raw, element: element?.firstElementChild ?? null },
@@ -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
@@ -11,6 +11,11 @@
11
11
  e.g. `icons.chevronDown()` produces an `<svg>` element containing a downward-pointing chevron
12
12
  icon with the class `icon-chevron-down`.
13
13
 
14
+ Most of the built-in icons come from [Feather](https://feathericons.com). If you want more
15
+ icons in the same style, [lucide](https://lucide.dev) is a fork and expansion of Feather and a
16
+ great place to start: its SVGs drop straight into `defineIcons()` (see
17
+ [Need More Icons?](#need-more-icons)).
18
+
14
19
  ```js
15
20
  const { tosi, elements } = tosijs
16
21
  import { icons, svgIcon, postNotification } from 'tosijs-ui'
@@ -202,8 +207,8 @@ probably be broken out as a standalone library to allow the use of whatever icon
202
207
 
203
208
  ### Need More Icons?
204
209
 
205
- [lucide](https://lucide.dev) is a fork of feather that maintains the style and adds a lot more
206
- options. Obviously it's completely compatible with this icon system and generously licensed. A
210
+ [lucide](https://lucide.dev) is a fork and expansion of feather that maintains the style and
211
+ adds a lot more options. Obviously it's completely compatible with this icon system and generously licensed. A
207
212
  good place to find more icons in a pinch.
208
213
 
209
214
  Concretely: lucide emits the same geometry feather does — `24×24` viewBox, `fill="none"`,
@@ -532,6 +537,7 @@ and render a `square` (in fact, `icons.square()`) as a fallback.
532
537
 
533
538
  Many of these icons are sourced from [Feather Icons](https://github.com/feathericons/feather), but
534
539
  all the icons have been processed to have integer coordinates in a `viewBox` typically scaled to 1024 &times; 1024.
540
+ For more in the same style, [lucide](https://lucide.dev) is a fork and expansion of Feather.
535
541
 
536
542
  The corporate logos (Google, etc.) are from a variety of sources, in many cases ultimately from the
537
543
  organizations themselves. It's up to you to use them correctly.
@@ -568,7 +574,30 @@ import { splitSuffixes, suffixNumber } from './icon-suffixes.js';
568
574
  import { iconRules, spinRule, matchRule, canResolve } from './icon-rules.js';
569
575
  export { iconRules } from './icon-rules.js';
570
576
  export const defineIcons = (newIcons) => {
577
+ const data = iconData;
578
+ const isNew = Object.keys(newIcons).filter((name) => !data[name]);
571
579
  Object.assign(iconData, newIcons);
580
+ /*
581
+ A defined icon always wins over a composition rule that would produce the same name, but
582
+ the author may not know the rule exists. Say so once, here, where the name was chosen, and
583
+ only for a NEW name that reads as the rule's: `checkTestShadow`, not `checkbox` or `unlink`
584
+ (a lowercase run is a word, not a prefix plus an icon, so icon packs stay quiet).
585
+ Overriding a name that already existed, a built-in such as `unlock` or `checkCircle`
586
+ included, is a deliberate choice of that name and never warns, so neither does defining a
587
+ name a second time.
588
+ */
589
+ for (const name of isNew) {
590
+ const readsAsRule = iconRules.some((rule) => {
591
+ const matched = matchRule(rule, name);
592
+ if (matched == null || !canResolve(matched.baseName))
593
+ return false;
594
+ return (typeof rule.prefix !== 'string' ||
595
+ /[A-Z]/.test(name[rule.prefix.length]));
596
+ });
597
+ if (readsAsRule) {
598
+ console.warn(`icon ${name} is also a composition rule's name; your definition wins`);
599
+ }
600
+ }
572
601
  };
573
602
  // Raw-markup accessors live in ./icon-svg (DOM-free) and are re-exported here.
574
603
  export { iconSvg, iconNames } from './icon-svg.js';
@@ -872,19 +901,28 @@ export function resolveIcon(prop, parts) {
872
901
  });
873
902
  return wrapIcon(prop, parts, base, ...overlays);
874
903
  }
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
904
+ /*
905
+ Style suffixes on a registered icon win over a composition rule (#3295): `checkCircle90r`
906
+ is `checkCircle` turned, not the `check` rule over `circle90r`. A rule still applies
907
+ when its suffixes are on an icon that only the rule makes (`spin90Loader50o`).
908
+ */
880
909
  const parsed = parseStyleSuffixes(prop);
910
+ const native = parsed != null && iconData[parsed.baseName];
911
+ if (!native) {
912
+ const composed = composeIcon(prop, parts);
913
+ if (composed)
914
+ return composed;
915
+ }
881
916
  if (parsed) {
882
917
  const icon = resolveIcon(parsed.baseName, parts);
883
918
  Object.assign(icon.style, parsed.style);
884
919
  return icon;
885
920
  }
886
921
  if (prop) {
887
- console.warn(`icon ${prop} does not exist`);
922
+ // Inside a redirect, also name what the caller asked for: a typo in a defineIcons
923
+ // target is otherwise reported under a name that appears nowhere in their code.
924
+ const via = expanding.size > 0 ? ` (requested as ${[...expanding].join(' → ')})` : '';
925
+ console.warn(`icon ${prop} does not exist${via}`);
888
926
  }
889
927
  return makeIcon(iconData.square, parts);
890
928
  }