@softwareone/spi-sv5-library 1.11.7 → 1.11.9

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,12 +1,13 @@
1
1
  <script lang="ts">
2
- import { type Snippet } from 'svelte';
2
+ import { untrack, type Snippet } from 'svelte';
3
3
 
4
4
  import { Button, ErrorPage, type ProgressWizardStep } from '../index.js';
5
5
 
6
6
  interface ProgressWizardProps {
7
7
  steps: ProgressWizardStep[];
8
- readonly?: boolean;
9
8
  currentStep: number;
9
+ readonly?: boolean;
10
+ initialStep?: number;
10
11
  content: Snippet;
11
12
  additionalButtons: Snippet<[lastActiveStep: number]>;
12
13
  oncancel: VoidFunction;
@@ -16,6 +17,7 @@
16
17
  steps,
17
18
  readonly = false,
18
19
  currentStep = $bindable(0),
20
+ initialStep,
19
21
  content,
20
22
  additionalButtons,
21
23
  oncancel
@@ -25,6 +27,21 @@
25
27
  const firstActiveStep = $derived<number>(steps.findIndex((step) => !step.disabled) + 1);
26
28
  const lastActiveStep = $derived<number>(steps.findLastIndex((step) => !step.disabled) + 1);
27
29
 
30
+ const isValidInitialStep = (initialStep: number): boolean => {
31
+ const index = initialStep - 1;
32
+ if (index < 0 || index >= steps.length) return false;
33
+ return !steps[index].disabled;
34
+ };
35
+
36
+ const getInitialStep = (): number => {
37
+ if (allStepsDisabled) return -1;
38
+ return initialStep && isValidInitialStep(initialStep) ? initialStep : firstActiveStep;
39
+ };
40
+
41
+ const getInitialStepReadonlyMode = (): number => {
42
+ return allStepsDisabled ? -1 : steps.length;
43
+ };
44
+
28
45
  const onclickNext = () => {
29
46
  const nextStepIndex = steps.slice(currentStep).findIndex((step) => !step.disabled);
30
47
  currentStep = currentStep + nextStepIndex + 1;
@@ -39,18 +56,9 @@
39
56
  currentStep = currentStep - previousStepIndex - 1;
40
57
  };
41
58
 
42
- const stepToStart = (): number => {
43
- if (readonly && allStepsDisabled) return -1;
44
- if (readonly && !allStepsDisabled) return steps.length;
45
- return firstActiveStep;
46
- };
47
-
48
- currentStep = stepToStart();
49
-
50
59
  $effect(() => {
51
- if (!readonly) {
52
- currentStep = firstActiveStep;
53
- }
60
+ if (!readonly) currentStep = untrack(() => getInitialStep());
61
+ else currentStep = untrack(() => getInitialStepReadonlyMode());
54
62
  });
55
63
  </script>
56
64
 
@@ -2,8 +2,9 @@ import { type Snippet } from 'svelte';
2
2
  import { type ProgressWizardStep } from '../index.js';
3
3
  interface ProgressWizardProps {
4
4
  steps: ProgressWizardStep[];
5
- readonly?: boolean;
6
5
  currentStep: number;
6
+ readonly?: boolean;
7
+ initialStep?: number;
7
8
  content: Snippet;
8
9
  additionalButtons: Snippet<[lastActiveStep: number]>;
9
10
  oncancel: VoidFunction;
@@ -1,59 +1,56 @@
1
1
  <script lang="ts">
2
+ import type { Attachment } from 'svelte/attachments';
3
+ import { fade } from 'svelte/transition';
4
+
2
5
  import type { Action } from './types.js';
3
6
 
4
7
  interface Props {
5
8
  actions: Action[];
6
- isLastChild?: boolean;
7
- isOnlyOneRow?: boolean;
8
9
  }
9
10
 
10
- let { actions, isLastChild = false, isOnlyOneRow = false }: Props = $props();
11
+ let { actions }: Props = $props();
12
+
13
+ let container = $state<HTMLDivElement>()!;
14
+ let isOpen = $state(false);
11
15
 
12
- const icons: Record<string, string> = {
16
+ const ICONS: Record<string, string> = {
13
17
  Edit: 'edit',
14
18
  Delete: 'delete'
15
19
  };
16
20
 
17
- const indexLastSecondaryAction = $derived(
18
- actions.findLastIndex((action) => !action.isMainAction)
19
- );
21
+ const isSingleAction = $derived(actions.length === 1);
22
+ const dividerIndex = $derived(actions.findLastIndex((action) => !action.isMainAction));
23
+
24
+ const showDividerAfter = (index: number) =>
25
+ dividerIndex !== -1 && dividerIndex !== actions.length - 1 && dividerIndex === index;
26
+
27
+ const autoPosition: Attachment<HTMLElement> = (element: HTMLElement) => {
28
+ const updatePosition = () => {
29
+ const rect = container.getBoundingClientRect();
30
+ const showAbove = rect.bottom + element.offsetHeight > window.innerHeight;
31
+
32
+ const left = rect.left + rect.width / 2 - element.offsetWidth / 2;
33
+ const top = showAbove ? rect.top - element.offsetHeight + 14 : rect.bottom - 8;
34
+
35
+ element.style.setProperty('--menu-left', `${Math.max(0, left)}px`);
36
+ element.style.setProperty('--menu-top', `${top}px`);
37
+ };
38
+
39
+ updatePosition();
40
+ window.addEventListener('scroll', updatePosition, true);
41
+
42
+ return () => window.removeEventListener('scroll', updatePosition, true);
43
+ };
44
+
45
+ const toggleActionsMenu = () => {
46
+ isOpen = !isOpen;
47
+ };
20
48
  </script>
21
49
 
22
50
  <div class="actions-column">
23
- {#if actions.length > 1}
24
- <div class="actions-group">
25
- <span class="actions-trigger">...</span>
26
- <div
27
- class={[
28
- 'actions-menu',
29
- isOnlyOneRow && 'actions-menu--only-row',
30
- isLastChild && !isOnlyOneRow && 'actions-menu--last',
31
- !isLastChild && !isOnlyOneRow && 'actions-menu--top'
32
- ]}
33
- >
34
- <ul class="actions-list">
35
- {#each actions as action, index}
36
- <li>
37
- <button
38
- type="button"
39
- onclick={action.onClick}
40
- class="action-button"
41
- class:action-button--delete={action.isDelete && action.isEnabled}
42
- disabled={!action.isEnabled}
43
- >
44
- <span>{action.name}</span>
45
- </button>
46
- </li>
47
- {#if indexLastSecondaryAction !== -1 && indexLastSecondaryAction !== actions.length - 1 && indexLastSecondaryAction === index}
48
- <hr class="action-divider" />
49
- {/if}
50
- {/each}
51
- </ul>
52
- </div>
53
- </div>
54
- {:else}
51
+ {#if isSingleAction}
55
52
  {@const action = actions[0]}
56
- {@const nameIcon = icons[action.name]}
53
+ {@const icon = ICONS[action.name]}
57
54
 
58
55
  <button
59
56
  type="button"
@@ -62,14 +59,45 @@
62
59
  class:action-button--delete={action.isDelete && action.isEnabled}
63
60
  disabled={!action.isEnabled}
64
61
  >
65
- {#if nameIcon}
66
- <span class="material-icons-outlined">
67
- {nameIcon}
68
- </span>
62
+ {#if icon}
63
+ <span class="material-icons-outlined">{icon}</span>
69
64
  {:else}
70
65
  <span class:action-button--primary={action.isEnabled}>{action.name}</span>
71
66
  {/if}
72
67
  </button>
68
+ {:else}
69
+ <div
70
+ role="menu"
71
+ tabindex="-1"
72
+ class="actions-group"
73
+ bind:this={container}
74
+ onmouseenter={toggleActionsMenu}
75
+ onmouseleave={toggleActionsMenu}
76
+ >
77
+ <span class="actions-trigger">...</span>
78
+ {#if isOpen}
79
+ <div class="actions-menu" {@attach autoPosition} transition:fade={{ duration: 100 }}>
80
+ <ul class="actions-list">
81
+ {#each actions as action, index}
82
+ <li>
83
+ <button
84
+ type="button"
85
+ onclick={action.onClick}
86
+ class="action-button"
87
+ class:action-button--delete={action.isDelete && action.isEnabled}
88
+ disabled={!action.isEnabled}
89
+ >
90
+ <span>{action.name}</span>
91
+ </button>
92
+ </li>
93
+ {#if showDividerAfter(index)}
94
+ <hr class="action-divider" />
95
+ {/if}
96
+ {/each}
97
+ </ul>
98
+ </div>
99
+ {/if}
100
+ </div>
73
101
  {/if}
74
102
  </div>
75
103
 
@@ -99,7 +127,7 @@
99
127
  }
100
128
 
101
129
  .actions-group {
102
- position: absolute;
130
+ position: relative;
103
131
  }
104
132
 
105
133
  .actions-trigger {
@@ -111,39 +139,15 @@
111
139
  }
112
140
 
113
141
  .actions-menu {
114
- position: absolute;
142
+ position: fixed;
115
143
  z-index: var(--z-index-menu);
116
144
  width: 108px;
145
+ left: var(--menu-left);
146
+ top: var(--menu-top);
117
147
  padding: var(--spacing-md) var(--spacing-lg);
118
148
  background: var(--color-white);
119
149
  border-radius: var(--radius-lg);
120
150
  box-shadow: var(--shadow-menu);
121
- visibility: hidden;
122
- opacity: 0;
123
- transition: var(--transition-standard);
124
- }
125
-
126
- .actions-group:hover .actions-menu {
127
- visibility: visible;
128
- opacity: 1;
129
- }
130
-
131
- .actions-menu--only-row {
132
- right: 28px;
133
- bottom: 16px;
134
- transform: translateY(50%);
135
- }
136
-
137
- .actions-menu--last {
138
- bottom: 28px;
139
- left: 50%;
140
- transform: translateX(-50%);
141
- }
142
-
143
- .actions-menu--top {
144
- top: 32px;
145
- left: 50%;
146
- transform: translateX(-50%);
147
151
  }
148
152
 
149
153
  .actions-list {
@@ -1,8 +1,6 @@
1
1
  import type { Action } from './types.js';
2
2
  interface Props {
3
3
  actions: Action[];
4
- isLastChild?: boolean;
5
- isOnlyOneRow?: boolean;
6
4
  }
7
5
  declare const ActionsColumn: import("svelte").Component<Props, {}, "">;
8
6
  type ActionsColumn = ReturnType<typeof ActionsColumn>;
@@ -275,8 +275,10 @@
275
275
  <Button
276
276
  type="button"
277
277
  variant="secondary"
278
- onclick={() => exportExcel(table, excelSetting)}>Export</Button
278
+ onclick={() => exportExcel(table, excelSetting)}
279
279
  >
280
+ {excelSetting.label ?? 'Export'}
281
+ </Button>
280
282
  {/if}
281
283
  {@render header?.()}
282
284
  </div>
@@ -1,6 +1,7 @@
1
1
  export interface ExcelSetting {
2
2
  fileName: string;
3
3
  exportAllColumns: boolean;
4
+ label?: string;
4
5
  }
5
6
  export declare const ColumnFormat: {
6
7
  readonly NUMBER: "#,##0.00";
@@ -43,10 +43,8 @@ export const createActionsColumn = (getActions) => {
43
43
  columnWidth: '117px',
44
44
  hideColumnFilter: true
45
45
  },
46
- cell: ({ row, table }) => renderComponent(ActionsColumn, {
47
- actions: getActions(row),
48
- isLastChild: table.getRowModel().rows.at(-1).index === row.index,
49
- isOnlyOneRow: table.getRowModel().rows.length === 1
46
+ cell: ({ row }) => renderComponent(ActionsColumn, {
47
+ actions: getActions(row)
50
48
  })
51
49
  });
52
50
  };
@@ -1,18 +1,15 @@
1
1
  <script lang="ts">
2
2
  interface Props {
3
3
  title: string;
4
- icon: string;
4
+ iconUrl: string;
5
5
  onclickwaffleitems: VoidFunction;
6
6
  }
7
7
 
8
- let { title, icon, onclickwaffleitems }: Props = $props();
8
+ let { title, iconUrl, onclickwaffleitems }: Props = $props();
9
9
  </script>
10
10
 
11
11
  <button type="button" onclick={onclickwaffleitems} class="waffle-item-button">
12
- <figure class="waffle-icon-container" aria-hidden="true">
13
- {@html icon}
14
- </figure>
15
-
12
+ <img src={iconUrl} alt="" aria-hidden="true" class="waffle-item-img" />
16
13
  <h3 class="waffle-title">
17
14
  {title}
18
15
  </h3>
@@ -44,13 +41,12 @@
44
41
  background: #eaecff;
45
42
  }
46
43
 
47
- .waffle-icon-container {
44
+ .waffle-item-img {
48
45
  width: 40px;
49
46
  height: 40px;
50
47
  margin-bottom: 12px;
51
- display: flex;
52
- align-items: center;
53
- justify-content: center;
48
+ filter: invert(27%) sepia(99%) saturate(4729%) hue-rotate(240deg) brightness(101%)
49
+ contrast(106%);
54
50
  }
55
51
 
56
52
  .waffle-title {
@@ -1,6 +1,6 @@
1
1
  interface Props {
2
2
  title: string;
3
- icon: string;
3
+ iconUrl: string;
4
4
  onclickwaffleitems: VoidFunction;
5
5
  }
6
6
  declare const WaffleItems: import("svelte").Component<Props, {}, "">;
@@ -1,5 +1,5 @@
1
1
  export interface WaffleItem {
2
2
  title: string;
3
3
  url: string;
4
- icon: string;
4
+ iconUrl: string;
5
5
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@softwareone/spi-sv5-library",
3
- "version": "1.11.7",
3
+ "version": "1.11.9",
4
4
  "description": "Svelte components",
5
5
  "keywords": [
6
6
  "svelte",