@svar-ui/svelte-toolbar 2.6.1 → 2.6.2

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.
@@ -1,18 +1,15 @@
1
- <script>
2
- import { Button } from "@svar-ui/svelte-core";
3
-
4
- let { css, disabled, options, onchange, value = $bindable() } = $props();
5
-
6
- function onClickHandler(id) {
7
- value = id;
8
- onchange && onchange({ value: id });
9
- }
1
+ <script lang="ts">import { Button } from "@svar-ui/svelte-core";
2
+ let { css = "", disabled, options = [], onchange, value = $bindable() } = $props();
3
+ function onClickHandler(id) {
4
+ value = id;
5
+ onchange?.({ value: id });
6
+ }
10
7
  </script>
11
8
 
12
9
  <div class="wx-button-list {css}">
13
10
  {#each options as option}
14
11
  <Button
15
- type={value === option.id ? "primary" : ""}
12
+ type={value === option.id ? "primary" : undefined}
16
13
  {disabled}
17
14
  text={option.label}
18
15
  onclick={() => onClickHandler(option.id)}
@@ -0,0 +1,13 @@
1
+ import type { IButtonListOption, TID } from "../types";
2
+ type Props = {
3
+ options?: IButtonListOption[];
4
+ value?: TID;
5
+ css?: string;
6
+ disabled?: boolean;
7
+ onchange?: (ev: {
8
+ value: TID;
9
+ }) => void;
10
+ };
11
+ declare const ButtonList: import("svelte").Component<Props, {}, "value">;
12
+ type ButtonList = ReturnType<typeof ButtonList>;
13
+ export default ButtonList;
@@ -1,14 +1,11 @@
1
- <script>
2
- import { Button } from "@svar-ui/svelte-core";
3
-
4
- let { icon, title, text, tooltip, css, type, disabled, menu, onclick } =
5
- $props();
1
+ <script lang="ts">import { Button } from "@svar-ui/svelte-core";
2
+ let { icon, title, text, tooltip, css, type, disabled, menu, onclick } = $props();
6
3
  </script>
7
4
 
8
5
  {#if menu}
9
6
  <!-- svelte-ignore a11y_click_events_have_key_events -->
10
7
  <!-- svelte-ignore a11y_no_static_element_interactions -->
11
- <div class="wx-item" class:wx-text-icon={text} {onclick}>
8
+ <div class="wx-item" class:wx-text-icon={!!text} {onclick}>
12
9
  <i class="{icon || 'wxi-empty'} {css || ''}"></i>
13
10
  {text}
14
11
  </div>
@@ -0,0 +1,16 @@
1
+ import type { ComponentProps } from "svelte";
2
+ import { Button } from "@svar-ui/svelte-core";
3
+ type Props = {
4
+ icon?: string;
5
+ title?: string;
6
+ text?: string;
7
+ tooltip?: string;
8
+ css?: string;
9
+ type?: ComponentProps<typeof Button>["type"];
10
+ disabled?: boolean;
11
+ menu?: boolean;
12
+ onclick?: (ev: MouseEvent) => void;
13
+ };
14
+ declare const Icon: import("svelte").Component<Props, {}, "">;
15
+ type Icon = ReturnType<typeof Icon>;
16
+ export default Icon;
@@ -1,9 +1,8 @@
1
- <script>
2
- let { id = "", text = "", css = "", icon = "", onclick } = $props();
3
-
4
- function handleClick() {
5
- onclick && onclick({ id });
6
- }
1
+ <script lang="ts">let { id = "", text = "", css = "", icon = "", onclick } = $props();
2
+ function handleClick() {
3
+ onclick?.({ id });
4
+ }
5
+ export {};
7
6
  </script>
8
7
 
9
8
  <!-- svelte-ignore a11y_click_events_have_key_events -->
@@ -0,0 +1,13 @@
1
+ import type { TID } from "../types";
2
+ type Props = {
3
+ id?: TID;
4
+ text?: string;
5
+ css?: string;
6
+ icon?: string;
7
+ onclick?: (ev: {
8
+ id: TID;
9
+ }) => void;
10
+ };
11
+ declare const Item: import("svelte").Component<Props, {}, "">;
12
+ type Item = ReturnType<typeof Item>;
13
+ export default Item;
@@ -1,5 +1,5 @@
1
- <script>
2
- const { text, value, children } = $props();
1
+ <script lang="ts">const { text, value, children } = $props();
2
+ export {};
3
3
  </script>
4
4
 
5
5
  {#if children}
@@ -0,0 +1,9 @@
1
+ import type { Snippet } from "svelte";
2
+ type Props = {
3
+ text?: string;
4
+ value?: any;
5
+ children?: Snippet;
6
+ };
7
+ declare const Label: import("svelte").Component<Props, {}, "">;
8
+ type Label = ReturnType<typeof Label>;
9
+ export default Label;
@@ -1,5 +1,4 @@
1
- <script>
2
- let { menu = false } = $props();
1
+ <script lang="ts">let { menu = false } = $props();
3
2
  </script>
4
3
 
5
4
  <div class="wx-separator{menu ? '-menu' : ''}">&nbsp;</div>
@@ -0,0 +1,6 @@
1
+ type Props = {
2
+ menu?: boolean;
3
+ };
4
+ declare const Separator: import("svelte").Component<Props, {}, "">;
5
+ type Separator = ReturnType<typeof Separator>;
6
+ export default Separator;
@@ -0,0 +1,26 @@
1
+ export default Spacer;
2
+ type Spacer = SvelteComponent<{
3
+ [x: string]: never;
4
+ }, {
5
+ [evt: string]: CustomEvent<any>;
6
+ }, {}> & {
7
+ $$bindings?: string | undefined;
8
+ };
9
+ declare const Spacer: $$__sveltets_2_IsomorphicComponent<{
10
+ [x: string]: never;
11
+ }, {
12
+ [evt: string]: CustomEvent<any>;
13
+ }, {}, {}, string>;
14
+ interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
15
+ new (options: import("svelte").ComponentConstructorOptions<Props>): import("svelte").SvelteComponent<Props, Events, Slots> & {
16
+ $$bindings?: Bindings;
17
+ } & Exports;
18
+ (internal: unknown, props: {
19
+ $$events?: Events;
20
+ $$slots?: Slots;
21
+ }): Exports & {
22
+ $set?: any;
23
+ $on?: any;
24
+ };
25
+ z_$$bindings?: Bindings;
26
+ }
@@ -1,24 +1,22 @@
1
- <script>
2
- import { setID } from "@svar-ui/lib-dom";
3
- import { getItemHandler } from "../helpers";
4
- import Separator from "../buttons/Separator.svelte";
5
- import Spacer from "../buttons/Spacer.svelte";
6
-
7
- let { item = {}, menu = false, values, onclick, onchange } = $props();
8
- let itemComponent = $derived(getItemHandler(item.comp, { menu }));
9
-
10
- function onClick() {
11
- if (item.handler) item.handler(item);
12
- onclick && onclick({ item });
13
- }
14
-
15
- let value = $derived(item.key ? values[item.key] : undefined);
16
- function detectChange({ value }) {
17
- if (item.handler) item.handler(item, value);
18
- onchange && onchange({ value, item });
19
- }
20
-
21
- const text = $derived(menu ? item.menuText || item.text : item.text);
1
+ <script lang="ts">import { setID } from "@svar-ui/lib-dom";
2
+ import { getItemHandler } from "../helpers";
3
+ import Separator from "../buttons/Separator.svelte";
4
+ import Spacer from "../buttons/Spacer.svelte";
5
+ let { item = {}, menu = false, values, onclick, onchange } = $props();
6
+ let itemComponent = $derived(getItemHandler(item.comp, { menu }));
7
+ function onClick() {
8
+ if (item.handler) item.handler(item);
9
+ onclick?.({ item });
10
+ }
11
+ let value = $derived(item.key ? values?.[item.key] : undefined);
12
+ function detectChange({ value }) {
13
+ if (item.handler) item.handler(item, value);
14
+ onchange?.({
15
+ value,
16
+ item
17
+ });
18
+ }
19
+ const text = $derived(menu ? item.menuText || item.text : item.text);
22
20
  </script>
23
21
 
24
22
  {#if item.comp == "spacer"}
@@ -31,7 +29,7 @@
31
29
  class="wx-tb-element {item.css || ''}"
32
30
  class:wx-spacer={item.spacer}
33
31
  class:wx-menu={menu}
34
- data-id={setID(item.id)}
32
+ data-id={setID(item.id as TID)}
35
33
  >
36
34
  <SvelteComponent
37
35
  {value}
@@ -0,0 +1,11 @@
1
+ import type { IToolbarChangeEvent, IToolbarClickEvent, IToolbarItem, TToolbarValues } from "../types";
2
+ type Props = {
3
+ item?: IToolbarItem;
4
+ menu?: boolean;
5
+ values?: TToolbarValues | null;
6
+ onclick?: (ev: IToolbarClickEvent) => void;
7
+ onchange?: (ev: IToolbarChangeEvent) => void;
8
+ };
9
+ declare const BarComponent: import("svelte").Component<Props, {}, "">;
10
+ type BarComponent = ReturnType<typeof BarComponent>;
11
+ export default BarComponent;
@@ -1,21 +1,15 @@
1
- <script>
2
- import Group from "./Group.svelte";
3
-
4
- import BarComponent from "./BarComponent.svelte";
5
- import { Dropdown } from "@svar-ui/svelte-core";
6
- import { collapseSeparators } from "../helpers";
7
-
8
- let { item, values = null, menu = false, onchange, onclick } = $props();
9
-
10
- let collapsedState = $state(true);
11
-
12
- const onClick = ev => {
13
- oncancel();
14
- onclick && onclick(ev);
15
- };
16
-
17
- const show = () => (collapsedState = false);
18
- const oncancel = () => (collapsedState = true);
1
+ <script lang="ts">import Group from "./Group.svelte";
2
+ import BarComponent from "./BarComponent.svelte";
3
+ import { Dropdown } from "@svar-ui/svelte-core";
4
+ import { collapseSeparators } from "../helpers";
5
+ let { item, values = null, menu = false, onchange, onclick } = $props();
6
+ let collapsedState = $state(true);
7
+ const onClick = (ev) => {
8
+ oncancel();
9
+ onclick?.(ev);
10
+ };
11
+ const show = () => collapsedState = false;
12
+ const oncancel = () => collapsedState = true;
19
13
  </script>
20
14
 
21
15
  <div
@@ -0,0 +1,12 @@
1
+ import Group from "./Group.svelte";
2
+ import type { IToolbarChangeEvent, IToolbarClickEvent, IToolbarItem, TToolbarValues } from "../types";
3
+ type Props = {
4
+ item: IToolbarItem;
5
+ values?: TToolbarValues | null;
6
+ menu?: boolean;
7
+ onchange?: (ev: IToolbarChangeEvent) => void;
8
+ onclick?: (ev: IToolbarClickEvent) => void;
9
+ };
10
+ declare const Group: import("svelte").Component<Props, {}, "">;
11
+ type Group = ReturnType<typeof Group>;
12
+ export default Group;
@@ -1,26 +1,21 @@
1
- <script>
2
- import BarComponent from "./BarComponent.svelte";
3
- import Group from "./Group.svelte";
4
- import { Dropdown, Button } from "@svar-ui/svelte-core";
5
-
6
- let { items = [], css, values, width, onclick, onchange } = $props();
7
-
8
- let popup = $state();
9
- let div = $state();
10
-
11
- function oncancel() {
12
- popup = null;
13
- }
14
- function showMenu() {
15
- popup = true;
16
- }
17
- function menuClick(ev) {
18
- oncancel();
19
- onclick && onclick(ev);
20
- }
1
+ <script lang="ts">import BarComponent from "./BarComponent.svelte";
2
+ import Group from "./Group.svelte";
3
+ import { Dropdown, Button } from "@svar-ui/svelte-core";
4
+ let { items = [], css, values, width, onclick, onchange } = $props();
5
+ let popup = $state(false);
6
+ function oncancel() {
7
+ popup = false;
8
+ }
9
+ function showMenu() {
10
+ popup = true;
11
+ }
12
+ function menuClick(ev) {
13
+ oncancel();
14
+ onclick?.(ev);
15
+ }
21
16
  </script>
22
17
 
23
- <div class="wx-menu {css || ''}" bind:this={div} data-id="$menu">
18
+ <div class="wx-menu {css || ''}" data-id="$menu">
24
19
  <Button icon="wxi-dots-h" onclick={showMenu} />
25
20
  {#if popup}
26
21
  <Dropdown width="{width}px" {oncancel}>
@@ -0,0 +1,12 @@
1
+ import type { IToolbarChangeEvent, IToolbarClickEvent, IToolbarItem, TToolbarValues } from "../types";
2
+ type Props = {
3
+ items?: IToolbarItem[];
4
+ css?: string;
5
+ values?: TToolbarValues | null;
6
+ width?: number;
7
+ onclick?: (ev: IToolbarClickEvent) => void;
8
+ onchange?: (ev: IToolbarChangeEvent) => void;
9
+ };
10
+ declare const Menu: import("svelte").Component<Props, {}, "">;
11
+ type Menu = ReturnType<typeof Menu>;
12
+ export default Menu;
@@ -0,0 +1,160 @@
1
+ <script lang="ts">import Menu from "./Menu.svelte";
2
+ import Group from "./Group.svelte";
3
+ import BarComponent from "./BarComponent.svelte";
4
+ import { onMount, tick } from "svelte";
5
+ import { normalizeToolbarItems } from "../helpers";
6
+ let { items = $bindable([]), menuCss = "", css = "", values = $bindable(null), overflow = "menu", layout = "row", onclick, onchange } = $props();
7
+ function handleChange(ev) {
8
+ if (values && ev.item.key) {
9
+ values[ev.item.key] = ev.value;
10
+ }
11
+ onchange?.(ev);
12
+ }
13
+ let div;
14
+ let menuItems = $state([]);
15
+ const getNodes = () => div.children;
16
+ const barItems = $derived(normalizeToolbarItems(items));
17
+ const vertical = $derived(layout === "column");
18
+ function getTotalWidth() {
19
+ const nodes = getNodes();
20
+ let sum = 0;
21
+ for (let i = 0; i < barItems.length; i++) {
22
+ if (barItems[i].comp != "spacer") {
23
+ sum += nodes[i]?.clientWidth || 0;
24
+ if (barItems[i].comp == "separator") sum += 8;
25
+ }
26
+ }
27
+ return sum;
28
+ }
29
+ function collapseGroups() {
30
+ for (let i = barItems.length - 1; i >= 0; i--) {
31
+ // close rightmost open group
32
+ if (barItems[i].items && !barItems[i].collapsed) {
33
+ barItems[i].collapsed = true;
34
+ barItems[i].$width = getNodes()[i].offsetWidth;
35
+ // check after dom update, maybe we need to close more
36
+ tick().then(processOverflow);
37
+ // items are not deep reactive, so we need to trigger the update
38
+ items = [...items];
39
+ return;
40
+ }
41
+ }
42
+ }
43
+ function expandGroups(freeSpace) {
44
+ for (let i = 0; i < barItems.length; i++) {
45
+ // open leftmost closed group that was collapsed by overflow
46
+ if (barItems[i].collapsed && barItems[i].$width) {
47
+ // check if group can fit in free space
48
+ if (barItems[i].$width - getNodes()[i].offsetWidth < freeSpace + 10) {
49
+ barItems[i].collapsed = false;
50
+ // items are not deep reactive, so we need to trigger the update
51
+ items = [...items];
52
+ // check after dom update, maybe we can open one more
53
+ tick().then(processOverflow);
54
+ }
55
+ return;
56
+ }
57
+ }
58
+ }
59
+ function processOverflow() {
60
+ if (overflow === "wrap" || vertical) return;
61
+ const nodes = getNodes();
62
+ // restore all items so widths can be measured
63
+ for (let i = 0; i < barItems.length; i++) {
64
+ if (nodes[i]) nodes[i].style.display = "";
65
+ }
66
+ const visibleWidth = div.clientWidth;
67
+ const fullWidth = div.scrollWidth;
68
+ const needMenu = fullWidth > visibleWidth;
69
+ if (needMenu) {
70
+ if (overflow === "collapse") return collapseGroups();
71
+ // pinned items always stay visible
72
+ let pinnedWidth = 0;
73
+ for (let i = 0; i < barItems.length; i++) {
74
+ if (barItems[i].pinned) pinnedWidth += nodes[i].clientWidth;
75
+ }
76
+ let sum = 0;
77
+ for (let i = 0; i < barItems.length; i++) {
78
+ if (barItems[i].pinned) continue;
79
+ sum += nodes[i].clientWidth;
80
+ if (barItems[i].comp == "separator") sum += 8;
81
+ if (sum > visibleWidth - 40 - pinnedWidth) {
82
+ // we need to hide nodes[i] and all next non-pinned nodes
83
+ menuItems = [];
84
+ for (let j = i; j < barItems.length; j++) {
85
+ if (barItems[j].pinned) continue;
86
+ menuItems.push(barItems[j]);
87
+ nodes[j].style.display = "none";
88
+ }
89
+ // hide the ending separator
90
+ if (i > 0 && barItems[i - 1].comp == "separator" && !barItems[i - 1].pinned) {
91
+ nodes[i - 1].style.display = "none";
92
+ }
93
+ break;
94
+ }
95
+ }
96
+ } else {
97
+ const freeWidth = visibleWidth - getTotalWidth();
98
+ if (freeWidth <= 0) return;
99
+ if (overflow === "collapse") return expandGroups(freeWidth);
100
+ if (menuItems.length) menuItems = [];
101
+ }
102
+ }
103
+ onMount(() => {
104
+ const resizeObserver = new ResizeObserver(() => processOverflow());
105
+ resizeObserver.observe(div);
106
+ return () => {
107
+ resizeObserver.unobserve(div);
108
+ };
109
+ });
110
+ </script>
111
+
112
+ <div
113
+ class="wx-toolbar {css}"
114
+ class:wx-wrap={overflow === "wrap"}
115
+ class:wx-column={vertical}
116
+ class:wx-has-menu={menuItems.length > 0}
117
+ bind:this={div}
118
+ >
119
+ {#each barItems as item}
120
+ {#if item.items}
121
+ <Group {item} {values} {onclick} onchange={handleChange} />
122
+ {:else}
123
+ <BarComponent {item} {values} {onclick} onchange={handleChange} />
124
+ {/if}
125
+ {/each}
126
+ {#if menuItems.length}
127
+ <Menu
128
+ items={menuItems}
129
+ css={menuCss}
130
+ {values}
131
+ {onclick}
132
+ onchange={handleChange}
133
+ />
134
+ {/if}
135
+ </div>
136
+
137
+ <style>
138
+ .wx-toolbar {
139
+ width: 100%;
140
+ flex-wrap: nowrap;
141
+ display: flex;
142
+ align-items: stretch;
143
+ padding: 4px;
144
+ position: relative;
145
+ min-height: 48px;
146
+ }
147
+ .wx-toolbar.wx-has-menu {
148
+ padding-right: 48px;
149
+ }
150
+ .wx-toolbar.wx-wrap {
151
+ flex-wrap: wrap;
152
+ }
153
+ .wx-column {
154
+ flex-flow: column;
155
+ width: auto;
156
+ }
157
+ .wx-column > :global(.wx-tb-element) {
158
+ width: 100%;
159
+ }
160
+ </style>
@@ -0,0 +1,14 @@
1
+ import type { IToolbarChangeEvent, IToolbarClickEvent, IToolbarItem, TToolbarValues } from "../types";
2
+ type Props = {
3
+ items?: IToolbarItem[];
4
+ menuCss?: string;
5
+ css?: string;
6
+ values?: TToolbarValues | null;
7
+ overflow?: "collapse" | "wrap" | "menu";
8
+ layout?: "row" | "column";
9
+ onclick?: (ev: IToolbarClickEvent) => void;
10
+ onchange?: (ev: IToolbarChangeEvent) => void;
11
+ };
12
+ declare const Toolbar: import("svelte").Component<Props, {}, "items" | "values">;
13
+ type Toolbar = ReturnType<typeof Toolbar>;
14
+ export default Toolbar;
package/dist/env.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ /// <reference types="svelte" />
2
+
3
+ declare module "*.svelte" {
4
+ import type { Component } from "svelte";
5
+ const component: Component<any>;
6
+ export default component;
7
+ }
@@ -0,0 +1,6 @@
1
+ import type { Component } from "svelte";
2
+ import type { IItemConfig, IToolbarItem } from "./types";
3
+ export declare function getItemHandler(type: string | Component<any> | undefined, config?: IItemConfig | null): Component<any>;
4
+ export declare function registerToolbarItem(type: string, handler: Component<any>, config?: IItemConfig | null): void;
5
+ export declare function normalizeToolbarItems(items?: IToolbarItem[]): IToolbarItem[];
6
+ export declare function collapseSeparators(items?: IToolbarItem[]): IToolbarItem[];
@@ -0,0 +1,41 @@
1
+ import { uid } from "@svar-ui/lib-dom";
2
+ const handlers = {};
3
+ export function getItemHandler(type, config = null) {
4
+ // try to get the handler for the config, fallback to the type
5
+ // so we need not register all button types for alternative modes
6
+ const c = typeof type === "string"
7
+ ? handlers[getID(type, config)] || handlers[type]
8
+ : type;
9
+ return c || handlers["label"];
10
+ }
11
+ export function registerToolbarItem(type, handler, config = null) {
12
+ handlers[getID(type, config)] = handler;
13
+ }
14
+ // Assign stable ids and collapse consecutive separators
15
+ export function normalizeToolbarItems(items) {
16
+ if (!items?.length)
17
+ return [];
18
+ for (const item of items) {
19
+ if (!item.id)
20
+ item.id = uid();
21
+ }
22
+ return collapseSeparators(items);
23
+ }
24
+ // Keep the first of consecutive separator
25
+ export function collapseSeparators(items) {
26
+ if (!items?.length)
27
+ return items || [];
28
+ const out = [];
29
+ let prevWasSeparator = false;
30
+ for (const item of items) {
31
+ const isSeparator = item.comp === "separator";
32
+ if (isSeparator && prevWasSeparator)
33
+ continue;
34
+ out.push(item);
35
+ prevWasSeparator = isSeparator;
36
+ }
37
+ return out;
38
+ }
39
+ function getID(type, config = null) {
40
+ return `${type}${config?.menu ? ":menu" : ""}`;
41
+ }
@@ -0,0 +1,5 @@
1
+ import Toolbar from "./components/Toolbar.svelte";
2
+ import { registerToolbarItem } from "./helpers";
3
+ import ButtonList from "./buttons/ButtonList.svelte";
4
+ export { Toolbar, ButtonList, registerToolbarItem };
5
+ export type { TID, IToolbarItem, TToolbarValues, IToolbarClickEvent, IToolbarChangeEvent, IButtonListOption, IItemConfig, } from "./types";
@@ -1,14 +1,12 @@
1
1
  import { Button } from "@svar-ui/svelte-core";
2
2
  import Toolbar from "./components/Toolbar.svelte";
3
- import { registerToolbarItem } from "./helpers.js";
4
-
3
+ import { registerToolbarItem } from "./helpers";
5
4
  import Separator from "./buttons/Separator.svelte";
6
5
  import Spacer from "./buttons/Spacer.svelte";
7
6
  import ButtonList from "./buttons/ButtonList.svelte";
8
7
  import Label from "./buttons/Label.svelte";
9
8
  import Icon from "./buttons/Icon.svelte";
10
9
  import Item from "./buttons/Item.svelte";
11
-
12
10
  registerToolbarItem("button", Button);
13
11
  registerToolbarItem("button", Icon, { menu: true });
14
12
  registerToolbarItem("separator", Separator);
@@ -16,5 +14,4 @@ registerToolbarItem("spacer", Spacer);
16
14
  registerToolbarItem("label", Label);
17
15
  registerToolbarItem("item", Item);
18
16
  registerToolbarItem("icon", Icon);
19
-
20
17
  export { Toolbar, ButtonList, registerToolbarItem };
@@ -0,0 +1,39 @@
1
+ import type { Component } from "svelte";
2
+ export type TID = string | number;
3
+ export interface IToolbarItem {
4
+ id?: TID;
5
+ comp?: string | Component<any>;
6
+ icon?: string;
7
+ css?: string;
8
+ type?: string;
9
+ title?: string;
10
+ text?: string;
11
+ tooltip?: string;
12
+ menuText?: string;
13
+ key?: string;
14
+ disabled?: boolean;
15
+ spacer?: boolean;
16
+ collapsed?: boolean;
17
+ handler?: (item: IToolbarItem, value?: any) => void;
18
+ layout?: "column";
19
+ items?: IToolbarItem[];
20
+ pinned?: boolean;
21
+ [key: string]: any;
22
+ }
23
+ export type TToolbarValues = {
24
+ [key: string]: any;
25
+ };
26
+ export interface IToolbarClickEvent {
27
+ item: IToolbarItem;
28
+ }
29
+ export interface IToolbarChangeEvent {
30
+ value: any;
31
+ item: IToolbarItem;
32
+ }
33
+ export interface IButtonListOption {
34
+ id: TID;
35
+ label: string;
36
+ }
37
+ export interface IItemConfig {
38
+ menu?: boolean;
39
+ }
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,54 +1,57 @@
1
1
  {
2
- "name": "@svar-ui/svelte-toolbar",
3
- "version": "2.6.1",
4
- "description": "Svelte toolbar component. Lightweight, customizable, easy-to-use.",
5
- "productTag": "toolbar",
6
- "productTrial": false,
7
- "type": "module",
8
- "scripts": {
9
- "build": "vite build",
10
- "build:dist": "vite build --mode dist",
11
- "build:tests": "vite build --mode test",
12
- "lint": "yarn eslint ./demos ./src",
13
- "start": "vite --open",
14
- "start:tests": "vite --open=/tests/ --host 0.0.0.0 --port 5100 --mode test",
15
- "test": "true",
16
- "test:cypress": "cypress run -P ./ --config \"baseUrl=http://localhost:5100/tests\""
17
- },
18
- "svelte": "src/index.js",
19
- "exports": {
20
- ".": {
21
- "svelte": "./src/index.js",
22
- "types": "./types/index.d.ts"
23
- },
24
- "./package.json": "./package.json"
25
- },
26
- "license": "MIT",
27
- "repository": {
28
- "type": "git",
29
- "url": "https://github.com/svar-widgets/toolbar.git"
30
- },
31
- "bugs": {
32
- "url": "https://forum.svar.dev"
33
- },
34
- "homepage": "https://svar.dev/svelte/core/",
35
- "dependencies": {
36
- "@svar-ui/svelte-core": "2.6.1",
37
- "@svar-ui/lib-dom": "0.14.0"
38
- },
39
- "files": [
40
- "src",
41
- "types",
42
- "readme.md",
43
- "whatsnew.md",
44
- "license.txt"
45
- ],
46
- "keywords": [
47
- "svelte",
48
- "svelte toolbar",
49
- "toolbar",
50
- "toolbar component",
51
- "buttons panel",
52
- "svar widgets"
53
- ]
54
- }
2
+ "name": "@svar-ui/svelte-toolbar",
3
+ "version": "2.6.2",
4
+ "description": "Svelte toolbar component. Lightweight, customizable, easy-to-use.",
5
+ "keywords": [
6
+ "buttons panel",
7
+ "svar widgets",
8
+ "svelte",
9
+ "svelte toolbar",
10
+ "toolbar",
11
+ "toolbar component"
12
+ ],
13
+ "homepage": "https://svar.dev/svelte/core/",
14
+ "bugs": {
15
+ "url": "https://forum.svar.dev"
16
+ },
17
+ "license": "MIT",
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "https://github.com/svar-widgets/toolbar.git"
21
+ },
22
+ "files": [
23
+ "dist",
24
+ "readme.md",
25
+ "whatsnew.md",
26
+ "license.txt"
27
+ ],
28
+ "type": "module",
29
+ "types": "./dist/index.d.ts",
30
+ "svelte": "./dist/index.js",
31
+ "exports": {
32
+ ".": {
33
+ "svelte": "./dist/index.js",
34
+ "types": "./dist/index.d.ts"
35
+ },
36
+ "./package.json": "./package.json"
37
+ },
38
+ "dependencies": {
39
+ "@svar-ui/lib-dom": "0.14.2",
40
+ "@svar-ui/svelte-core": "2.6.2"
41
+ },
42
+ "devDependencies": {
43
+ "@sveltejs/package": "^2.5.7",
44
+ "@sveltejs/vite-plugin-svelte": "^7.1.2",
45
+ "svelte": "5.56.9",
46
+ "svelte-check": "^4.4.6"
47
+ },
48
+ "peerDependencies": {
49
+ "svelte": "^5.0.0"
50
+ },
51
+ "productTag": "toolbar",
52
+ "scripts": {
53
+ "check": "svelte-check --tsconfig ./tsconfig.json",
54
+ "build": "svelte-package -i ./src",
55
+ "dev": "svelte-package -i ./src --watch"
56
+ }
57
+ }
package/readme.md CHANGED
@@ -1,5 +1,5 @@
1
1
  <div align="center">
2
-
2
+
3
3
  # SVAR Svelte Toolbar
4
4
 
5
5
  [![npm](https://img.shields.io/npm/v/@svar-ui/svelte-toolbar.svg)](https://www.npmjs.com/package/@svar-ui/svelte-toolbar)
@@ -46,21 +46,10 @@ To use the widget, simply import the package and include the component in your S
46
46
 
47
47
  Typically, you don't need to modify the code. However, if you wish to do so, follow these steps:
48
48
 
49
- 1. Run `yarn` to install dependencies. Note that this project is a monorepo using `yarn` workspaces, so npm will not work
50
- 2. Start the project in development mode with `yarn start`
51
-
52
- ### Run Tests
53
-
54
- To run the test:
55
-
56
- 1. Start the test examples with:
57
- ```sh
58
- yarn start:tests
59
- ```
60
- 2. In a separate console, run the end-to-end tests with:
61
- ```sh
62
- yarn test:cypress
63
- ```
49
+ 1. Install [vite-plus](https://vite.plus) (`curl -fsSL https://vite.plus | bash` on Mac/Linux, `irm https://vite.plus/ps1 | iex` on Windows). The project uses `pnpm` workspaces under the hood, so plain `npm` will not work.
50
+ 2. Run `vp i` from the project root to install dependencies.
51
+ 3. Run `vp run build` to build all packages.
52
+ 4. Start the demo app in development mode with `vp run start`.
64
53
 
65
54
  ### Need help?
66
55
 
package/whatsnew.md CHANGED
@@ -1,117 +1,129 @@
1
+ ## 2.6.2
2
+
3
+ ### Updates
4
+
5
+ - Native TypeScript support with types generated from the component sources
6
+
7
+ ### Fixes
8
+
9
+ - An item with key throws an error when the Toolbar has no values
10
+ - Changing an item without key writes `values["undefined"]`
11
+ - ButtonList without css gets an undefined CSS class
12
+
1
13
  ## 2.6.1
2
14
 
3
15
  ### Updates
4
16
 
5
- - Impossible to render an item differently in the `menu` mode
6
- - Option based items are not usable in the `menu` mode
17
+ - Impossible to render an item differently in the `menu` mode
18
+ - Option based items are not usable in the `menu` mode
7
19
 
8
20
  ## 2.6.0
9
21
 
10
22
  ### New features
11
23
 
12
- - Tooltip for toolbar items
24
+ - Tooltip for toolbar items
13
25
 
14
26
  ### Fixes
15
27
 
16
- - Duplicate separators are auto removed
28
+ - Duplicate separators are auto removed
17
29
 
18
30
  ## 2.5.3
19
31
 
20
32
  ### Fixes
21
33
 
22
- - Impossible to arrange first level items as a column
23
- - Impossible to fix certain items as always visible
34
+ - Impossible to arrange first level items as a column
35
+ - Impossible to fix certain items as always visible
24
36
 
25
37
  ## 2.5.1
26
38
 
27
- - Using Svelte Core v2.5.1
39
+ - Using Svelte Core v2.5.1
28
40
 
29
41
  ## 2.5.0
30
42
 
31
- - Using Svelte Core v2.5.0
43
+ - Using Svelte Core v2.5.0
32
44
 
33
45
  ## 2.4.1
34
46
 
35
- - Using Svelte Core v2.4.1
47
+ - Using Svelte Core v2.4.1
36
48
 
37
49
  ## 2.4.0
38
50
 
39
- - Using Svelte Core v2.4.0
51
+ - Using Svelte Core v2.4.0
40
52
 
41
53
  ## 2.3.0
42
54
 
43
55
  ### New features
44
56
 
45
- - TypeScript definitions
57
+ - TypeScript definitions
46
58
 
47
59
  ### Fixes
48
60
 
49
- - item `menuText` property is not applied
61
+ - item `menuText` property is not applied
50
62
 
51
63
  ## 2.2.1
52
64
 
53
65
  ### Fixes
54
66
 
55
- - Using in Salesforce environment
67
+ - Using in Salesforce environment
56
68
 
57
69
  ## 2.2.0
58
70
 
59
- - Using core@2.2.0
71
+ - Using core@2.2.0
60
72
 
61
73
  ## 2.1.0
62
74
 
63
- - Using core@2.1.0
75
+ - Using core@2.1.0
64
76
 
65
77
  ## 2.0.1
66
78
 
67
79
  ### New features
68
80
 
69
- - Svelte 5 support
81
+ - Svelte 5 support
70
82
 
71
83
  ## 1.3.0
72
84
 
73
- - Using core@1.3.0
85
+ - Using core@1.3.0
74
86
 
75
87
  ## 1.2.3
76
88
 
77
89
  ### Fixes
78
90
 
79
- - Regression in overflow menu closing
80
- - Regression in dropdown rendering
91
+ - Regression in overflow menu closing
92
+ - Regression in dropdown rendering
81
93
 
82
94
  ## 1.2.1
83
95
 
84
- - Using core@1.2.0
96
+ - Using core@1.2.0
85
97
 
86
98
  ## 1.2.1
87
99
 
88
100
  ### New features
89
101
 
90
- - Labels can be bound to data
102
+ - Labels can be bound to data
91
103
 
92
104
  ## 1.2.0
93
105
 
94
106
  ### New features
95
107
 
96
- - Extra demo for the HeaderMenu scenario
97
- - Ability to use `spacer` on toolbar items
98
- - Ability to use `item` subtype for the drop-down menus
108
+ - Extra demo for the HeaderMenu scenario
109
+ - Ability to use `spacer` on toolbar items
110
+ - Ability to use `item` subtype for the drop-down menus
99
111
 
100
112
  ### Fixes
101
113
 
102
- - Separator rendering in drop-down menus
103
- - Names of css classes corrected
114
+ - Separator rendering in drop-down menus
115
+ - Names of css classes corrected
104
116
 
105
117
  ## 1.1.2
106
118
 
107
119
  ### Updates
108
120
 
109
- - Inner css classes renamed, "wx" prefix added
121
+ - Inner css classes renamed, "wx" prefix added
110
122
 
111
123
  ## 1.1.1
112
124
 
113
- - Migrated to vite based toolchain
125
+ - Migrated to vite based toolchain
114
126
 
115
127
  ### 1.1.0
116
128
 
117
- - Initial version
129
+ - Initial version
@@ -1,195 +0,0 @@
1
- <script>
2
- import Menu from "./Menu.svelte";
3
- import Group from "./Group.svelte";
4
- import BarComponent from "./BarComponent.svelte";
5
-
6
- import { onMount, tick } from "svelte";
7
- import { normalizeToolbarItems } from "../helpers";
8
-
9
- let {
10
- items = $bindable([]),
11
- menuCss = "",
12
- css = "",
13
- values = $bindable(null),
14
- overflow = "menu",
15
- layout = "row",
16
- onclick,
17
- onchange,
18
- } = $props();
19
-
20
- function handleChange(ev) {
21
- if (values) {
22
- values[ev.item.key] = ev.value;
23
- values = values;
24
- }
25
- onchange && onchange(ev);
26
- }
27
-
28
- let div = null;
29
- let menuItems = $state([]);
30
-
31
- const barItems = $derived(normalizeToolbarItems(items));
32
- const vertical = $derived(layout === "column");
33
-
34
- function getTotalWidth() {
35
- const nodes = div.children;
36
- let sum = 0;
37
- for (let i = 0; i < barItems.length; i++) {
38
- if (barItems[i].comp != "spacer") {
39
- sum += nodes[i]?.clientWidth || 0;
40
- if (barItems[i].comp == "separator") sum += 8;
41
- }
42
- }
43
- return sum;
44
- }
45
-
46
- function collapseGroups() {
47
- for (let i = barItems.length - 1; i >= 0; i--) {
48
- // close rightmost open group
49
- if (barItems[i].items && !barItems[i].collapsed) {
50
- barItems[i].collapsed = true;
51
- barItems[i].$width = div.children[i].offsetWidth;
52
- // check after dom update, maybe we need to close more
53
- tick().then(processOverflow);
54
-
55
- // items are not deep reactive, so we need to trigger the update
56
- items = [...items];
57
- return;
58
- }
59
- }
60
- }
61
-
62
- function expandGroups(freeSpace) {
63
- for (let i = 0; i < barItems.length; i++) {
64
- // open leftmost closed group that was collapsed by overflow
65
- if (barItems[i].collapsed && barItems[i].$width) {
66
- // check if group can fit in free space
67
- if (
68
- barItems[i].$width - div.children[i].offsetWidth <
69
- freeSpace + 10
70
- ) {
71
- barItems[i].collapsed = false;
72
- // items are not deep reactive, so we need to trigger the update
73
- items = [...items];
74
- // check after dom update, maybe we can open one more
75
- tick().then(processOverflow);
76
- }
77
- return;
78
- }
79
- }
80
- }
81
-
82
- function processOverflow() {
83
- if (overflow === "wrap" || vertical) return;
84
-
85
- const nodes = div.children;
86
- // restore all items so widths can be measured
87
- for (let i = 0; i < barItems.length; i++) {
88
- if (nodes[i]) nodes[i].style.display = "";
89
- }
90
-
91
- const visibleWidth = div.clientWidth;
92
- const fullWidth = div.scrollWidth;
93
- const needMenu = fullWidth > visibleWidth;
94
-
95
- if (needMenu) {
96
- if (overflow === "collapse") return collapseGroups();
97
-
98
- // pinned items always stay visible
99
- let pinnedWidth = 0;
100
- for (let i = 0; i < barItems.length; i++) {
101
- if (barItems[i].pinned) pinnedWidth += nodes[i].clientWidth;
102
- }
103
-
104
- let sum = 0;
105
- for (let i = 0; i < barItems.length; i++) {
106
- if (barItems[i].pinned) continue;
107
- sum += nodes[i].clientWidth;
108
- if (barItems[i].comp == "separator") sum += 8;
109
- if (sum > visibleWidth - 40 - pinnedWidth) {
110
- // we need to hide nodes[i] and all next non-pinned nodes
111
- menuItems = [];
112
- for (let j = i; j < barItems.length; j++) {
113
- if (barItems[j].pinned) continue;
114
- menuItems.push(barItems[j]);
115
- nodes[j].style.display = "none";
116
- }
117
- // hide the ending separator
118
- if (
119
- i > 0 &&
120
- barItems[i - 1].comp == "separator" &&
121
- !barItems[i - 1].pinned
122
- ) {
123
- nodes[i - 1].style.display = "none";
124
- }
125
- break;
126
- }
127
- }
128
- } else {
129
- const freeWidth = visibleWidth - getTotalWidth();
130
- if (freeWidth <= 0) return;
131
- if (overflow === "collapse") return expandGroups(freeWidth);
132
-
133
- if (menuItems.length) menuItems = [];
134
- }
135
- }
136
-
137
- onMount(() => {
138
- const resizeObserver = new ResizeObserver(() => processOverflow());
139
- resizeObserver.observe(div);
140
-
141
- return () => {
142
- resizeObserver.unobserve(div);
143
- };
144
- });
145
- </script>
146
-
147
- <div
148
- class="wx-toolbar {css}"
149
- class:wx-wrap={overflow === "wrap"}
150
- class:wx-column={vertical}
151
- class:wx-has-menu={menuItems.length}
152
- bind:this={div}
153
- >
154
- {#each barItems as item}
155
- {#if item.items}
156
- <Group {item} {values} {onclick} onchange={handleChange} />
157
- {:else}
158
- <BarComponent {item} {values} {onclick} onchange={handleChange} />
159
- {/if}
160
- {/each}
161
- {#if menuItems.length}
162
- <Menu
163
- items={menuItems}
164
- css={menuCss}
165
- {values}
166
- {onclick}
167
- onchange={handleChange}
168
- />
169
- {/if}
170
- </div>
171
-
172
- <style>
173
- .wx-toolbar {
174
- width: 100%;
175
- flex-wrap: nowrap;
176
- display: flex;
177
- align-items: stretch;
178
- padding: 4px;
179
- position: relative;
180
- min-height: 48px;
181
- }
182
- .wx-toolbar.wx-has-menu {
183
- padding-right: 48px;
184
- }
185
- .wx-toolbar.wx-wrap {
186
- flex-wrap: wrap;
187
- }
188
- .wx-column {
189
- flex-flow: column;
190
- width: auto;
191
- }
192
- .wx-column :global(.wx-tb-element) {
193
- width: 100%;
194
- }
195
- </style>
package/src/helpers.js DELETED
@@ -1,42 +0,0 @@
1
- import { uid } from "@svar-ui/lib-dom";
2
-
3
- const handlers = {};
4
- export function getItemHandler(type, config = null) {
5
- // try to get the handler for the config, fallback to the type
6
- // so we need not register all button types for alternative modes
7
- const c =
8
- typeof type === "string"
9
- ? handlers[getID(type, config)] || handlers[type]
10
- : type;
11
- return c || handlers["label"];
12
- }
13
- export function registerToolbarItem(type, handler, config = null) {
14
- handlers[getID(type, config)] = handler;
15
- }
16
-
17
- // Assign stable ids and collapse consecutive separators
18
- export function normalizeToolbarItems(items) {
19
- if (!items?.length) return [];
20
- for (const item of items) {
21
- if (!item.id) item.id = uid();
22
- }
23
- return collapseSeparators(items);
24
- }
25
-
26
- // Keep the first of consecutive separator
27
- export function collapseSeparators(items) {
28
- if (!items?.length) return items || [];
29
- const out = [];
30
- let prevWasSeparator = false;
31
- for (const item of items) {
32
- const isSeparator = item.comp === "separator";
33
- if (isSeparator && prevWasSeparator) continue;
34
- out.push(item);
35
- prevWasSeparator = isSeparator;
36
- }
37
- return out;
38
- }
39
-
40
- function getID(type, config = null) {
41
- return `${type}${config?.menu ? ":menu" : ""}`;
42
- }
package/types/index.d.ts DELETED
@@ -1,37 +0,0 @@
1
- import type { Component } from "svelte";
2
-
3
- export interface IToolbarItem {
4
- id?: string | number;
5
- comp?: string | Component<any>;
6
- icon?: string;
7
- css?: string;
8
- type?: string;
9
- title?: string;
10
- text?: string;
11
- tooltip?: string;
12
- menuText?: string;
13
- key?: string;
14
- disabled?: boolean;
15
- spacer?: boolean;
16
- collapsed?: boolean;
17
- handler?: (item: IToolbarItem, value?: any) => void;
18
- layout?: "column";
19
- items?: IToolbarItem[];
20
- pinned?: boolean;
21
- [key: string]: any;
22
- }
23
-
24
- export declare const Toolbar: Component<{
25
- items?: IToolbarItem[];
26
- menuCss?: string;
27
- css?: string;
28
- values?: { [key: string]: any };
29
- overflow?: "collapse" | "wrap" | "menu";
30
- onclick?: (ev: { item: IToolbarItem }) => void;
31
- onchange?: (ev: { value: any; item: IToolbarItem }) => void;
32
- }>;
33
-
34
- export declare function registerToolbarItem(
35
- type: string,
36
- handler: Component<any>
37
- ): void;
File without changes