@svar-ui/svelte-toolbar 2.5.3 → 2.6.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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@svar-ui/svelte-toolbar",
3
- "version": "2.5.3",
3
+ "version": "2.6.0",
4
4
  "description": "Svelte toolbar component. Lightweight, customizable, easy-to-use.",
5
5
  "productTag": "toolbar",
6
6
  "productTrial": false,
@@ -33,8 +33,8 @@
33
33
  },
34
34
  "homepage": "https://svar.dev/svelte/core/",
35
35
  "dependencies": {
36
- "@svar-ui/svelte-core": "2.5.3",
37
- "@svar-ui/lib-dom": "0.12.2"
36
+ "@svar-ui/svelte-core": "2.6.0",
37
+ "@svar-ui/lib-dom": "0.13.1"
38
38
  },
39
39
  "files": [
40
40
  "src",
@@ -1,7 +1,17 @@
1
1
  <script>
2
2
  import { Button } from "@svar-ui/svelte-core";
3
3
 
4
- let { icon, text = "", css, type, disabled, menu, onclick } = $props();
4
+ let {
5
+ icon,
6
+ title,
7
+ text = "",
8
+ tooltip,
9
+ css,
10
+ type,
11
+ disabled,
12
+ menu,
13
+ onclick,
14
+ } = $props();
5
15
  </script>
6
16
 
7
17
  {#if menu}
@@ -12,7 +22,7 @@
12
22
  {text}
13
23
  </div>
14
24
  {:else}
15
- <Button {icon} {type} {css} {text} {disabled} {onclick} />
25
+ <Button {icon} {type} {css} {title} {text} {tooltip} {disabled} {onclick} />
16
26
  {/if}
17
27
 
18
28
  <style>
@@ -1,7 +1,8 @@
1
1
  <script>
2
2
  import { Button } from "@svar-ui/svelte-core";
3
3
 
4
- let { icon, text, css, type, disabled, menu, onclick } = $props();
4
+ let { icon, title, text, tooltip, css, type, disabled, menu, onclick } =
5
+ $props();
5
6
  </script>
6
7
 
7
8
  {#if menu}
@@ -12,7 +13,7 @@
12
13
  {text}
13
14
  </div>
14
15
  {:else}
15
- <Button {icon} {type} {css} title={text} {disabled} {onclick} />
16
+ <Button {icon} {type} {css} {disabled} {title} {tooltip} {onclick} />
16
17
  {/if}
17
18
 
18
19
  <style>
@@ -3,6 +3,7 @@
3
3
 
4
4
  import BarComponent from "./BarComponent.svelte";
5
5
  import { Dropdown } from "@svar-ui/svelte-core";
6
+ import { collapseSeparators } from "../helpers";
6
7
 
7
8
  let { item, values = null, menu = false, onchange, onclick } = $props();
8
9
 
@@ -49,7 +50,7 @@
49
50
  {/if}
50
51
  {:else}
51
52
  <div class="wx-tb-body">
52
- {#each item.items as sub}
53
+ {#each collapseSeparators(item.items) as sub}
53
54
  {#if sub.items}
54
55
  <Group item={sub} {values} onclick={onClick} {onchange} />
55
56
  {:else}
@@ -3,8 +3,8 @@
3
3
  import Group from "./Group.svelte";
4
4
  import BarComponent from "./BarComponent.svelte";
5
5
 
6
- import { uid } from "@svar-ui/lib-dom";
7
6
  import { onMount, tick } from "svelte";
7
+ import { normalizeToolbarItems } from "../helpers";
8
8
 
9
9
  let {
10
10
  items = $bindable([]),
@@ -28,12 +28,62 @@
28
28
  let div = null;
29
29
  let menuItems = $state([]);
30
30
 
31
+ const barItems = $derived(normalizeToolbarItems(items));
32
+
33
+ function getTotalWidth() {
34
+ const nodes = div.children;
35
+ let sum = 0;
36
+ for (let i = 0; i < barItems.length; i++) {
37
+ if (barItems[i].comp != "spacer") {
38
+ sum += nodes[i]?.clientWidth || 0;
39
+ if (barItems[i].comp == "separator") sum += 8;
40
+ }
41
+ }
42
+ return sum;
43
+ }
44
+
45
+ function collapseGroups() {
46
+ for (let i = barItems.length - 1; i >= 0; i--) {
47
+ // close rightmost open group
48
+ if (barItems[i].items && !barItems[i].collapsed) {
49
+ barItems[i].collapsed = true;
50
+ barItems[i].$width = div.children[i].offsetWidth;
51
+ // check after dom update, maybe we need to close more
52
+ tick().then(processOverflow);
53
+
54
+ // items are not deep reactive, so we need to trigger the update
55
+ items = [...items];
56
+ return;
57
+ }
58
+ }
59
+ }
60
+
61
+ function expandGroups(freeSpace) {
62
+ for (let i = 0; i < barItems.length; i++) {
63
+ // open leftmost closed group that was collapsed by overflow
64
+ if (barItems[i].collapsed && barItems[i].$width) {
65
+ // check if group can fit in free space
66
+ if (
67
+ barItems[i].$width - div.children[i].offsetWidth <
68
+ freeSpace + 10
69
+ ) {
70
+ barItems[i].collapsed = false;
71
+ // items are not deep reactive, so we need to trigger the update
72
+ items = [...items];
73
+ // check after dom update, maybe we can open one more
74
+ tick().then(processOverflow);
75
+ }
76
+ return;
77
+ }
78
+ }
79
+ }
80
+
31
81
  function processOverflow() {
32
82
  if (overflow === "wrap") return;
33
83
 
34
84
  const nodes = div.children;
35
85
  // restore all items so widths can be measured
36
- for (let i = 0; i < items.length; i++) {
86
+ for (let i = 0; i < barItems.length; i++) {
37
87
  if (nodes[i]) nodes[i].style.display = "";
38
88
  }
39
89
 
@@ -42,32 +92,32 @@
42
92
  const needMenu = fullWidth > visibleWidth;
43
93
 
44
94
  if (needMenu) {
45
- if (overflow === "collapse") return collapseGroups(visibleWidth);
95
+ if (overflow === "collapse") return collapseGroups();
46
96
 
47
97
  // pinned items always stay visible
48
98
  let pinnedWidth = 0;
49
- for (let i = 0; i < items.length; i++) {
50
- if (items[i].pinned) pinnedWidth += nodes[i].clientWidth;
99
+ for (let i = 0; i < barItems.length; i++) {
100
+ if (barItems[i].pinned) pinnedWidth += nodes[i].clientWidth;
51
101
  }
52
102
 
53
103
  let sum = 0;
54
- for (let i = 0; i < items.length; i++) {
55
- if (items[i].pinned) continue;
104
+ for (let i = 0; i < barItems.length; i++) {
105
+ if (barItems[i].pinned) continue;
56
106
  sum += nodes[i].clientWidth;
57
- if (items[i].comp == "separator") sum += 8;
107
+ if (barItems[i].comp == "separator") sum += 8;
58
108
  if (sum > visibleWidth - 40 - pinnedWidth) {
59
109
  // we need to hide nodes[i] and all next non-pinned nodes
60
110
  menuItems = [];
61
- for (let j = i; j < items.length; j++) {
62
- if (items[j].pinned) continue;
63
- menuItems.push(items[j]);
111
+ for (let j = i; j < barItems.length; j++) {
112
+ if (barItems[j].pinned) continue;
113
+ menuItems.push(barItems[j]);
64
114
  nodes[j].style.display = "none";
65
115
  }
66
116
  // hide the ending separator
67
117
  if (
68
118
  i > 0 &&
69
- items[i - 1].comp == "separator" &&
70
- !items[i - 1].pinned
119
+ barItems[i - 1].comp == "separator" &&
120
+ !barItems[i - 1].pinned
71
121
  ) {
72
122
  nodes[i - 1].style.display = "none";
73
123
  }
@@ -83,72 +133,14 @@
83
133
  }
84
134
  }
85
135
 
86
- function getTotalWidth() {
87
- const nodes = div.children;
88
- let sum = 0;
89
- for (let i = 0; i < items.length; i++) {
90
- if (items[i].comp != "spacer") {
91
- sum += nodes[i]?.clientWidth || 0;
92
- if (items[i].comp == "separator") sum += 8;
93
- }
94
- }
95
- return sum;
96
- }
97
-
98
- function collapseGroups() {
99
- for (let i = items.length - 1; i >= 0; i--) {
100
- // close rightmost open group
101
- if (items[i].items && !items[i].collapsed) {
102
- items[i].collapsed = true;
103
- items[i].$width = div.children[i].offsetWidth;
104
- // check after dom update, maybe we need to close more
105
- tick().then(processOverflow);
106
-
107
- // items are not deep reactive, so we need to trigger the update
108
- items = [...items];
109
- return;
110
- }
111
- }
112
- }
113
-
114
- function expandGroups(freeSpace) {
115
- for (let i = 0; i < items.length; i++) {
116
- // close leftmost closed group, that was closed previously
117
- if (items[i].collapsed && items[i].$width) {
118
- // check if group can fit in free space
119
- if (
120
- items[i].$width - div.children[i].offsetWidth <
121
- freeSpace + 10
122
- ) {
123
- items[i].collapsed = false;
124
- // check after dom update, maybe we can open one more
125
- tick().then(processOverflow);
126
- }
127
-
128
- items = [...items];
129
- return;
130
- }
131
- }
132
- }
133
-
134
- // rebuild toolbar items, inject group nodes
135
- function normalize(items) {
136
- items.forEach(item => {
137
- if (!item.id) item.id = uid();
138
- });
139
- return items;
140
- }
141
-
142
136
  onMount(() => {
143
- const resizeObserver = new ResizeObserver(() => processOverflow(div));
137
+ const resizeObserver = new ResizeObserver(() => processOverflow());
144
138
  resizeObserver.observe(div);
145
139
 
146
140
  return () => {
147
- if (resizeObserver) resizeObserver.unobserve(div);
141
+ resizeObserver.unobserve(div);
148
142
  };
149
143
  });
150
-
151
- const visibleItems = $derived(normalize(items));
152
144
  </script>
153
145
 
154
146
  <div
@@ -158,7 +150,7 @@
158
150
  class:wx-has-menu={menuItems.length}
159
151
  bind:this={div}
160
152
  >
161
- {#each visibleItems as item}
153
+ {#each barItems as item}
162
154
  {#if item.items}
163
155
  <Group {item} {values} {onclick} onchange={handleChange} />
164
156
  {:else}
package/src/helpers.js CHANGED
@@ -1,3 +1,5 @@
1
+ import { uid } from "@svar-ui/lib-dom";
2
+
1
3
  const handlers = {};
2
4
  export function getItemHandler(type) {
3
5
  return handlers[type] || type;
@@ -5,3 +7,26 @@ export function getItemHandler(type) {
5
7
  export function registerToolbarItem(type, handler) {
6
8
  handlers[type] = handler;
7
9
  }
10
+
11
+ // Assign stable ids and collapse consecutive separators
12
+ export function normalizeToolbarItems(items) {
13
+ if (!items?.length) return [];
14
+ for (const item of items) {
15
+ if (!item.id) item.id = uid();
16
+ }
17
+ return collapseSeparators(items);
18
+ }
19
+
20
+ // Keep the first of consecutive separator
21
+ export function collapseSeparators(items) {
22
+ if (!items?.length) return items || [];
23
+ const out = [];
24
+ let prevWasSeparator = false;
25
+ for (const item of items) {
26
+ const isSeparator = item.comp === "separator";
27
+ if (isSeparator && prevWasSeparator) continue;
28
+ out.push(item);
29
+ prevWasSeparator = isSeparator;
30
+ }
31
+ return out;
32
+ }
package/types/index.d.ts CHANGED
@@ -5,9 +5,13 @@ export interface IToolbarItem {
5
5
  comp?: string | Component<any>;
6
6
  icon?: string;
7
7
  css?: string;
8
+ type?: string;
9
+ title?: string;
8
10
  text?: string;
11
+ tooltip?: string;
9
12
  menuText?: string;
10
13
  key?: string;
14
+ disabled?: boolean;
11
15
  spacer?: boolean;
12
16
  collapsed?: boolean;
13
17
  handler?: (item: IToolbarItem, value?: any) => void;
package/whatsnew.md CHANGED
@@ -1,3 +1,13 @@
1
+ ## 2.6.0
2
+
3
+ ### New features
4
+
5
+ - Tooltip for toolbar items
6
+
7
+ ### Fixes
8
+
9
+ - Duplicate separators are auto removed
10
+
1
11
  ## 2.5.3
2
12
 
3
13
  ### Fixes