@lavalogic/scoria 0.29.0 → 0.31.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.
Files changed (38) hide show
  1. package/dist/Components/DesktopModal.svelte +5 -2
  2. package/dist/Components/HorizontalTabGroup.svelte +43 -3
  3. package/dist/Components/HorizontalTabGroupButton.svelte +40 -15
  4. package/dist/Components/HorizontalTabGroupProps.d.ts +13 -1
  5. package/dist/Components/Nav/CallbackButtonWithIcon.d.ts +23 -0
  6. package/dist/Components/Nav/CallbackButtonWithIcon.js +1 -0
  7. package/dist/Components/Nav/Dimensions.d.ts +4 -0
  8. package/dist/Components/Nav/Dimensions.js +5 -0
  9. package/dist/Components/Nav/Nav.svelte +217 -0
  10. package/dist/Components/Nav/Nav.svelte.d.ts +4 -0
  11. package/dist/Components/Nav/NavProps.d.ts +9 -0
  12. package/dist/Components/Nav/NavProps.js +1 -0
  13. package/dist/Components/Nav/NavServiceMenuList.svelte +57 -0
  14. package/dist/Components/Nav/NavServiceMenuList.svelte.d.ts +4 -0
  15. package/dist/Components/Nav/NavServiceMenuListProps.d.ts +7 -0
  16. package/dist/Components/Nav/NavServiceMenuListProps.js +1 -0
  17. package/dist/Components/Nav/NavTab.svelte +123 -0
  18. package/dist/Components/Nav/NavTab.svelte.d.ts +4 -0
  19. package/dist/Components/Nav/NavTabProps.d.ts +5 -0
  20. package/dist/Components/Nav/NavTabProps.js +1 -0
  21. package/dist/Components/Nav/ServiceMenuItem.svelte +156 -0
  22. package/dist/Components/Nav/ServiceMenuItem.svelte.d.ts +4 -0
  23. package/dist/Components/Nav/ServicesNav.svelte +92 -0
  24. package/dist/Components/Nav/ServicesNav.svelte.d.ts +4 -0
  25. package/dist/Components/Nav/ServicesNavProps.d.ts +6 -0
  26. package/dist/Components/Nav/ServicesNavProps.js +1 -0
  27. package/dist/Components/Nav/SideMenuItemProps.d.ts +7 -0
  28. package/dist/Components/Nav/SideMenuItemProps.js +1 -0
  29. package/dist/Components/Nav/SideMenuItemVariant.d.ts +5 -0
  30. package/dist/Components/Nav/SideMenuItemVariant.js +5 -0
  31. package/dist/Components/TabGroupButtonProps.d.ts +1 -0
  32. package/dist/Helpers/Helpers.svelte.d.ts +1 -0
  33. package/dist/Helpers/Helpers.svelte.js +1 -0
  34. package/dist/index.d.ts +13 -2
  35. package/dist/index.js +13 -2
  36. package/package.json +1 -1
  37. package/dist/Types/Internal/AttributeOperation.d.ts +0 -31
  38. package/dist/Types/Internal/AttributeOperation.js +0 -31
@@ -25,7 +25,7 @@
25
25
  minWidth,
26
26
  minHeight,
27
27
  width,
28
- height
28
+ height,
29
29
  }: DesktopModalProps = $props();
30
30
  </script>
31
31
 
@@ -96,7 +96,10 @@
96
96
  </div>
97
97
 
98
98
  {#if isLoading}
99
- <LoadingOverlay absolute message={loadingMessage} />
99
+ <LoadingOverlay
100
+ absolute
101
+ message={loadingMessage}
102
+ />
100
103
  {/if}
101
104
  </Modal>
102
105
 
@@ -4,8 +4,11 @@
4
4
  lang="ts"
5
5
  generics="CommonDenominator"
6
6
  >
7
+ import { ellipses } from '../Helpers/Helpers.svelte.js';
8
+ import { Colour } from '../scss/colours.js';
7
9
  import HorizontalTabGroupButton from './HorizontalTabGroupButton.svelte';
8
10
  import type { HorizontalTabGroupProps } from './HorizontalTabGroupProps.js';
11
+ import TextInput from './TextInput.svelte';
9
12
  let {
10
13
  // name: _name, //FIXME why is this not implemented?
11
14
  tabComponent,
@@ -13,7 +16,24 @@
13
16
  selected,
14
17
  noRadius = false,
15
18
  grow = false,
19
+ filterable = false,
20
+ filterFn,
16
21
  }: HorizontalTabGroupProps<CommonDenominator> = $props();
22
+
23
+ // svelte-ignore state_referenced_locally
24
+ let filteredTabs = $state(tabs);
25
+
26
+ $effect(() => {
27
+ const term = searchValue;
28
+ const id = setTimeout(() => {
29
+ filteredTabs = filterFn ? tabs.filter((t) => filterFn(t, term)) : tabs;
30
+ }, 300);
31
+ return () => {
32
+ clearTimeout(id);
33
+ };
34
+ });
35
+
36
+ let searchValue = $state('');
17
37
  </script>
18
38
 
19
39
  <div
@@ -22,14 +42,24 @@
22
42
  class:grow
23
43
  >
24
44
  <div class="tabs">
45
+ {#if filterable}
46
+ <div class="searchbar">
47
+ <TextInput
48
+ bind:value={searchValue}
49
+ placeholder="Search any keyword to find matches{ellipses}"
50
+ rightIcon={{ name: 'search-1', colour: Colour['$ui-blue-4'] }}
51
+ />
52
+ </div>
53
+ {/if}
25
54
  {#if tabComponent}
26
- {#each tabs as option (option)}
55
+ {#each filteredTabs as option (option)}
27
56
  {@render tabComponent(option, selected === option)}
28
57
  {/each}
29
58
  {:else}
30
- {#each tabs as option (option)}
59
+ {#each filteredTabs as option (option)}
31
60
  <HorizontalTabGroupButton
32
61
  {option}
62
+ noRadius={noRadius || filterable}
33
63
  selected={selected === option}
34
64
  />
35
65
  {/each}
@@ -43,7 +73,15 @@
43
73
  </div>
44
74
  </div>
45
75
 
46
- <style>.wrapper {
76
+ <style>.searchbar {
77
+ display: flex;
78
+ width: 100%;
79
+ padding: 0.5rem;
80
+ background-color: #ffffff;
81
+ min-width: 330px;
82
+ }
83
+
84
+ .wrapper {
47
85
  box-shadow: 0px 0px 5px 0px rgba(0, 0, 0, 0.1333333333);
48
86
  border-radius: 4px;
49
87
  display: flex;
@@ -65,6 +103,8 @@
65
103
  border-radius: 4px 0 0 4px;
66
104
  display: flex;
67
105
  flex-flow: column nowrap;
106
+ padding-top: 1px;
107
+ gap: 1px;
68
108
  min-width: 10rem;
69
109
  background-color: #e9ecf1;
70
110
  border-right: solid 1px #cbd4da;
@@ -6,13 +6,11 @@
6
6
  >
7
7
  import { devCatch } from '../Helpers/Helpers.svelte.js';
8
8
  import { Colour } from '../scss/colours.js';
9
-
10
9
  import { Size } from '../Types/Internal/Size.js';
11
-
12
10
  import Icon from './Icon.svelte';
13
11
  import type { TabGroupButtonProps } from './TabGroupButtonProps.js';
14
12
 
15
- let { option, selected }: TabGroupButtonProps<Args> = $props();
13
+ let { option, selected, noRadius = false }: TabGroupButtonProps<Args> = $props();
16
14
 
17
15
  let disabled = $state(true);
18
16
  let visible = $state(true);
@@ -68,13 +66,15 @@
68
66
  class:invalid={!isValid}
69
67
  {disabled}
70
68
  class:hidden={!visible}
69
+ class:no-radius={noRadius}
71
70
  >
72
- <Icon
71
+ <div class="selected-marker"></div>
72
+ <!-- <Icon
73
73
  name="nothing"
74
74
  colour={Colour['$brand-primary']}
75
75
  size={Size.Small}
76
76
  {...option.iconLeft}
77
- />
77
+ /> -->
78
78
 
79
79
  {option.label}
80
80
  {#if !isValid}
@@ -83,13 +83,13 @@
83
83
  colour={Colour['$error-border']}
84
84
  size={Size.Small}
85
85
  />
86
- {:else}
86
+ <!-- {:else}
87
87
  <Icon
88
88
  name="nothing"
89
89
  colour={Colour['$brand-primary']}
90
90
  size={Size.Small}
91
91
  {...option.iconRight}
92
- />
92
+ /> -->
93
93
  {/if}
94
94
  </button>
95
95
 
@@ -102,27 +102,52 @@
102
102
  cursor: pointer;
103
103
  font-size: 1rem;
104
104
  font-weight: 500;
105
- padding: 0.75rem;
106
- background-color: transparent;
105
+ padding: 1rem;
106
+ background-color: #ffffff;
107
107
  border: none;
108
- transition: border-color ease-out 0.15s 0s, background-color ease-out 0.15s 0s, color ease-out 0.15s 0s, fill ease-out 0.15s 0s;
109
108
  text-align: start;
110
109
  display: flex;
111
110
  flex-flow: row nowrap;
112
111
  align-items: center;
113
112
  width: 100%;
114
- justify-content: space-between;
115
113
  gap: 0.5rem;
114
+ position: relative;
115
+ transition: border-color ease-out 0.15s 0s, background-color ease-out 0.15s 0s, color ease-out 0.15s 0s, fill ease-out 0.15s 0s;
116
116
  }
117
- .tab:first-child {
118
- border-radius: 4px 0 0 0;
117
+ .tab .selected-marker {
118
+ position: absolute;
119
+ left: 0;
120
+ top: 0;
121
+ bottom: 0;
122
+ height: 100%;
123
+ opacity: 0;
124
+ background-color: #259fc7;
125
+ width: 0.3333333333rem;
126
+ transition: border-color ease-out 0.15s 0s, background-color ease-out 0.15s 0s, color ease-out 0.15s 0s, fill ease-out 0.15s 0s, opacity ease-out 0.15s 0s;
119
127
  }
120
128
  .tab.selected {
121
- background-color: white;
129
+ color: #259fc7;
130
+ }
131
+ .tab.selected .selected-marker {
132
+ opacity: 1;
133
+ }
134
+ .tab:hover, .tab.selected:hover {
135
+ color: #49b8dc;
136
+ background-color: #f4f7f9;
137
+ }
138
+ .tab:hover .selected-marker, .tab.selected:hover .selected-marker {
139
+ opacity: 1;
140
+ background-color: #49b8dc;
141
+ }
142
+ .tab:not(.no-radius):first-child {
143
+ border-radius: 4px 0 0 0;
122
144
  }
123
145
  .tab:hover, .tab.selected:hover, .tab.invalid:hover {
124
146
  background-color: #f4f7f9;
125
147
  }
148
+ .tab.invalid .selected-marker {
149
+ background-color: #aa1414 !important;
150
+ }
126
151
  .tab.invalid {
127
- color: #aa1414;
152
+ color: #aa1414 !important;
128
153
  }</style>
@@ -1,10 +1,22 @@
1
1
  import type { TabOption } from '../Types/Internal/TabOption.js';
2
2
  import type { Snippet } from 'svelte';
3
- export interface HorizontalTabGroupProps<CommonDenominator> {
3
+ export type HorizontalTabGroupProps<CommonDenominator> = HorizontalTabGroupPropsWithSearch<CommonDenominator> | HorizontalTabGroupPropsWithoutSearch<CommonDenominator>;
4
+ interface HorizontalTabGroupPropsBase<CommonDenominator> {
4
5
  name?: string;
5
6
  tabComponent?: Snippet<[TabOption<CommonDenominator>, boolean]>;
6
7
  noRadius?: boolean;
7
8
  grow?: boolean;
8
9
  tabs: Array<TabOption<CommonDenominator>>;
9
10
  selected: TabOption<CommonDenominator> | undefined;
11
+ filterable?: boolean;
12
+ filterFn?: (item: TabOption<CommonDenominator>, filterValue: string) => boolean;
10
13
  }
14
+ export interface HorizontalTabGroupPropsWithoutSearch<CommonDenominator> extends HorizontalTabGroupPropsBase<CommonDenominator> {
15
+ filterable?: false;
16
+ filterFn?: never;
17
+ }
18
+ export interface HorizontalTabGroupPropsWithSearch<CommonDenominator> extends HorizontalTabGroupPropsBase<CommonDenominator> {
19
+ filterable: true;
20
+ filterFn: (item: TabOption<CommonDenominator>, filterValue: string) => boolean;
21
+ }
22
+ export {};
@@ -0,0 +1,23 @@
1
+ import type { Pathname } from '$app/types';
2
+ import type { IconProps } from '../IconProps.js';
3
+ export type CallbackButtonWithIcon = CallbackButtonWithPath | CallbackButtonWithCallback;
4
+ export interface NavButtonPartial {
5
+ iconName?: IconProps['name'];
6
+ label: string;
7
+ hoverLabel?: string;
8
+ subtitle?: string;
9
+ visible?: boolean | Promise<boolean>;
10
+ }
11
+ export type NavButton = NavButtonPartial & ({
12
+ path: Pathname;
13
+ } | {
14
+ callback: () => void;
15
+ });
16
+ export interface CallbackButtonWithPath extends NavButtonPartial {
17
+ path: Pathname;
18
+ callback?: never;
19
+ }
20
+ export interface CallbackButtonWithCallback extends NavButtonPartial {
21
+ path?: never;
22
+ callback: () => void;
23
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,4 @@
1
+ export declare const Dimensions: {
2
+ readonly smallPanelWidth: 3;
3
+ readonly fullPanelWidth: 15;
4
+ };
@@ -0,0 +1,5 @@
1
+ export const Dimensions = {
2
+ smallPanelWidth: 3,
3
+ fullPanelWidth: 15,
4
+ };
5
+ Object.freeze(Dimensions);
@@ -0,0 +1,217 @@
1
+ <svelte:options runes />
2
+
3
+ <script lang="ts">
4
+ import { Size } from '../../Types/Internal/Size.js';
5
+ import Icon from '../Icon.svelte';
6
+ import type { NavProps } from './NavProps.js';
7
+ import ServicesNav from './ServicesNav.svelte';
8
+
9
+ const { userName, extras, userDialog, serviceTabs, selectedServiceTab }: NavProps = $props();
10
+
11
+ const userIcon = $derived.by(() => {
12
+ const names = userName.split(' ');
13
+
14
+ const initials = names.map((it) => it.charAt(0)).slice(0, 2);
15
+ return initials;
16
+ });
17
+
18
+ const singleLetter = $derived(userIcon.length === 1);
19
+ const hasLetters = $derived(userIcon.length > 0);
20
+
21
+ let showServicesDialog = $state(false);
22
+ let showUserDialog = $state(false);
23
+ </script>
24
+
25
+ <nav class="main-navigation">
26
+ <span class="nav-link hoverable"> FloWMS </span>
27
+ <!-- HACK swap above for below when a decent landing page has been added -->
28
+ <!-- <a
29
+ href={resolve(Locations.Home)}
30
+ class="nav-link hoverable"
31
+ >
32
+ FloWMS
33
+ </a> -->
34
+
35
+ <div class="nav-link">
36
+ <button
37
+ onclick={() => {
38
+ showServicesDialog = !showServicesDialog;
39
+ }}
40
+ >
41
+ <div class="delimited">
42
+ <Icon
43
+ name="block-menu"
44
+ size={Size.Medium}
45
+ />
46
+ <span>Services</span>
47
+ </div>
48
+ </button>
49
+
50
+ {#if showServicesDialog}
51
+ <ServicesNav
52
+ onclose={() => {
53
+ showServicesDialog = false;
54
+ }}
55
+ {serviceTabs}
56
+ {selectedServiceTab}
57
+ />
58
+ {/if}
59
+ </div>
60
+
61
+ <div class="nav-search-container">
62
+ <!-- <TextInput
63
+ bind:value={searchParam}
64
+ placeholder="Search for anything"
65
+ leftIcon={{ name: 'search-1' }}
66
+ /> -->
67
+ </div>
68
+ {#if extras}
69
+ <div class="nav-link spaced selects">
70
+ {@render extras()}
71
+ </div>
72
+ {/if}
73
+
74
+ <div>
75
+ <button
76
+ onclick={() => {
77
+ showUserDialog = !showUserDialog;
78
+ }}
79
+ >
80
+ <span class="nav-link padded">
81
+ <span
82
+ class="user-icon"
83
+ class:visible={hasLetters}
84
+ class:single-letter={singleLetter}>{userIcon}</span
85
+ >
86
+ <span class="user-name">{userName}</span>
87
+ </span>
88
+ </button>
89
+ {#if showUserDialog}
90
+ {@render userDialog(() => {
91
+ showUserDialog = false;
92
+ })}
93
+ {/if}
94
+ </div>
95
+ </nav>
96
+
97
+ <style>div.overlay {
98
+ position: fixed;
99
+ top: calc(3.25rem + var(--env-border-height, 0));
100
+ bottom: 0;
101
+ left: 0;
102
+ right: 0;
103
+ z-index: 98;
104
+ background-color: rgba(0, 0, 0, 0.2);
105
+ }
106
+
107
+ a.nav-link {
108
+ color: unset;
109
+ }
110
+
111
+ div.user {
112
+ display: flex;
113
+ flex-flow: column nowrap;
114
+ gap: 0.75rem;
115
+ position: fixed;
116
+ border-radius: 4px;
117
+ background-color: #ffffff;
118
+ padding: 0.75rem;
119
+ top: calc(3.25rem + var(--env-border-height, 0));
120
+ right: 0.5rem;
121
+ z-index: 99;
122
+ box-shadow: 0px 0px 5px 0px rgba(0, 0, 0, 0.1333333333);
123
+ }
124
+
125
+ nav {
126
+ position: relative;
127
+ grid-column-start: side-menu;
128
+ grid-column-end: end;
129
+ grid-row-start: nav-start;
130
+ grid-row-end: nav-end;
131
+ background-color: #ffffff;
132
+ display: flex;
133
+ flex-flow: row;
134
+ align-content: center;
135
+ justify-content: space-between;
136
+ border-bottom: solid 1px #cbd4da;
137
+ }
138
+ nav button {
139
+ background-color: #ffffff;
140
+ border: none;
141
+ height: 100%;
142
+ width: 100%;
143
+ }
144
+ nav button:hover {
145
+ background-color: rgba(47, 151, 186, 0.1);
146
+ cursor: pointer;
147
+ }
148
+
149
+ .spaced {
150
+ gap: 1rem;
151
+ }
152
+
153
+ .user-icon {
154
+ background-color: #dae0e5;
155
+ padding: 0.5rem;
156
+ border-radius: 17px;
157
+ opacity: 0;
158
+ font-weight: 600;
159
+ }
160
+ .user-icon.visible {
161
+ opacity: 1;
162
+ }
163
+ .user-icon.single-letter {
164
+ padding: 0.3rem 0.6rem;
165
+ border-radius: 14px;
166
+ }
167
+
168
+ .main-navigation > *:nth-child(even) {
169
+ position: relative;
170
+ }
171
+ .main-navigation > *:nth-child(even)::before {
172
+ content: "";
173
+ height: 1.5rem;
174
+ border-left: solid 1px #e8e8e8;
175
+ border-radius: 10px;
176
+ left: 0;
177
+ position: absolute;
178
+ }
179
+ .main-navigation > *:nth-child(even)::after {
180
+ content: "";
181
+ height: 1.5rem;
182
+ border-right: solid 1px #e8e8e8;
183
+ border-radius: 10px;
184
+ position: absolute;
185
+ right: 0;
186
+ }
187
+ .main-navigation > *:nth-child(even).selects {
188
+ padding-left: 1px;
189
+ padding-right: 1px;
190
+ width: 32rem;
191
+ --sv-border: none;
192
+ --input-width: calc(50% - 0.5rem);
193
+ }
194
+
195
+ .nav-link {
196
+ display: flex;
197
+ justify-content: center;
198
+ flex-flow: row;
199
+ align-items: center;
200
+ /* flex-grow: 1; */
201
+ min-width: 7.5rem;
202
+ transition: background-color 200ms ease 0s;
203
+ }
204
+ .nav-link.padded {
205
+ padding-left: 1rem;
206
+ padding-right: 1rem;
207
+ gap: 1rem;
208
+ }
209
+
210
+ div.delimited {
211
+ padding: 0 0.5rem;
212
+ display: flex;
213
+ flex-flow: row;
214
+ align-items: center;
215
+ justify-content: center;
216
+ gap: 0.5rem;
217
+ }</style>
@@ -0,0 +1,4 @@
1
+ import type { NavProps } from './NavProps.js';
2
+ declare const Nav: import("svelte").Component<NavProps, {}, "">;
3
+ type Nav = ReturnType<typeof Nav>;
4
+ export default Nav;
@@ -0,0 +1,9 @@
1
+ import type { TabOption } from '../../Types/Internal/TabOption.js';
2
+ import type { Snippet } from 'svelte';
3
+ export interface NavProps {
4
+ userName: string;
5
+ userDialog: Snippet<[closeUserDialog: () => void]>;
6
+ serviceTabs: Array<TabOption<unknown>>;
7
+ selectedServiceTab: TabOption<unknown> | undefined;
8
+ extras?: Snippet;
9
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,57 @@
1
+ <svelte:options runes />
2
+
3
+ <script lang="ts">
4
+ import { Colour } from '../../scss/colours.js';
5
+ import { Size } from '../../Types/Internal/Size.js';
6
+ import Icon from '../Icon.svelte';
7
+ import type { NavServiceMenuListProps } from './NavServiceMenuListProps.js';
8
+ import ServiceMenuItem from './ServiceMenuItem.svelte';
9
+
10
+ const { title, items, icon }: NavServiceMenuListProps = $props();
11
+ </script>
12
+
13
+ <div class="wrapper">
14
+ <h3>
15
+ <div class="icon">
16
+ {#if icon}
17
+ <Icon
18
+ size={Size.Large}
19
+ colour={Colour['$font-primary']}
20
+ {...icon}
21
+ />
22
+ {/if}
23
+ </div>
24
+ {title}
25
+ </h3>
26
+
27
+ <div class="items">
28
+ {#each items as { item } (item.label)}
29
+ <ServiceMenuItem {item} />
30
+ {/each}
31
+ </div>
32
+ </div>
33
+
34
+ <style>h3 {
35
+ font-size: 1.5rem;
36
+ display: flex;
37
+ flex-flow: row nowrap;
38
+ align-items: center;
39
+ color: #3a4952;
40
+ }
41
+ h3 .icon {
42
+ display: flex;
43
+ flex-flow: row nowrap;
44
+ align-items: center;
45
+ justify-content: center;
46
+ width: 3.4rem;
47
+ }
48
+
49
+ .wrapper {
50
+ display: flex;
51
+ flex-flow: column nowrap;
52
+ height: 100%;
53
+ }
54
+ .wrapper .items {
55
+ flex-shrink: 1;
56
+ overflow: auto;
57
+ }</style>
@@ -0,0 +1,4 @@
1
+ import type { NavServiceMenuListProps } from './NavServiceMenuListProps.js';
2
+ declare const NavServiceMenuList: import("svelte").Component<NavServiceMenuListProps, {}, "">;
3
+ type NavServiceMenuList = ReturnType<typeof NavServiceMenuList>;
4
+ export default NavServiceMenuList;
@@ -0,0 +1,7 @@
1
+ import type { IconProps } from '../IconProps.js';
2
+ import type { SideMenuItemProps } from './SideMenuItemProps.js';
3
+ export interface NavServiceMenuListProps {
4
+ items: Array<SideMenuItemProps>;
5
+ title: string;
6
+ icon?: IconProps;
7
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,123 @@
1
+ <svelte:options runes />
2
+
3
+ <script lang="ts">
4
+ import { devCatch } from '../../Helpers/Helpers.svelte.js';
5
+ import { Colour } from '../../scss/colours.js';
6
+ import { Size } from '../../Types/Internal/Size.js';
7
+ import Icon from '../Icon.svelte';
8
+ import type { NavTabProps } from './NavTabProps.js';
9
+
10
+ let { option, selected }: NavTabProps = $props();
11
+
12
+ let disabled = $state(true);
13
+ let visible = $state(true);
14
+ let isValid = $state(true);
15
+
16
+ $effect(() => {
17
+ if (typeof option.isValid === 'boolean') {
18
+ isValid = option.isValid;
19
+ } else {
20
+ option.isValid
21
+ ?.then((v) => {
22
+ isValid = v;
23
+ })
24
+ .catch(devCatch);
25
+ }
26
+ });
27
+ $effect(() => {
28
+ if (typeof option.disabled === 'boolean') {
29
+ disabled = option.disabled;
30
+ } else if (option.disabled == undefined) {
31
+ disabled = false;
32
+ } else {
33
+ option.disabled
34
+ .then((v) => {
35
+ disabled = v;
36
+ })
37
+ .catch(devCatch);
38
+ }
39
+ });
40
+
41
+ $effect(() => {
42
+ if (typeof option.visible === 'boolean') {
43
+ visible = option.visible;
44
+ } else if (option.visible == undefined) {
45
+ visible = true;
46
+ } else {
47
+ option.visible
48
+ .then((v) => {
49
+ visible = v;
50
+ })
51
+ .catch(devCatch);
52
+ }
53
+ });
54
+ </script>
55
+
56
+ <button
57
+ class:selected
58
+ onclick={() => {
59
+ option.onclick();
60
+ }}
61
+ type="button"
62
+ class="tab"
63
+ {disabled}
64
+ class:invalid={!isValid}
65
+ class:hidden={!visible}
66
+ >
67
+ <Icon
68
+ name="nothing"
69
+ colour={Colour['$brand-primary']}
70
+ size={Size.MediumLarge}
71
+ {...option.iconLeft}
72
+ />
73
+
74
+ {option.label}
75
+ {#if option.isValid === false}
76
+ <Icon
77
+ name="error"
78
+ colour={Colour['$error-border']}
79
+ size={Size.MediumLarge}
80
+ />
81
+ {:else}
82
+ <Icon
83
+ name="nothing"
84
+ colour={Colour['$brand-primary']}
85
+ size={Size.MediumLarge}
86
+ {...option.iconRight}
87
+ />
88
+ {/if}
89
+ </button>
90
+
91
+ <style>button.tab.hidden {
92
+ display: none !important;
93
+ }
94
+
95
+ .tab {
96
+ color: #3a4952;
97
+ cursor: pointer;
98
+ font-size: 1rem;
99
+ font-weight: 500;
100
+ padding: 1.5rem;
101
+ background-color: transparent;
102
+ border: none;
103
+ transition: border-color ease-out 0.15s 0s, background-color ease-out 0.15s 0s, color ease-out 0.15s 0s, fill ease-out 0.15s 0s;
104
+ text-align: start;
105
+ display: flex;
106
+ flex-flow: row nowrap;
107
+ align-items: center;
108
+ width: 100%;
109
+ justify-content: start;
110
+ gap: 0.5rem;
111
+ }
112
+ .tab:first-child {
113
+ border-radius: 4px 0 0 0;
114
+ }
115
+ .tab.selected {
116
+ color: #259fc7;
117
+ }
118
+ .tab:hover, .tab.selected:hover, .tab.invalid:hover {
119
+ background-color: #f4f7f9;
120
+ }
121
+ .tab.invalid {
122
+ color: #aa1414;
123
+ }</style>
@@ -0,0 +1,4 @@
1
+ import type { NavTabProps } from './NavTabProps.js';
2
+ declare const NavTab: import("svelte").Component<NavTabProps, {}, "">;
3
+ type NavTab = ReturnType<typeof NavTab>;
4
+ export default NavTab;
@@ -0,0 +1,5 @@
1
+ import type { TabOption } from '../../Types/Internal/TabOption.js';
2
+ export interface NavTabProps {
3
+ option: TabOption<unknown>;
4
+ selected: boolean;
5
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,156 @@
1
+ <svelte:options runes />
2
+
3
+ <script lang="ts">
4
+ import { dev } from '$app/environment';
5
+ import { resolve } from '$app/paths';
6
+ import { page } from '$app/state';
7
+ import { devCatch } from '../../Helpers/Helpers.svelte.js';
8
+ import { Size } from '../../Types/Internal/Size.js';
9
+ import Icon from '../Icon.svelte';
10
+ import { Dimensions } from './Dimensions.js';
11
+ import type { SideMenuItemProps } from './SideMenuItemProps.js';
12
+
13
+ const { item, selected: maybeSelected }: SideMenuItemProps = $props();
14
+
15
+ const selected = $derived(maybeSelected ?? (item.path ? page.url.pathname == item.path : false));
16
+
17
+ let visible = $state(true);
18
+
19
+ $effect(() => {
20
+ if (typeof item.visible === 'boolean') {
21
+ visible = item.visible;
22
+ } else if (item.visible == undefined) {
23
+ visible = true;
24
+ } else {
25
+ item.visible
26
+ .then((v) => {
27
+ visible = v;
28
+ })
29
+ .catch(devCatch);
30
+ }
31
+ });
32
+ </script>
33
+
34
+ {#if item.callback}
35
+ <button
36
+ onclick={() => {
37
+ item.callback();
38
+ }}
39
+ style="--iconWidth: {Dimensions.smallPanelWidth}rem"
40
+ class:selected
41
+ class:hidden={!visible}
42
+ >
43
+ <!-- <div class="selected-marker"></div> -->
44
+ <div class="icon-wrapper">
45
+ {#if item.iconName}
46
+ <Icon
47
+ name={item.iconName}
48
+ size={Size.Medium}
49
+ label={item.label}
50
+ />
51
+ {/if}
52
+ </div>
53
+ <div class="spacer"></div>
54
+ <div class="text">
55
+ <div class="label">{item.label}</div>
56
+ {#if item.subtitle}
57
+ <div class="subtitle">{item.subtitle}</div>
58
+ {/if}
59
+ </div></button
60
+ >
61
+ {:else if item.path}
62
+ <!-- FIXME why is this type cast required? -->
63
+ <a
64
+ href={resolve(item.path as '/')}
65
+ style="--iconWidth: {Dimensions.smallPanelWidth}rem"
66
+ class:selected
67
+ class:hidden={!visible}
68
+ >
69
+ <!-- <div class="selected-marker"></div> -->
70
+ <div class="icon-wrapper">
71
+ {#if item.iconName}
72
+ <Icon
73
+ name={item.iconName}
74
+ size={Size.Medium}
75
+ label={item.label}
76
+ />
77
+ {/if}
78
+ </div>
79
+ <div class="spacer"></div>
80
+ <div class="text">
81
+ <div class="label">{item.label}</div>
82
+ {#if item.subtitle}
83
+ <div class="subtitle">{item.subtitle}</div>
84
+ {/if}
85
+ </div></a
86
+ >
87
+ {:else if dev}
88
+ Error: Callback had neither callback nor path
89
+ {/if}
90
+
91
+ <style>button.hidden,
92
+ a.hidden {
93
+ display: none !important;
94
+ }
95
+
96
+ button,
97
+ a {
98
+ font-size: 1rem;
99
+ background-color: transparent;
100
+ border: none;
101
+ cursor: pointer;
102
+ padding: 0.8rem 0;
103
+ display: flex;
104
+ flex-flow: row nowrap;
105
+ justify-content: flex-start;
106
+ align-items: center;
107
+ position: relative;
108
+ color: #455661;
109
+ fill: #455661;
110
+ box-sizing: border-box;
111
+ }
112
+ button div,
113
+ a div {
114
+ user-select: none;
115
+ }
116
+ button .text,
117
+ a .text {
118
+ display: flex;
119
+ flex-flow: column nowrap;
120
+ gap: 0.75rem;
121
+ }
122
+ button .text .label,
123
+ a .text .label {
124
+ font-weight: 500;
125
+ }
126
+ button .subtitle,
127
+ a .subtitle {
128
+ font-size: 0.875rem;
129
+ color: #9da9b1;
130
+ }
131
+ button,
132
+ a {
133
+ transition: border-color ease-out 0.15s 0s, background-color ease-out 0.15s 0s, color ease-out 0.15s 0s, fill ease-out 0.15s 0s;
134
+ }
135
+ button .icon-wrapper,
136
+ a .icon-wrapper {
137
+ width: var(--iconWidth);
138
+ display: flex;
139
+ flex-flow: row nowrap;
140
+ align-items: center;
141
+ justify-content: center;
142
+ box-sizing: border-box;
143
+ }
144
+ button.selected, button:hover,
145
+ a.selected,
146
+ a:hover {
147
+ color: #259fc7;
148
+ }
149
+ button:hover,
150
+ a:hover {
151
+ background-color: #f4f7f9;
152
+ }
153
+ button .spacer,
154
+ a .spacer {
155
+ width: 0.5rem;
156
+ }</style>
@@ -0,0 +1,4 @@
1
+ import type { SideMenuItemProps } from './SideMenuItemProps.js';
2
+ declare const ServiceMenuItem: import("svelte").Component<SideMenuItemProps, {}, "">;
3
+ type ServiceMenuItem = ReturnType<typeof ServiceMenuItem>;
4
+ export default ServiceMenuItem;
@@ -0,0 +1,92 @@
1
+ <svelte:options runes />
2
+
3
+ <script lang="ts">
4
+ import type { TabOption } from '../../Types/Internal/TabOption.js';
5
+ import HorizontalTabGroup from '../HorizontalTabGroup.svelte';
6
+ import NavTab from './NavTab.svelte';
7
+ import type { ServicesNavProps } from './ServicesNavProps.js';
8
+
9
+ const { onclose, serviceTabs, selectedServiceTab }: ServicesNavProps = $props();
10
+
11
+ let servicesDialogRef: HTMLDivElement | undefined = $state.raw();
12
+ </script>
13
+
14
+ <div
15
+ bind:this={servicesDialogRef}
16
+ role="dialog"
17
+ tabindex="0"
18
+ class="overlay"
19
+ onclick={(e) => {
20
+ e.stopPropagation();
21
+ if (servicesDialogRef === e.target) {
22
+ onclose();
23
+ }
24
+ }}
25
+ onkeydown={(e) => {
26
+ if (e.key === 'Escape') {
27
+ onclose();
28
+ }
29
+ }}
30
+ >
31
+ <div
32
+ role="navigation"
33
+ class="services"
34
+ >
35
+ <HorizontalTabGroup
36
+ name="nav-services"
37
+ tabs={serviceTabs}
38
+ selected={selectedServiceTab}
39
+ tabComponent={navTab}
40
+ noRadius
41
+ grow
42
+ />
43
+ </div>
44
+ </div>
45
+
46
+ {#snippet navTab(option: TabOption<unknown>, selected: boolean)}
47
+ <NavTab
48
+ {option}
49
+ {selected}
50
+ />
51
+ {/snippet}
52
+
53
+ <style>div.overlay {
54
+ position: fixed;
55
+ top: calc(3.25rem + var(--env-border-height, 0));
56
+ bottom: 0;
57
+ left: 0;
58
+ right: 0;
59
+ z-index: 98;
60
+ background-color: rgba(0, 0, 0, 0.2);
61
+ }
62
+
63
+ div.services {
64
+ position: fixed;
65
+ border-radius: 4px;
66
+ background-color: #ffffff;
67
+ top: calc(3.25rem + var(--env-border-height, 0));
68
+ left: 7.5rem;
69
+ z-index: 99;
70
+ box-shadow: 0px 0px 5px 0px rgba(0, 0, 0, 0.1333333333);
71
+ padding: 0;
72
+ display: flex;
73
+ flex-flow: row nowrap;
74
+ align-items: stretch;
75
+ justify-content: stretch;
76
+ height: min(710px, 80%);
77
+ width: 800px;
78
+ }
79
+
80
+ div.wrapper {
81
+ display: flex;
82
+ flex-flow: row wrap;
83
+ width: 600px;
84
+ gap: 1.5rem;
85
+ --input-width: 100%;
86
+ }
87
+ div.wrapper .row {
88
+ width: 100%;
89
+ gap: 1.5rem;
90
+ display: flex;
91
+ flex-flow: row nowrap;
92
+ }</style>
@@ -0,0 +1,4 @@
1
+ import type { ServicesNavProps } from './ServicesNavProps.js';
2
+ declare const ServicesNav: import("svelte").Component<ServicesNavProps, {}, "">;
3
+ type ServicesNav = ReturnType<typeof ServicesNav>;
4
+ export default ServicesNav;
@@ -0,0 +1,6 @@
1
+ import type { TabOption } from '../../Types/Internal/TabOption.js';
2
+ export interface ServicesNavProps {
3
+ onclose: () => void;
4
+ serviceTabs: Array<TabOption<unknown>>;
5
+ selectedServiceTab: TabOption<unknown> | undefined;
6
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,7 @@
1
+ import type { CallbackButtonWithIcon } from './CallbackButtonWithIcon.js';
2
+ import type { SideMenuItemVariant } from './SideMenuItemVariant.js';
3
+ export interface SideMenuItemProps {
4
+ item: CallbackButtonWithIcon;
5
+ selected?: boolean;
6
+ variant?: SideMenuItemVariant;
7
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,5 @@
1
+ export declare const SideMenuItemVariant: {
2
+ readonly Nav: "Nav";
3
+ readonly Tab: "Tab";
4
+ };
5
+ export type SideMenuItemVariant = (typeof SideMenuItemVariant)[keyof typeof SideMenuItemVariant];
@@ -0,0 +1,5 @@
1
+ export const SideMenuItemVariant = {
2
+ Nav: 'Nav',
3
+ Tab: 'Tab',
4
+ };
5
+ Object.freeze(SideMenuItemVariant);
@@ -2,4 +2,5 @@ import type { TabOption } from '../Types/Internal/TabOption.js';
2
2
  export interface TabGroupButtonProps<Args> {
3
3
  option: TabOption<Args>;
4
4
  selected: boolean;
5
+ noRadius?: boolean;
5
6
  }
@@ -54,6 +54,7 @@ export declare function hasQuery<FilterValueType>(customFilter: CustomFilter<Fil
54
54
  export declare function isDateTuple(value: unknown): value is [Date | null, Date | null];
55
55
  export declare function isNumericArray(value: unknown): value is Array<number | null>;
56
56
  export declare function isValidityTuple(v: Validity | ValidityWrapper | undefined): v is ValidityWrapper;
57
+ export declare const ellipses = "\u2026";
57
58
  export declare const loadingText = "loading\u2026";
58
59
  export declare function isKeyOf<T extends object>(key: string, obj: T): key is string & keyof T;
59
60
  export interface ICreateTableArgs<T extends object, RowIdType extends Primitive> {
@@ -410,6 +410,7 @@ export function isNumericArray(value) {
410
410
  export function isValidityTuple(v) {
411
411
  return v != null && typeof v == 'object';
412
412
  }
413
+ export const ellipses = '…';
413
414
  export const loadingText = 'loading…';
414
415
  export function isKeyOf(key, obj) {
415
416
  return key in obj;
package/dist/index.d.ts CHANGED
@@ -58,6 +58,18 @@ export type { MultiSelectCommonProps } from './Components/MultiSelectCommonProps
58
58
  export type { MultiSelectProps } from './Components/MultiSelectProps.js';
59
59
  export type { MultiSelectPropsAsObject } from './Components/MultiSelectPropsAsObject.js';
60
60
  export type { MultiSelectPropsAsPrimitive } from './Components/MultiSelectPropsAsPrimitive.js';
61
+ export { type CallbackButtonWithCallback, type CallbackButtonWithIcon, type CallbackButtonWithPath, type NavButton, type NavButtonPartial, } from './Components/Nav/CallbackButtonWithIcon.js';
62
+ export { Dimensions } from './Components/Nav/Dimensions.js';
63
+ export { default as Nav } from './Components/Nav/Nav.svelte';
64
+ export { type NavProps } from './Components/Nav/NavProps.js';
65
+ export { default as NavServiceMenuList } from './Components/Nav/NavServiceMenuList.svelte';
66
+ export { type NavServiceMenuListProps } from './Components/Nav/NavServiceMenuListProps.js';
67
+ export { default as NavTab } from './Components/Nav/NavTab.svelte';
68
+ export { default as ServiceMenuItem } from './Components/Nav/ServiceMenuItem.svelte';
69
+ export { default as ServicesNav } from './Components/Nav/ServicesNav.svelte';
70
+ export { type ServicesNavProps } from './Components/Nav/ServicesNavProps.js';
71
+ export { type SideMenuItemProps } from './Components/Nav/SideMenuItemProps.js';
72
+ export { SideMenuItemVariant } from './Components/Nav/SideMenuItemVariant.js';
61
73
  export { default as NumberInput } from './Components/NumberInput.svelte';
62
74
  export type { NumberInputProps } from './Components/NumberInputProps.js';
63
75
  export { type PartialIconProps } from './Components/PartialIconProps.js';
@@ -211,11 +223,10 @@ export { TooltipContext } from './Contexts/TooltipContext.svelte.js';
211
223
  export { UntypedInputContext } from './Contexts/UntypedInputContext.svelte.js';
212
224
  export { Delay } from './Delay/Delay.js';
213
225
  export { DATAMatrix } from './Helpers/Datamatrix.js';
214
- export { asyncAll, asyncEvery, asyncFilter, asyncFilterIndices, asyncFlatMap, asyncForEach, asyncMap, asyncSleep, asyncSome, autoPluralise, createLocalTable, devCatch, generateShortUUID, generateUUID, getAccessorSortingFn, getCanvasContext, getErrorMessage, gridItem, isDateTuple, isNumericArray, isValidEmailAddress, loadingText, localTimeFormat, passthrough, refWrapper, SQLFriendly, SQLFriendlyWithLocalTime, vowels, type ICreateTableArgs, } from './Helpers/Helpers.svelte.js';
226
+ export { asyncAll, asyncEvery, asyncFilter, asyncFilterIndices, asyncFlatMap, asyncForEach, asyncMap, asyncSleep, asyncSome, autoPluralise, createLocalTable, devCatch, ellipses, generateShortUUID, generateUUID, getAccessorSortingFn, getCanvasContext, getErrorMessage, gridItem, isDateTuple, isNumericArray, isValidEmailAddress, loadingText, localTimeFormat, passthrough, refWrapper, SQLFriendly, SQLFriendlyWithLocalTime, vowels, type ICreateTableArgs, } from './Helpers/Helpers.svelte.js';
215
227
  export { type INamed } from './Helpers/INamed.js';
216
228
  export { Colour, ColourSet } from './scss/colours.js';
217
229
  export { Sizing } from './scss/sizing.js';
218
- export { AttributeOperation } from './Types/Internal/AttributeOperation.js';
219
230
  export { type BigRadioOption } from './Types/Internal/BigRadioOption.js';
220
231
  export { type ButtonProp } from './Types/Internal/ButtonProp.js';
221
232
  export { type CallbackButton } from './Types/Internal/CallbackButton.js';
package/dist/index.js CHANGED
@@ -38,6 +38,18 @@ export { default as LoadingModal } from './Components/LoadingModal.svelte';
38
38
  export { default as LoadingOverlay } from './Components/LoadingOverlay.svelte';
39
39
  export { default as Modal } from './Components/Modal.svelte';
40
40
  export { default as MultiSelect } from './Components/MultiSelect.svelte';
41
+ export {} from './Components/Nav/CallbackButtonWithIcon.js';
42
+ export { Dimensions } from './Components/Nav/Dimensions.js';
43
+ export { default as Nav } from './Components/Nav/Nav.svelte';
44
+ export {} from './Components/Nav/NavProps.js';
45
+ export { default as NavServiceMenuList } from './Components/Nav/NavServiceMenuList.svelte';
46
+ export {} from './Components/Nav/NavServiceMenuListProps.js';
47
+ export { default as NavTab } from './Components/Nav/NavTab.svelte';
48
+ export { default as ServiceMenuItem } from './Components/Nav/ServiceMenuItem.svelte';
49
+ export { default as ServicesNav } from './Components/Nav/ServicesNav.svelte';
50
+ export {} from './Components/Nav/ServicesNavProps.js';
51
+ export {} from './Components/Nav/SideMenuItemProps.js';
52
+ export { SideMenuItemVariant } from './Components/Nav/SideMenuItemVariant.js';
41
53
  export { default as NumberInput } from './Components/NumberInput.svelte';
42
54
  export {} from './Components/PartialIconProps.js';
43
55
  export { default as PasswordInput } from './Components/PasswordInput.svelte';
@@ -154,11 +166,10 @@ export { TooltipContext } from './Contexts/TooltipContext.svelte.js';
154
166
  export { UntypedInputContext } from './Contexts/UntypedInputContext.svelte.js';
155
167
  export { Delay } from './Delay/Delay.js';
156
168
  export { DATAMatrix } from './Helpers/Datamatrix.js';
157
- export { asyncAll, asyncEvery, asyncFilter, asyncFilterIndices, asyncFlatMap, asyncForEach, asyncMap, asyncSleep, asyncSome, autoPluralise, createLocalTable, devCatch, generateShortUUID, generateUUID, getAccessorSortingFn, getCanvasContext, getErrorMessage, gridItem, isDateTuple, isNumericArray, isValidEmailAddress, loadingText, localTimeFormat, passthrough, refWrapper, SQLFriendly, SQLFriendlyWithLocalTime, vowels, } from './Helpers/Helpers.svelte.js';
169
+ export { asyncAll, asyncEvery, asyncFilter, asyncFilterIndices, asyncFlatMap, asyncForEach, asyncMap, asyncSleep, asyncSome, autoPluralise, createLocalTable, devCatch, ellipses, generateShortUUID, generateUUID, getAccessorSortingFn, getCanvasContext, getErrorMessage, gridItem, isDateTuple, isNumericArray, isValidEmailAddress, loadingText, localTimeFormat, passthrough, refWrapper, SQLFriendly, SQLFriendlyWithLocalTime, vowels, } from './Helpers/Helpers.svelte.js';
158
170
  export {} from './Helpers/INamed.js';
159
171
  export { Colour, ColourSet } from './scss/colours.js';
160
172
  export { Sizing } from './scss/sizing.js';
161
- export { AttributeOperation } from './Types/Internal/AttributeOperation.js';
162
173
  export {} from './Types/Internal/BigRadioOption.js';
163
174
  export {} from './Types/Internal/ButtonProp.js';
164
175
  export {} from './Types/Internal/CallbackButton.js';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@lavalogic/scoria",
3
3
  "description": "Svelte components used for the FloWMS Web Frontend",
4
- "version": "0.29.0",
4
+ "version": "0.31.0",
5
5
  "publishConfig": {
6
6
  "access": "restricted"
7
7
  },
@@ -1,31 +0,0 @@
1
- export declare const AttributeOperation: {
2
- readonly Preadvice: "Preadvice";
3
- readonly RequiredAtPreadvice: "Preadvice";
4
- readonly Order: "Order";
5
- readonly RequiredAtOrder: "Order";
6
- readonly Despatch: "Despatch";
7
- readonly RequiredAtDespatch: "RequiredAtDespatch";
8
- readonly ValidateAtDespatch: "ValidateAtDespatch";
9
- readonly Kit: "Kit";
10
- readonly RequiredAtKit: "RequiredAtKit";
11
- readonly ValidateAtKit: "ValidateAtKit";
12
- readonly Pack: "Pack";
13
- readonly RequiredAtPack: "RequiredAtPack";
14
- readonly ValidateAtPack: "ValidateAtPack";
15
- readonly Pick: "Pick";
16
- readonly RequiredAtPick: "RequiredAtPick";
17
- readonly ValidateAtPick: "ValidateAtPick";
18
- readonly Receipt: "Receipt";
19
- readonly RequiredAtReceipt: "RequiredAtReceipt";
20
- readonly ValidateAtReceipt: "ValidateAtReceipt";
21
- readonly ReceiptReturn: "ReceiptReturn";
22
- readonly RequiredAtReceiptReturn: "RequiredAtReceiptReturn";
23
- readonly ValidateAtReceiptReturn: "ValidateAtReceiptReturn";
24
- readonly Sort: "Sort";
25
- readonly RequiredAtSort: "RequiredAtSort";
26
- readonly ValidateAtSort: "ValidateAtSort";
27
- readonly StockTake: "StockTake";
28
- readonly RequiredAtStockTake: "RequiredAtStockTake";
29
- readonly ValidateAtStockTake: "ValidateAtStockTake";
30
- };
31
- export type AttributeOperation = (typeof AttributeOperation)[keyof typeof AttributeOperation];
@@ -1,31 +0,0 @@
1
- export const AttributeOperation = {
2
- Preadvice: 'Preadvice',
3
- RequiredAtPreadvice: 'Preadvice', //functionally identical
4
- Order: 'Order',
5
- RequiredAtOrder: 'Order', // functionally identical
6
- Despatch: 'Despatch',
7
- RequiredAtDespatch: 'RequiredAtDespatch',
8
- ValidateAtDespatch: 'ValidateAtDespatch',
9
- Kit: 'Kit',
10
- RequiredAtKit: 'RequiredAtKit',
11
- ValidateAtKit: 'ValidateAtKit',
12
- Pack: 'Pack',
13
- RequiredAtPack: 'RequiredAtPack',
14
- ValidateAtPack: 'ValidateAtPack',
15
- Pick: 'Pick',
16
- RequiredAtPick: 'RequiredAtPick',
17
- ValidateAtPick: 'ValidateAtPick',
18
- Receipt: 'Receipt',
19
- RequiredAtReceipt: 'RequiredAtReceipt',
20
- ValidateAtReceipt: 'ValidateAtReceipt',
21
- ReceiptReturn: 'ReceiptReturn',
22
- RequiredAtReceiptReturn: 'RequiredAtReceiptReturn',
23
- ValidateAtReceiptReturn: 'ValidateAtReceiptReturn',
24
- Sort: 'Sort',
25
- RequiredAtSort: 'RequiredAtSort',
26
- ValidateAtSort: 'ValidateAtSort',
27
- StockTake: 'StockTake',
28
- RequiredAtStockTake: 'RequiredAtStockTake',
29
- ValidateAtStockTake: 'ValidateAtStockTake'
30
- };
31
- Object.freeze(AttributeOperation);