@lavalogic/scoria 0.6.0 → 0.7.1

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.
@@ -82,7 +82,7 @@
82
82
  }
83
83
  </script>
84
84
 
85
- {#snippet draggableWithSelect<T>(item: Draggable, index: number)}
85
+ {#snippet draggableWithSelect<T>(item: Draggable<T>, index: number)}
86
86
  <DraggableCard
87
87
  title={item.title}
88
88
  subtitle={item.subtitle}
@@ -9,6 +9,7 @@
9
9
  import { ToastContext } from '../../../../../Contexts/ToastContext.svelte.js';
10
10
  import { Validity } from '../../../../../Types/Internal/Validity.js';
11
11
  import type { DateTableCell } from '../../../Types/Columns/DateTableCell.js';
12
+ import { dev } from '$app/environment';
12
13
 
13
14
  export interface TableDatePickerProps<T extends object> {
14
15
  cell: DateTableCell<T>;
@@ -32,7 +33,35 @@
32
33
  id: columnDef.id as string & keyof T
33
34
  });
34
35
 
35
- const editable = $derived(cell.column.columnDef.editable === true);
36
+ const editableAsync = $derived(
37
+ cell.column.columnDef.isEditable(cell.row.original, cell.row.originalIndex)
38
+ );
39
+ let editable = $state(false);
40
+
41
+ let asyncCounter = 0;
42
+ $effect(() => {
43
+ void editableAsync;
44
+ const localCounter = (asyncCounter = asyncCounter + 1);
45
+ (async () => {
46
+ try {
47
+ const isEditable = await editableAsync;
48
+ if (asyncCounter === localCounter) {
49
+ editable = isEditable;
50
+ } else if (dev) {
51
+ console.log('skipping async editable because it is stale');
52
+ }
53
+ } catch (e) {
54
+ if (dev) {
55
+ console.error(e);
56
+ }
57
+ if (asyncCounter === localCounter) {
58
+ editable = false;
59
+ } else if (dev) {
60
+ console.log('skipping async editable because it is stale');
61
+ }
62
+ }
63
+ })();
64
+ });
36
65
  const focusDetails: TableFocusDetails<T> = $derived(
37
66
  new TableFocusDetails(cell, tableContext, toast)
38
67
  );
@@ -7,6 +7,8 @@
7
7
  </script>
8
8
 
9
9
  <script lang="ts" generics="T extends object">
10
+ import { dev } from '$app/environment';
11
+
10
12
  import NumberInput from '../../../../NumberInput.svelte';
11
13
  import { NumberInputDef } from '../../../Types/Columns/Definitions/Accessors/NumberInputDef.svelte.js';
12
14
  import type { TableCell } from '../../../Types/Columns/TableCell.js';
@@ -49,7 +51,34 @@
49
51
  const tableContext = getContext<TableContext<T>>(TableContext.identifier);
50
52
  const { toast } = getContext<ToastContext>(ToastContext.identifier);
51
53
 
52
- const editable = $derived(cell.column.columnDef.editable === true);
54
+ const editableAsync = $derived(
55
+ cell.column.columnDef.isEditable(cell.row.original, cell.row.originalIndex)
56
+ );
57
+ let editable = $state(false);
58
+ let asyncCounter = 0;
59
+ $effect(() => {
60
+ void editableAsync;
61
+ const localCounter = (asyncCounter = asyncCounter + 1);
62
+ (async () => {
63
+ try {
64
+ const isEditable = await editableAsync;
65
+ if (asyncCounter === localCounter) {
66
+ editable = isEditable;
67
+ } else if (dev) {
68
+ console.log('skipping async editable because it is stale');
69
+ }
70
+ } catch (e) {
71
+ if (dev) {
72
+ console.error(e);
73
+ }
74
+ if (asyncCounter === localCounter) {
75
+ editable = false;
76
+ } else if (dev) {
77
+ console.log('skipping async editable because it is stale');
78
+ }
79
+ }
80
+ })();
81
+ });
53
82
 
54
83
  const focusDetails: TableFocusDetails<T> = $derived(
55
84
  new TableFocusDetails(cell, tableContext, toast)
@@ -1,6 +1,7 @@
1
1
  <svelte:options runes />
2
2
 
3
3
  <script lang="ts" module>
4
+ import { dev } from '$app/environment';
4
5
  import QuerySelect from '../../../../QuerySelect.svelte';
5
6
  import type { Column } from '../../../Types/Columns/Column.js';
6
7
  import type { QueryDef } from '../../../Types/Columns/Definitions/Accessors/QueryDef.svelte.js';
@@ -46,7 +47,35 @@
46
47
  const tableContext = getContext<TableContext<T>>(TableContext.identifier);
47
48
  const { toast } = getContext<ToastContext>(ToastContext.identifier);
48
49
 
49
- const editable = $derived(columnDef.editable === true);
50
+ const editableAsync = $derived(
51
+ cell.column.columnDef.isEditable(cell.row.original, cell.row.originalIndex)
52
+ );
53
+ let editable = $state(false);
54
+
55
+ let asyncCounter = 0;
56
+ $effect(() => {
57
+ void editableAsync;
58
+ const localCounter = (asyncCounter = asyncCounter + 1);
59
+ (async () => {
60
+ try {
61
+ const isEditable = await editableAsync;
62
+ if (asyncCounter === localCounter) {
63
+ editable = isEditable;
64
+ } else if (dev) {
65
+ console.log('skipping async editable because it is stale');
66
+ }
67
+ } catch (e) {
68
+ if (dev) {
69
+ console.error(e);
70
+ }
71
+ if (asyncCounter === localCounter) {
72
+ editable = false;
73
+ } else if (dev) {
74
+ console.log('skipping async editable because it is stale');
75
+ }
76
+ }
77
+ })();
78
+ });
50
79
 
51
80
  const focusDetails: TableFocusDetails<T> = $derived(
52
81
  new TableFocusDetails(cell, tableContext, toast)
@@ -1,6 +1,7 @@
1
1
  <svelte:options runes />
2
2
 
3
3
  <script lang="ts" module>
4
+ import { dev } from '$app/environment';
4
5
  import SingleSelect from '../../../../SingleSelect.svelte';
5
6
  import type { Column } from '../../../Types/Columns/Column.js';
6
7
  import type { SelectDef } from '../../../Types/Columns/Definitions/Accessors/SelectDef.svelte.js';
@@ -28,7 +29,7 @@
28
29
 
29
30
  let tdref = $state<HTMLTableCellElement | undefined>();
30
31
 
31
- const selectValue = $derived(cell.getValue() as T |null|undefined);
32
+ const selectValue = $derived(cell.getValue() as T | null | undefined);
32
33
 
33
34
  const visibleValue = $derived(
34
35
  cell.column.columnDef.getDisplayValue!(cell.row.original, cell.row.originalIndex)
@@ -39,7 +40,34 @@
39
40
  const tableContext = getContext<TableContext<T>>(TableContext.identifier);
40
41
  const { toast } = getContext<ToastContext>(ToastContext.identifier);
41
42
 
42
- const editable = $derived(cell.column.columnDef.editable === true);
43
+ const editableAsync = $derived(
44
+ cell.column.columnDef.isEditable(cell.row.original, cell.row.originalIndex)
45
+ );
46
+ let editable = $state(false);
47
+ let asyncCounter = 0;
48
+ $effect(() => {
49
+ void editableAsync;
50
+ const localCounter = (asyncCounter = asyncCounter + 1);
51
+ (async () => {
52
+ try {
53
+ const isEditable = await editableAsync;
54
+ if (asyncCounter === localCounter) {
55
+ editable = isEditable;
56
+ } else if (dev) {
57
+ console.log('skipping async editable because it is stale');
58
+ }
59
+ } catch (e) {
60
+ if (dev) {
61
+ console.error(e);
62
+ }
63
+ if (asyncCounter === localCounter) {
64
+ editable = false;
65
+ } else if (dev) {
66
+ console.log('skipping async editable because it is stale');
67
+ }
68
+ }
69
+ })();
70
+ });
43
71
 
44
72
  const focusDetails: TableFocusDetails<T> = $derived(
45
73
  new TableFocusDetails(cell, tableContext, toast)
@@ -7,6 +7,8 @@
7
7
  </script>
8
8
 
9
9
  <script lang="ts" generics="T extends object">
10
+ import { dev } from '$app/environment';
11
+
10
12
  import type { TableCell } from '../../../Types/Columns/TableCell.js';
11
13
  import { TableContext } from '../../../Types/Context/TableContext.svelte.js';
12
14
  import { TableFocusDetails } from '../../../Types/Focus/TableFocusDetails.svelte.js';
@@ -22,7 +24,35 @@
22
24
  // getNumberColumnDefOrError(cell.column.columnDef)
23
25
  // );
24
26
 
25
- const editable = $derived(cell.column.columnDef.editable === true);
27
+ const editableAsync = $derived(
28
+ cell.column.columnDef.isEditable(cell.row.original, cell.row.originalIndex)
29
+ );
30
+ let editable = $state(false);
31
+
32
+ let asyncCounter = 0;
33
+ $effect(() => {
34
+ void editableAsync;
35
+ const localCounter = (asyncCounter = asyncCounter + 1);
36
+ (async () => {
37
+ try {
38
+ const isEditable = await editableAsync;
39
+ if (asyncCounter === localCounter) {
40
+ editable = isEditable;
41
+ } else if (dev) {
42
+ console.log('skipping async editable because it is stale');
43
+ }
44
+ } catch (e) {
45
+ if (dev) {
46
+ console.error(e);
47
+ }
48
+ if (asyncCounter === localCounter) {
49
+ editable = false;
50
+ } else if (dev) {
51
+ console.log('skipping async editable because it is stale');
52
+ }
53
+ }
54
+ })();
55
+ });
26
56
 
27
57
  const value = $derived(cell.getValue());
28
58
 
@@ -10,6 +10,7 @@ import { BubbleDef } from '../Types/Columns/Definitions/Display/BubbleDef.svelte
10
10
  import { DisplayDef } from '../Types/Columns/Definitions/Display/DisplayDef.svelte.js';
11
11
  import { ProgressBarDef } from '../Types/Columns/Definitions/Display/ProgressBarDef.svelte.js';
12
12
  import { ValidityDef } from '../Types/Columns/Definitions/Display/ValidityDef.svelte.js';
13
+ import { asyncSleep } from '../../../Helpers/Helpers.svelte.js';
13
14
  export class ExampleItemColumnDef {
14
15
  static generateExampleItemColumnDefs(modal) {
15
16
  let defs = $state([
@@ -41,12 +42,12 @@ export class ExampleItemColumnDef {
41
42
  new TextInputDef({
42
43
  header: 'Name',
43
44
  id: 'name',
44
- editable: true
45
+ isEditable: () => true
45
46
  }),
46
47
  new NumberInputDef({
47
48
  header: 'Some Number',
48
49
  id: 'someNumber',
49
- editable: true,
50
+ isEditable: () => true,
50
51
  min: 0,
51
52
  max: 12345
52
53
  }),
@@ -67,25 +68,35 @@ export class ExampleItemColumnDef {
67
68
  new DateInputDef({
68
69
  header: 'Some Date',
69
70
  id: 'someDate',
70
- editable: true,
71
- defaultHidden: true
71
+ isEditable: async (item) => {
72
+ if (item.someDate?.getFullYear() == 2030) {
73
+ await asyncSleep(1000);
74
+ return false;
75
+ }
76
+ else if (item.someDate?.getFullYear() == 2029) {
77
+ await asyncSleep(5000);
78
+ return false;
79
+ }
80
+ return true;
81
+ }
82
+ // defaultHidden: true
72
83
  }),
73
84
  new DateInputDef({
74
85
  header: 'Some Date With Time',
75
86
  id: 'someDateWithTime',
76
- editable: true,
87
+ isEditable: () => true,
77
88
  showTime: true
78
89
  }),
79
90
  new DateInputDef({
80
91
  header: 'Flo Date',
81
92
  id: 'floDate',
82
- editable: true,
93
+ isEditable: () => true,
83
94
  defaultHidden: true
84
95
  }),
85
96
  new DateInputDef({
86
97
  header: 'Flo Date With Time',
87
98
  id: 'floDateWithTime',
88
- editable: true,
99
+ isEditable: () => true,
89
100
  defaultHidden: true,
90
101
  showTime: true
91
102
  }),
@@ -114,7 +125,7 @@ export class ExampleItemColumnDef {
114
125
  ];
115
126
  },
116
127
  getDisplayValue: (item) => item.someSelect,
117
- editable: true
128
+ isEditable: () => true
118
129
  }),
119
130
  new BubbleDef({
120
131
  header: 'Generated Bubble',
@@ -16,7 +16,7 @@ export interface ColumnDefProps<T extends object, FilterFnType = undefined> {
16
16
  defaultHidden?: boolean;
17
17
  minSize?: Pixels;
18
18
  width?: Pixels;
19
- editable?: boolean;
19
+ isEditable?: (item: T, index: number) => boolean | Promise<boolean>;
20
20
  resizable?: boolean;
21
21
  isValid?: ValidationFn<T>;
22
22
  filterFn?: FilterFn<T, FilterFnType> | CustomFilterFn;
@@ -57,7 +57,7 @@ export declare abstract class ColumnDef<T extends object, FilterFnType = undefin
57
57
  get getDisplayValue(): ((item: T, index: number) => string | null | Promise<string | null>) | undefined;
58
58
  set getDisplayValue(v: ((item: T, index: number) => string | null | Promise<string | null>) | undefined);
59
59
  toJSON(): object;
60
- readonly editable: boolean;
60
+ readonly isEditable: (item: T, index: number) => boolean | Promise<boolean>;
61
61
  readonly resizable: boolean;
62
62
  readonly header: string | Snippet;
63
63
  }
@@ -10,7 +10,7 @@ export class ColumnDef {
10
10
  this.accessorFn = $derived(props.accessorFn);
11
11
  this._minSize = $state(props.minSize ?? 48);
12
12
  this._width = $state(props.width ?? props.minSize ?? 48);
13
- this.editable = $derived(props.editable ?? false);
13
+ this.isEditable = $derived(props.isEditable ?? (() => false));
14
14
  this.resizable = $derived(props.resizable ?? true);
15
15
  this.header = $derived(props.header);
16
16
  this._filterValueType = $derived(props.filterValueType);
@@ -84,7 +84,7 @@ export class ColumnDef {
84
84
  toJSON() {
85
85
  return { columnType: this.columnType, id: this.id, header: this.header };
86
86
  }
87
- editable;
87
+ isEditable;
88
88
  resizable;
89
89
  header;
90
90
  }
@@ -0,0 +1,13 @@
1
+ <svelte:options runes />
2
+
3
+ <script lang="ts">
4
+ import { TestContext } from '../../Contexts/TestContext.svelte.js';
5
+ import { getContext } from 'svelte';
6
+
7
+ const testContext = getContext<TestContext>(TestContext.identifier);
8
+ </script>
9
+
10
+ <h1>counter 1: {testContext.value1.value}</h1>
11
+ <p>Ref wrapper 1 is incremented every time you click the button</p>
12
+ <h2>counter 2: {testContext.value2.value}</h2>
13
+ <p>ref wrapper 2 adds ref wrapper 1's value every time ref wrapper 1's value is divisible by 3</p>
@@ -0,0 +1,3 @@
1
+ declare const TestContextConsumer: import("svelte").Component<Record<string, never>, {}, "">;
2
+ type TestContextConsumer = ReturnType<typeof TestContextConsumer>;
3
+ export default TestContextConsumer;
@@ -0,0 +1,11 @@
1
+ import type { RefWrapper } from '../Types/Internal/RefWrapper.js';
2
+ export declare class TestContext {
3
+ static readonly identifier: unique symbol;
4
+ private _value1;
5
+ get value1(): RefWrapper<number>;
6
+ set value1(v: RefWrapper<number>);
7
+ private _value2;
8
+ get value2(): RefWrapper<number>;
9
+ set value2(v: RefWrapper<number>);
10
+ useTestEffect(sourceValue: RefWrapper<number>): RefWrapper<number>;
11
+ }
@@ -0,0 +1,30 @@
1
+ import { refWrapper } from '../Helpers/Helpers.svelte.js';
2
+ import { untrack } from 'svelte';
3
+ export class TestContext {
4
+ static identifier = Symbol();
5
+ _value1 = $state(refWrapper(0));
6
+ get value1() {
7
+ return this._value1;
8
+ }
9
+ set value1(v) {
10
+ this._value1 = v;
11
+ }
12
+ _value2 = $state(this.useTestEffect(this.value1));
13
+ get value2() {
14
+ return this._value2;
15
+ }
16
+ set value2(v) {
17
+ this._value2 = v;
18
+ }
19
+ useTestEffect(sourceValue) {
20
+ const internalState = refWrapper(0);
21
+ $effect(() => {
22
+ if (sourceValue.value % 3 == 0) {
23
+ untrack(() => {
24
+ internalState.value += sourceValue.value;
25
+ });
26
+ }
27
+ });
28
+ return internalState;
29
+ }
30
+ }
@@ -26,4 +26,6 @@ export declare function isErrorResponse(error: unknown): error is IErrorResponse
26
26
  export declare function getErrorMessage(error: unknown, includeUUID?: boolean): string;
27
27
  export declare function isValidEmailAddress(emailAddress: string): RegExpMatchArray | null;
28
28
  export declare function gridItem(element: HTMLElement): (() => void) | undefined;
29
+ export type Milliseconds = number;
30
+ export declare function asyncSleep(delay: Milliseconds): Promise<void>;
29
31
  export {};
@@ -1,4 +1,5 @@
1
1
  import { Sizing } from '../scss/sizing.js';
2
+ import { untrack } from 'svelte';
2
3
  export function autoPluralise(text, quantity = 2, { capitalise = false } = {}) {
3
4
  if (quantity === 1) {
4
5
  return text;
@@ -39,11 +40,11 @@ export function generateUUID() {
39
40
  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
40
41
  let r = Math.random() * 16;
41
42
  if (d > 0) {
42
- r = (d + r) % 16 | 0;
43
+ r = ((d + r) % 16) | 0;
43
44
  d = Math.floor(d / 16);
44
45
  }
45
46
  else {
46
- r = (d2 + r) % 16 | 0;
47
+ r = ((d2 + r) % 16) | 0;
47
48
  d2 = Math.floor(d2 / 16);
48
49
  }
49
50
  return (c == 'x' ? r : (r & 0x7) | 0x8).toString(16);
@@ -165,3 +166,6 @@ export function gridItem(element) {
165
166
  observer.observe(firstChild);
166
167
  return () => observer.unobserve(firstChild);
167
168
  }
169
+ export async function asyncSleep(delay) {
170
+ await new Promise((r) => setTimeout(r, delay));
171
+ }
package/dist/index.d.ts CHANGED
@@ -123,7 +123,7 @@ export { TooltipContext } from './Contexts/TooltipContext.svelte.js';
123
123
  export { UntypedInputContext } from './Contexts/UntypedInputContext.svelte.js';
124
124
  export { Delay } from './Delay/Delay.js';
125
125
  export { DATAMatrix } from './Helpers/Datamatrix.js';
126
- export { gridItem, autoPluralise, generateUUID, generateShortUUID, refWrapper, SQLFriendly, SQLFriendlyWithLocalTime, passthrough, getCanvasContext, getErrorMessage, isValidEmailAddress, localTimeFormat, vowels } from './Helpers/Helpers.svelte.js';
126
+ export { gridItem, autoPluralise, generateUUID, generateShortUUID, refWrapper, SQLFriendly, SQLFriendlyWithLocalTime, passthrough, getCanvasContext, getErrorMessage, isValidEmailAddress, localTimeFormat, vowels, asyncSleep } from './Helpers/Helpers.svelte.js';
127
127
  export { type INamed } from './Helpers/INamed.js';
128
128
  export { AttributeOperation } from './Types/Internal/AttributeOperation.js';
129
129
  export { type BigRadioOption } from './Types/Internal/BigRadioOption.js';
package/dist/index.js CHANGED
@@ -130,7 +130,7 @@ export { TooltipContext } from './Contexts/TooltipContext.svelte.js';
130
130
  export { UntypedInputContext } from './Contexts/UntypedInputContext.svelte.js';
131
131
  export { Delay } from './Delay/Delay.js';
132
132
  export { DATAMatrix } from './Helpers/Datamatrix.js';
133
- export { gridItem, autoPluralise, generateUUID, generateShortUUID, refWrapper, SQLFriendly, SQLFriendlyWithLocalTime, passthrough, getCanvasContext, getErrorMessage, isValidEmailAddress, localTimeFormat, vowels } from './Helpers/Helpers.svelte.js';
133
+ export { gridItem, autoPluralise, generateUUID, generateShortUUID, refWrapper, SQLFriendly, SQLFriendlyWithLocalTime, passthrough, getCanvasContext, getErrorMessage, isValidEmailAddress, localTimeFormat, vowels, asyncSleep } from './Helpers/Helpers.svelte.js';
134
134
  export {} from './Helpers/INamed.js';
135
135
  export { AttributeOperation } from './Types/Internal/AttributeOperation.js';
136
136
  export {} from './Types/Internal/BigRadioOption.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.6.0",
4
+ "version": "0.7.1",
5
5
  "publishConfig": {
6
6
  "access": "restricted"
7
7
  },