@svar-ui/svelte-toolbar 2.5.2 → 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 +3 -3
- package/src/buttons/Button.svelte +12 -2
- package/src/buttons/Icon.svelte +3 -2
- package/src/components/Group.svelte +2 -1
- package/src/components/Toolbar.svelte +77 -77
- package/src/helpers.js +25 -0
- package/types/index.d.ts +5 -0
- package/whatsnew.md +12 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@svar-ui/svelte-toolbar",
|
|
3
|
-
"version": "2.
|
|
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.
|
|
37
|
-
"@svar-ui/lib-dom": "0.
|
|
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 {
|
|
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>
|
package/src/buttons/Icon.svelte
CHANGED
|
@@ -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 } =
|
|
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}
|
|
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([]),
|
|
@@ -26,78 +26,28 @@
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
let div = null;
|
|
29
|
-
let lastToolbarState = -1;
|
|
30
29
|
let menuItems = $state([]);
|
|
31
30
|
|
|
32
|
-
|
|
33
|
-
if (overflow === "wrap") return;
|
|
34
|
-
|
|
35
|
-
const visibleWidth = div.clientWidth;
|
|
36
|
-
const fullWidth = div.scrollWidth;
|
|
37
|
-
const needMenu = fullWidth > visibleWidth;
|
|
38
|
-
|
|
39
|
-
if (needMenu) {
|
|
40
|
-
if (overflow === "collapse") return collapseGroups(visibleWidth);
|
|
41
|
-
// we need to decide how many nodes need to be
|
|
42
|
-
const nodes = div.children;
|
|
43
|
-
let sum = 0;
|
|
44
|
-
|
|
45
|
-
for (let i = 0; i < items.length; i++) {
|
|
46
|
-
sum += nodes[i].clientWidth;
|
|
47
|
-
if (items[i].comp == "separator") sum += 8;
|
|
48
|
-
if (sum > visibleWidth - 40) {
|
|
49
|
-
// skip updates, as visibility state was not changed
|
|
50
|
-
if (lastToolbarState === i) return;
|
|
51
|
-
lastToolbarState = i;
|
|
52
|
-
|
|
53
|
-
// we need to hide nodes[i] and all next nodes
|
|
54
|
-
menuItems = [];
|
|
55
|
-
for (let j = i; j < items.length; j++) {
|
|
56
|
-
menuItems.push(items[j]);
|
|
57
|
-
nodes[j].style.visibility = "hidden";
|
|
58
|
-
}
|
|
59
|
-
// hide the ending separator
|
|
60
|
-
if (i > 0 && items[i - 1].comp == "separator") {
|
|
61
|
-
nodes[i - 1].style.visibility = "hidden";
|
|
62
|
-
}
|
|
63
|
-
break;
|
|
64
|
-
}
|
|
65
|
-
nodes[i].style.visibility = "";
|
|
66
|
-
}
|
|
67
|
-
} else {
|
|
68
|
-
const freeWidth = visibleWidth - getTotalWidth();
|
|
69
|
-
if (freeWidth <= 0) return;
|
|
70
|
-
if (overflow === "collapse") return expandGroups(freeWidth);
|
|
71
|
-
|
|
72
|
-
if (menuItems.length) {
|
|
73
|
-
lastToolbarState = null;
|
|
74
|
-
const nodes = div.children;
|
|
75
|
-
for (let i = 0; i < items.length; i++) {
|
|
76
|
-
nodes[i].style.visibility = "";
|
|
77
|
-
}
|
|
78
|
-
menuItems = [];
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
}
|
|
31
|
+
const barItems = $derived(normalizeToolbarItems(items));
|
|
82
32
|
|
|
83
33
|
function getTotalWidth() {
|
|
84
34
|
const nodes = div.children;
|
|
85
35
|
let sum = 0;
|
|
86
|
-
for (let i = 0; i <
|
|
87
|
-
if (
|
|
36
|
+
for (let i = 0; i < barItems.length; i++) {
|
|
37
|
+
if (barItems[i].comp != "spacer") {
|
|
88
38
|
sum += nodes[i]?.clientWidth || 0;
|
|
89
|
-
if (
|
|
39
|
+
if (barItems[i].comp == "separator") sum += 8;
|
|
90
40
|
}
|
|
91
41
|
}
|
|
92
42
|
return sum;
|
|
93
43
|
}
|
|
94
44
|
|
|
95
45
|
function collapseGroups() {
|
|
96
|
-
for (let i =
|
|
46
|
+
for (let i = barItems.length - 1; i >= 0; i--) {
|
|
97
47
|
// close rightmost open group
|
|
98
|
-
if (
|
|
99
|
-
|
|
100
|
-
|
|
48
|
+
if (barItems[i].items && !barItems[i].collapsed) {
|
|
49
|
+
barItems[i].collapsed = true;
|
|
50
|
+
barItems[i].$width = div.children[i].offsetWidth;
|
|
101
51
|
// check after dom update, maybe we need to close more
|
|
102
52
|
tick().then(processOverflow);
|
|
103
53
|
|
|
@@ -109,52 +59,98 @@
|
|
|
109
59
|
}
|
|
110
60
|
|
|
111
61
|
function expandGroups(freeSpace) {
|
|
112
|
-
for (let i = 0; i <
|
|
113
|
-
//
|
|
114
|
-
if (
|
|
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) {
|
|
115
65
|
// check if group can fit in free space
|
|
116
66
|
if (
|
|
117
|
-
|
|
67
|
+
barItems[i].$width - div.children[i].offsetWidth <
|
|
118
68
|
freeSpace + 10
|
|
119
69
|
) {
|
|
120
|
-
|
|
70
|
+
barItems[i].collapsed = false;
|
|
71
|
+
// items are not deep reactive, so we need to trigger the update
|
|
72
|
+
items = [...items];
|
|
121
73
|
// check after dom update, maybe we can open one more
|
|
122
74
|
tick().then(processOverflow);
|
|
123
75
|
}
|
|
124
|
-
|
|
125
|
-
items = [...items];
|
|
126
76
|
return;
|
|
127
77
|
}
|
|
128
78
|
}
|
|
129
79
|
}
|
|
130
80
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
81
|
+
function processOverflow() {
|
|
82
|
+
if (overflow === "wrap") return;
|
|
83
|
+
|
|
84
|
+
const nodes = div.children;
|
|
85
|
+
// restore all items so widths can be measured
|
|
86
|
+
for (let i = 0; i < barItems.length; i++) {
|
|
87
|
+
if (nodes[i]) nodes[i].style.display = "";
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const visibleWidth = div.clientWidth;
|
|
91
|
+
const fullWidth = div.scrollWidth;
|
|
92
|
+
const needMenu = fullWidth > visibleWidth;
|
|
93
|
+
|
|
94
|
+
if (needMenu) {
|
|
95
|
+
if (overflow === "collapse") return collapseGroups();
|
|
96
|
+
|
|
97
|
+
// pinned items always stay visible
|
|
98
|
+
let pinnedWidth = 0;
|
|
99
|
+
for (let i = 0; i < barItems.length; i++) {
|
|
100
|
+
if (barItems[i].pinned) pinnedWidth += nodes[i].clientWidth;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
let sum = 0;
|
|
104
|
+
for (let i = 0; i < barItems.length; i++) {
|
|
105
|
+
if (barItems[i].pinned) continue;
|
|
106
|
+
sum += nodes[i].clientWidth;
|
|
107
|
+
if (barItems[i].comp == "separator") sum += 8;
|
|
108
|
+
if (sum > visibleWidth - 40 - pinnedWidth) {
|
|
109
|
+
// we need to hide nodes[i] and all next non-pinned nodes
|
|
110
|
+
menuItems = [];
|
|
111
|
+
for (let j = i; j < barItems.length; j++) {
|
|
112
|
+
if (barItems[j].pinned) continue;
|
|
113
|
+
menuItems.push(barItems[j]);
|
|
114
|
+
nodes[j].style.display = "none";
|
|
115
|
+
}
|
|
116
|
+
// hide the ending separator
|
|
117
|
+
if (
|
|
118
|
+
i > 0 &&
|
|
119
|
+
barItems[i - 1].comp == "separator" &&
|
|
120
|
+
!barItems[i - 1].pinned
|
|
121
|
+
) {
|
|
122
|
+
nodes[i - 1].style.display = "none";
|
|
123
|
+
}
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
} else {
|
|
128
|
+
const freeWidth = visibleWidth - getTotalWidth();
|
|
129
|
+
if (freeWidth <= 0) return;
|
|
130
|
+
if (overflow === "collapse") return expandGroups(freeWidth);
|
|
131
|
+
|
|
132
|
+
if (menuItems.length) menuItems = [];
|
|
133
|
+
}
|
|
137
134
|
}
|
|
138
135
|
|
|
139
136
|
onMount(() => {
|
|
140
|
-
const resizeObserver = new ResizeObserver(() => processOverflow(
|
|
137
|
+
const resizeObserver = new ResizeObserver(() => processOverflow());
|
|
141
138
|
resizeObserver.observe(div);
|
|
142
139
|
|
|
143
140
|
return () => {
|
|
144
|
-
|
|
141
|
+
resizeObserver.unobserve(div);
|
|
145
142
|
};
|
|
146
143
|
});
|
|
147
|
-
|
|
148
|
-
const visibleItems = $derived(normalize(items));
|
|
149
144
|
</script>
|
|
150
145
|
|
|
151
146
|
<div
|
|
152
147
|
class="wx-toolbar {css}"
|
|
153
148
|
class:wx-wrap={overflow === "wrap"}
|
|
154
149
|
class:wx-column={layout == "column"}
|
|
150
|
+
class:wx-has-menu={menuItems.length}
|
|
155
151
|
bind:this={div}
|
|
156
152
|
>
|
|
157
|
-
{#each
|
|
153
|
+
{#each barItems as item}
|
|
158
154
|
{#if item.items}
|
|
159
155
|
<Group {item} {values} {onclick} onchange={handleChange} />
|
|
160
156
|
{:else}
|
|
@@ -180,6 +176,10 @@
|
|
|
180
176
|
align-items: stretch;
|
|
181
177
|
padding: 4px;
|
|
182
178
|
position: relative;
|
|
179
|
+
min-height: 48px;
|
|
180
|
+
}
|
|
181
|
+
.wx-toolbar.wx-has-menu {
|
|
182
|
+
padding-right: 48px;
|
|
183
183
|
}
|
|
184
184
|
.wx-toolbar.wx-wrap {
|
|
185
185
|
flex-wrap: wrap;
|
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,14 +5,19 @@ 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;
|
|
14
18
|
layout?: "column";
|
|
15
19
|
items?: IToolbarItem[];
|
|
20
|
+
pinned?: boolean;
|
|
16
21
|
[key: string]: any;
|
|
17
22
|
}
|
|
18
23
|
|
package/whatsnew.md
CHANGED
|
@@ -1,8 +1,19 @@
|
|
|
1
|
-
## 2.
|
|
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
|
+
|
|
11
|
+
## 2.5.3
|
|
2
12
|
|
|
3
13
|
### Fixes
|
|
4
14
|
|
|
5
15
|
- Impossible to arrange first level items as a column
|
|
16
|
+
- Impossible to fix certain items as always visible
|
|
6
17
|
|
|
7
18
|
## 2.5.1
|
|
8
19
|
|