@websline/system-components 1.3.25 → 1.3.26

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 (28) hide show
  1. package/dist/components/atoms/actions/closeButton/closeButton.variants.js +3 -3
  2. package/dist/components/atoms/actions/iconButton/IconButton.svelte +1 -1
  3. package/dist/components/atoms/actions/iconButton/IconButton.svelte.d.ts +2 -2
  4. package/dist/components/atoms/actions/iconButton/iconButton.variants.js +2 -2
  5. package/dist/components/atoms/input/input.variants.js +3 -3
  6. package/dist/components/atoms/select/Select.svelte +38 -19
  7. package/dist/components/atoms/select/Select.svelte.d.ts +8 -0
  8. package/dist/components/atoms/select/select.variants.d.ts +6 -1
  9. package/dist/components/atoms/select/select.variants.js +17 -12
  10. package/dist/components/molecules/{notification/Notification.svelte → notifications/systemAlert/SystemAlert.svelte} +8 -8
  11. package/dist/components/molecules/notifications/systemAlert/SystemAlert.svelte.d.ts +57 -0
  12. package/dist/components/molecules/{notification/notification.variants.d.ts → notifications/systemAlert/systemAlert.variants.d.ts} +4 -4
  13. package/dist/components/molecules/{notification/notification.variants.js → notifications/systemAlert/systemAlert.variants.js} +6 -6
  14. package/dist/components/molecules/notifications/toast/Toast.svelte +51 -0
  15. package/dist/components/molecules/notifications/toast/Toast.svelte.d.ts +49 -0
  16. package/dist/components/molecules/notifications/toast/toast.variants.d.ts +31 -0
  17. package/dist/components/molecules/notifications/toast/toast.variants.js +18 -0
  18. package/dist/components/organisms/notificationGroup/NotificationGroup.svelte +71 -39
  19. package/dist/components/organisms/notificationGroup/NotificationGroup.svelte.d.ts +13 -2
  20. package/dist/components/organisms/notificationGroup/notificationGroup.variants.d.ts +72 -17
  21. package/dist/components/organisms/notificationGroup/notificationGroup.variants.js +14 -4
  22. package/dist/index.css +9 -1
  23. package/dist/index.d.ts +2 -1
  24. package/dist/index.js +2 -1
  25. package/dist/utils/notification.d.ts +6 -1
  26. package/dist/utils/notification.js +18 -4
  27. package/package.json +22 -23
  28. package/dist/components/molecules/notification/Notification.svelte.d.ts +0 -57
@@ -3,8 +3,8 @@ import { tv } from "../../../../utils/tailwind.js";
3
3
  const closeButtonVariants = tv({
4
4
  slots: {
5
5
  button:
6
- "relative cursor-pointer overflow-hidden bg-white p-1 transition duration-200 select-none dark:bg-neutral-800 dark:text-white",
7
- bar: "absolute inset-[0px] z-0 bg-gray-300 dark:bg-neutral-700",
6
+ "relative cursor-pointer overflow-hidden bg-neutral-100 p-1 text-neutral-700 transition duration-200 select-none dark:bg-neutral-900 dark:text-neutral-300",
7
+ bar: "absolute inset-0 z-0 bg-neutral-300 dark:bg-neutral-700",
8
8
  },
9
9
  variants: {
10
10
  shape: {
@@ -12,7 +12,7 @@ const closeButtonVariants = tv({
12
12
  button: "rounded-full",
13
13
  },
14
14
  square: {
15
- button: "rounded-md",
15
+ button: "rounded-sm",
16
16
  },
17
17
  },
18
18
  },
@@ -6,7 +6,7 @@
6
6
  * @typedef {Object} Props
7
7
  * @property {string} [as="button"] The HTML element to use for the button
8
8
  * @property {string} [class=""] Additional CSS classes to apply to the component
9
- * @property {"default" | "primary" | "secondary" | "success" | "danger" | "transparent"} [color="default"] Button color
9
+ * @property {"default" | "primary" | "secondary" | "success" | "error" | "transparent"} [color="default"] Button color
10
10
  * @property {boolean} [disabled=false] Whether the button is disabled
11
11
  * @property {boolean} [loading=false] Whether the button is in a loading state, for example after a form submission
12
12
  * @property {string} [icon=""] Icon name
@@ -15,7 +15,7 @@ declare const IconButton: import("svelte").Component<{
15
15
  /**
16
16
  * Button color
17
17
  */
18
- color?: "default" | "primary" | "secondary" | "success" | "danger" | "transparent";
18
+ color?: "default" | "primary" | "secondary" | "success" | "error" | "transparent";
19
19
  /**
20
20
  * Whether the button is disabled
21
21
  */
@@ -61,7 +61,7 @@ type Props = {
61
61
  /**
62
62
  * Button color
63
63
  */
64
- color?: "default" | "primary" | "secondary" | "success" | "danger" | "transparent";
64
+ color?: "default" | "primary" | "secondary" | "success" | "error" | "transparent";
65
65
  /**
66
66
  * Whether the button is disabled
67
67
  */
@@ -73,13 +73,13 @@ const iconButtonVariants = tv({
73
73
  "border-green-500 text-green-500 hover:border-green-700 hover:bg-green-700 hover:text-white dark:border-green-500 dark:text-green-500 dark:hover:border-green-700 dark:hover:bg-green-700",
74
74
  },
75
75
  {
76
- color: "danger",
76
+ color: "error",
77
77
  variant: "filled",
78
78
  class:
79
79
  "bg-red-500 text-white hover:bg-red-700 dark:bg-red-500 dark:hover:bg-red-700",
80
80
  },
81
81
  {
82
- color: "danger",
82
+ color: "error",
83
83
  variant: "outlined",
84
84
  class:
85
85
  "border-red-500 text-red-500 hover:border-red-700 hover:bg-red-700 hover:text-white dark:border-red-500 dark:text-red-500 dark:hover:border-red-700 dark:hover:bg-red-700",
@@ -4,10 +4,10 @@ const inputVariants = tv({
4
4
  extend: inputBaseVariant,
5
5
  slots: {
6
6
  adornmentStart:
7
- "pointer-events-none absolute top-1/2 left-4 -translate-y-1/2 body-small text-neutral-900 dark:text-neutral-200",
7
+ "pointer-events-none shrink-0 body-small text-neutral-900 dark:text-neutral-200",
8
8
  adornmentEnd:
9
- "pointer-events-none absolute top-1/2 right-4 -translate-y-1/2 body-small text-neutral-900 dark:text-neutral-200",
10
- base: "relative h-10",
9
+ "pointer-events-none shrink-0 body-small text-neutral-900 dark:text-neutral-200",
10
+ base: "flex h-10 items-center gap-2",
11
11
  input:
12
12
  "h-full w-full border-0 bg-transparent p-0 [font-size:inherit] leading-[inherit] focus:shadow-none focus:ring-0 focus:outline-0",
13
13
  },
@@ -4,6 +4,7 @@
4
4
 
5
5
  /**
6
6
  * @typedef {Object} Props
7
+ * @property {string|import("svelte").SvelteComponent|Function} [adornmentStart=""] Content to display at the start of the field (e.g., icon or text)
7
8
  * @property {"default" | "raised"} [buttonAppearance="default"] Appearance of the button variant
8
9
  * @property {"small" | "medium" | "large"} [buttonSize="medium"] Size of the button variant
9
10
  * @property {string} [class=""] Additional CSS classes for the select
@@ -19,6 +20,7 @@
19
20
 
20
21
  /** @type {Props} */
21
22
  let {
23
+ adornmentStart = "",
22
24
  buttonAppearance = "default",
23
25
  buttonSize = "medium",
24
26
  class: className = "",
@@ -43,6 +45,7 @@
43
45
  error: error ?? storeValues.error ?? false,
44
46
  id: id ?? storeValues.id ?? "",
45
47
  required: required ?? storeValues.required ?? false,
48
+ value: value ?? "",
46
49
  };
47
50
  });
48
51
 
@@ -50,32 +53,48 @@
50
53
  selectVariants({
51
54
  buttonAppearance,
52
55
  buttonSize,
53
- defaultValue: variant === "default" && (value === "" || value == null),
56
+ defaultValue: variant === "default" && localValues.value === "",
54
57
  error: localValues.error,
55
58
  variant,
56
59
  }),
57
60
  );
61
+
62
+ const setValue = (v) => (value = v);
58
63
  </script>
59
64
 
60
- <select
61
- class={styles.base({ class: className, disabled: localValues.disabled })}
62
- disabled={localValues.disabled}
63
- id={localValues.id}
64
- required={localValues.required}
65
- bind:value
66
- {...rest}>
67
- {#if placeholder}
68
- <option class={styles.option({ disabled: true })} hidden readonly value=""
69
- >{placeholder}</option>
65
+ <div class={styles.base({ class: className, disabled: localValues.disabled })}>
66
+ {#if adornmentStart}
67
+ <div class={styles.adornmentStart()}>
68
+ {#if typeof adornmentStart === "function"}
69
+ {@render adornmentStart()}
70
+ {:else}
71
+ {adornmentStart}
72
+ {/if}
73
+ </div>
70
74
  {/if}
71
- {#each options as { disabled, label, value: optValue } (optValue)}
72
- <option
73
- class={styles.option({ disabled })}
74
- {disabled}
75
- selected={value === optValue}
76
- value={optValue}>{label}</option>
77
- {/each}
78
- </select>
75
+ <select
76
+ class={styles.select()}
77
+ disabled={localValues.disabled}
78
+ id={localValues.id}
79
+ required={localValues.required}
80
+ bind:value={() => localValues.value, setValue}
81
+ {...rest}>
82
+ {#if placeholder}
83
+ <option
84
+ class={styles.option({ disabled: localValues.required })}
85
+ hidden={localValues.required}
86
+ readonly
87
+ value="">{placeholder}</option>
88
+ {/if}
89
+ {#each options as { disabled, label, value: optValue } (optValue)}
90
+ <option
91
+ class={styles.option({ disabled })}
92
+ {disabled}
93
+ selected={value === optValue}
94
+ value={optValue}>{label}</option>
95
+ {/each}
96
+ </select>
97
+ </div>
79
98
 
80
99
  <style>
81
100
  select {
@@ -4,6 +4,10 @@ type Select = {
4
4
  $set?(props: Partial<Props>): void;
5
5
  };
6
6
  declare const Select: import("svelte").Component<{
7
+ /**
8
+ * Content to display at the start of the field (e.g., icon or text)
9
+ */
10
+ adornmentStart?: string | import("svelte").SvelteComponent | Function;
7
11
  /**
8
12
  * Appearance of the button variant
9
13
  */
@@ -54,6 +58,10 @@ declare const Select: import("svelte").Component<{
54
58
  variant?: "default" | "button";
55
59
  }, {}, "value">;
56
60
  type Props = {
61
+ /**
62
+ * Content to display at the start of the field (e.g., icon or text)
63
+ */
64
+ adornmentStart?: string | import("svelte").SvelteComponent | Function;
57
65
  /**
58
66
  * Appearance of the button variant
59
67
  */
@@ -2,6 +2,7 @@ export const selectVariants: import("tailwind-variants").TVReturnType<{
2
2
  disabled: {
3
3
  false: {
4
4
  option: string;
5
+ select: string;
5
6
  };
6
7
  true: {
7
8
  option: string;
@@ -9,15 +10,19 @@ export const selectVariants: import("tailwind-variants").TVReturnType<{
9
10
  };
10
11
  variant: {
11
12
  default: {
13
+ adornmentStart: string;
12
14
  base: string;
13
15
  };
14
16
  button: {
17
+ adornmentStart: string;
15
18
  base: string;
16
19
  };
17
20
  };
18
21
  }, {
19
- base: string[];
22
+ adornmentStart: string;
23
+ base: string;
20
24
  option: string[];
25
+ select: string[];
21
26
  }, undefined, {
22
27
  disabled: {
23
28
  true: {
@@ -3,29 +3,32 @@ import { inputBaseVariant, tv } from "../../../utils/tailwind.js";
3
3
  const selectVariants = tv({
4
4
  extend: inputBaseVariant,
5
5
  slots: {
6
- base: [
7
- "flex items-center",
8
- "[&::picker(select)]:my-1 [&::picker(select)]:rounded-sm [&::picker(select)]:border-0 [&::picker(select)]:p-1",
9
- "[&::picker-icon]:hidden",
10
- "[&::picker(select)]:bg-white [&::picker(select)]:shadow-sm dark:[&::picker(select)]:bg-neutral-800",
11
- "bg-none after:bg-current after:bg-size-[100%] after:bg-center after:bg-no-repeat",
12
- "after:ml-auto after:size-5 after:shrink-0",
13
- "[&:open]:after:rotate-180",
14
- ],
6
+ adornmentStart: "pointer-events-none shrink-0 body-small leading-[inherit]",
7
+ base: "flex items-center ui-select-label",
15
8
  option: [
16
- "flex rounded-sm p-2 ui-select-label",
9
+ "flex rounded-sm p-2",
17
10
  "bg-white dark:bg-neutral-800",
18
11
  "bg-linear-to-r to-transparent",
19
12
  "[&::checkmark]:order-1 [&::checkmark]:ml-auto [&::checkmark]:shrink-0",
20
13
  "[&::checkmark]:bg-current [&::checkmark]:bg-size-[100%] [&::checkmark]:bg-center [&::checkmark]:bg-no-repeat",
21
14
  "[&::checkmark]:size-4 [&::checkmark]:content-['']",
22
15
  ],
16
+ select: [
17
+ "flex size-full items-center border-0 bg-transparent p-0 [font-size:inherit] leading-[inherit] focus:shadow-none focus:ring-0 focus:outline-0",
18
+ "[&::picker(select)]:my-1 [&::picker(select)]:rounded-sm [&::picker(select)]:border-0 [&::picker(select)]:p-1",
19
+ "[&::picker-icon]:hidden",
20
+ "[&::picker(select)]:bg-white [&::picker(select)]:shadow-sm dark:[&::picker(select)]:bg-neutral-800",
21
+ "bg-none after:bg-current after:bg-size-[100%] after:bg-center after:bg-no-repeat",
22
+ "after:ml-auto after:size-5 after:shrink-0",
23
+ "[&:open]:after:rotate-180",
24
+ ],
23
25
  },
24
26
  variants: {
25
27
  disabled: {
26
28
  false: {
27
29
  option:
28
30
  "cursor-pointer text-neutral-900 hover:from-black/15 dark:text-neutral-200 dark:hover:from-white/15",
31
+ select: "cursor-pointer",
29
32
  },
30
33
  true: {
31
34
  option: "text-neutral-500 dark:text-neutral-500 [&::checkmark]:hidden",
@@ -33,9 +36,11 @@ const selectVariants = tv({
33
36
  },
34
37
  variant: {
35
38
  default: {
36
- base: "min-h-10 border border-neutral-300 pr-2 pl-4",
39
+ adornmentStart: "text-neutral-900 dark:text-neutral-200",
40
+ base: "min-h-10 gap-2 border border-neutral-300 pr-2 pl-4",
37
41
  },
38
42
  button: {
43
+ adornmentStart: "text-neutral-500",
39
44
  base: "gap-0.5 border-none bg-white pr-1 pl-2 leading-5 ring-transparent! dark:bg-neutral-800",
40
45
  },
41
46
  },
@@ -65,7 +70,7 @@ const selectVariants = tv({
65
70
  variant: "button",
66
71
  disabled: false,
67
72
  class: {
68
- base: "cursor-pointer focus-within:bg-neutral-300 hover:bg-neutral-300 focus:ring-2 focus:ring-blue-500 dark:focus-within:bg-neutral-600 dark:hover:bg-neutral-600",
73
+ base: "focus-within:bg-neutral-300 hover:bg-neutral-300 focus:ring-2 focus:ring-blue-500 dark:focus-within:bg-neutral-600 dark:hover:bg-neutral-600",
69
74
  },
70
75
  },
71
76
  {
@@ -1,15 +1,15 @@
1
1
  <script>
2
- import { CloseButton, Icon } from "../../../index.js";
3
- import { notificationVariants } from "./notification.variants.js";
2
+ import { CloseButton, Icon } from "../../../../index.js";
3
+ import { systemAlertVariants } from "./systemAlert.variants.js";
4
4
 
5
5
  /**
6
6
  * @typedef {Object} Props
7
- * @property {boolean} [autoClose=true] Whether to automatically close the notification after a duration
7
+ * @property {boolean} [autoClose=true] Whether to automatically close the system alert after a duration
8
8
  * @property {string} [class=""] Additional CSS classes to apply to the component
9
- * @property {string} [icon=""] Icon to display in the notification
10
- * @property {string} [message=""] Message to display in the notification
11
- * @property {string} [title=""] Title to display in the notification
12
- * @property {"info" | "success" | "warning" | "danger"} [variant="info"] Notification variant
9
+ * @property {string} [icon=""] Icon to display in the system alert
10
+ * @property {string} [message=""] Message to display in the system alert
11
+ * @property {string} [title=""] Title to display in the system alert
12
+ * @property {"info" | "success" | "warning" | "error"} [variant="info"] System alert variant
13
13
  */
14
14
 
15
15
  /** @type {Props} */
@@ -24,7 +24,7 @@
24
24
  ...rest
25
25
  } = $props();
26
26
 
27
- let styles = $derived(notificationVariants({ variant }));
27
+ let styles = $derived(systemAlertVariants({ variant }));
28
28
  </script>
29
29
 
30
30
  <div class={styles.base({ class: className })} {...rest}>
@@ -0,0 +1,57 @@
1
+ export default SystemAlert;
2
+ type SystemAlert = {
3
+ $on?(type: string, callback: (e: any) => void): () => void;
4
+ $set?(props: Partial<Props>): void;
5
+ };
6
+ declare const SystemAlert: import("svelte").Component<{
7
+ /**
8
+ * Whether to automatically close the system alert after a duration
9
+ */
10
+ autoClose?: boolean;
11
+ /**
12
+ * Additional CSS classes to apply to the component
13
+ */
14
+ class?: string;
15
+ /**
16
+ * Icon to display in the system alert
17
+ */
18
+ icon?: string;
19
+ /**
20
+ * Message to display in the system alert
21
+ */
22
+ message?: string;
23
+ /**
24
+ * Title to display in the system alert
25
+ */
26
+ title?: string;
27
+ /**
28
+ * System alert variant
29
+ */
30
+ variant?: "info" | "success" | "warning" | "error";
31
+ }, {}, "">;
32
+ type Props = {
33
+ /**
34
+ * Whether to automatically close the system alert after a duration
35
+ */
36
+ autoClose?: boolean;
37
+ /**
38
+ * Additional CSS classes to apply to the component
39
+ */
40
+ class?: string;
41
+ /**
42
+ * Icon to display in the system alert
43
+ */
44
+ icon?: string;
45
+ /**
46
+ * Message to display in the system alert
47
+ */
48
+ message?: string;
49
+ /**
50
+ * Title to display in the system alert
51
+ */
52
+ title?: string;
53
+ /**
54
+ * System alert variant
55
+ */
56
+ variant?: "info" | "success" | "warning" | "error";
57
+ };
@@ -1,4 +1,4 @@
1
- export const notificationVariants: import("tailwind-variants").TVReturnType<{
1
+ export const systemAlertVariants: import("tailwind-variants").TVReturnType<{
2
2
  variant: {
3
3
  info: {
4
4
  icon: string;
@@ -9,7 +9,7 @@ export const notificationVariants: import("tailwind-variants").TVReturnType<{
9
9
  warning: {
10
10
  icon: string;
11
11
  };
12
- danger: {
12
+ error: {
13
13
  icon: string;
14
14
  };
15
15
  };
@@ -31,7 +31,7 @@ export const notificationVariants: import("tailwind-variants").TVReturnType<{
31
31
  warning: {
32
32
  icon: string;
33
33
  };
34
- danger: {
34
+ error: {
35
35
  icon: string;
36
36
  };
37
37
  };
@@ -53,7 +53,7 @@ export const notificationVariants: import("tailwind-variants").TVReturnType<{
53
53
  warning: {
54
54
  icon: string;
55
55
  };
56
- danger: {
56
+ error: {
57
57
  icon: string;
58
58
  };
59
59
  };
@@ -1,13 +1,13 @@
1
- import { tv } from "../../../utils/tailwind.js";
1
+ import { tv } from "../../../../utils/tailwind.js";
2
2
 
3
- const notificationVariants = tv({
3
+ const systemAlertVariants = tv({
4
4
  slots: {
5
- base: "grid grid-cols-[40px_1fr_24px] gap-2 rounded-lg border border-neutral-300 bg-white p-2 dark:border-neutral-700 dark:bg-neutral-900",
5
+ base: "grid grid-cols-[40px_1fr_24px] gap-2 rounded-lg border border-neutral-200 bg-white/90 p-2 backdrop-blur-sm dark:border-neutral-700 dark:bg-neutral-800/90",
6
6
  close: "col-3 self-start",
7
7
  icon: "flex h-10 w-10 items-center justify-center rounded-sm text-white",
8
8
  content: "flex flex-col justify-center",
9
9
  message: "ui-caption-helper text-neutral-500",
10
- title: "body-small dark:text-white",
10
+ title: "body-small dark:text-neutral-200",
11
11
  },
12
12
  variants: {
13
13
  variant: {
@@ -20,11 +20,11 @@ const notificationVariants = tv({
20
20
  warning: {
21
21
  icon: "bg-yellow-500",
22
22
  },
23
- danger: {
23
+ error: {
24
24
  icon: "bg-red-500",
25
25
  },
26
26
  },
27
27
  },
28
28
  });
29
29
 
30
- export { notificationVariants };
30
+ export { systemAlertVariants };
@@ -0,0 +1,51 @@
1
+ <script>
2
+ import { Icon, Spinner } from "../../../../index.js";
3
+ import { toastVariants } from "./toast.variants.js";
4
+
5
+ /**
6
+ * @typedef {Object} Props
7
+ * @property {string} [class=""] Additional CSS classes to apply to the component
8
+ * @property {number} [durationInSeconds=6] Duration in seconds before auto-close triggers
9
+ * @property {string} [message=""] Message to display in the toast
10
+ * @property {(event) => void} [onClose] Function to call when the toast is auto-closed
11
+ * @property {"info" | "loading" | "error"} [variant="info"] Toast variant
12
+ */
13
+
14
+ const ICONS_BY_VARIANT = {
15
+ error: "closeSmall",
16
+ info: "check",
17
+ };
18
+
19
+ /** @type {Props} */
20
+ let {
21
+ class: className = "",
22
+ durationInSeconds = 6,
23
+ message = "",
24
+ onClose,
25
+ variant = "info",
26
+ ...rest
27
+ } = $props();
28
+
29
+ let to;
30
+ let styles = $derived(toastVariants({ variant }));
31
+
32
+ $effect(() => {
33
+ clearTimeout(to);
34
+ if (variant !== "loading" && durationInSeconds > 0) {
35
+ to = setTimeout(() => {
36
+ onClose?.();
37
+ }, durationInSeconds * 1e3);
38
+ }
39
+ });
40
+ </script>
41
+
42
+ <div class={styles.base({ class: className })} {...rest}>
43
+ <div class={styles.icon()}>
44
+ {#if variant === "loading"}
45
+ <Spinner size="small" />
46
+ {:else}
47
+ <Icon name={ICONS_BY_VARIANT[variant]} size={24} />
48
+ {/if}
49
+ </div>
50
+ <p class={styles.message()}>{message}</p>
51
+ </div>
@@ -0,0 +1,49 @@
1
+ export default Toast;
2
+ type Toast = {
3
+ $on?(type: string, callback: (e: any) => void): () => void;
4
+ $set?(props: Partial<Props>): void;
5
+ };
6
+ declare const Toast: import("svelte").Component<{
7
+ /**
8
+ * Additional CSS classes to apply to the component
9
+ */
10
+ class?: string;
11
+ /**
12
+ * Duration in seconds before auto-close triggers
13
+ */
14
+ durationInSeconds?: number;
15
+ /**
16
+ * Message to display in the toast
17
+ */
18
+ message?: string;
19
+ /**
20
+ * Function to call when the toast is auto-closed
21
+ */
22
+ onClose?: (event: any) => void;
23
+ /**
24
+ * Toast variant
25
+ */
26
+ variant?: "info" | "loading" | "error";
27
+ }, {}, "">;
28
+ type Props = {
29
+ /**
30
+ * Additional CSS classes to apply to the component
31
+ */
32
+ class?: string;
33
+ /**
34
+ * Duration in seconds before auto-close triggers
35
+ */
36
+ durationInSeconds?: number;
37
+ /**
38
+ * Message to display in the toast
39
+ */
40
+ message?: string;
41
+ /**
42
+ * Function to call when the toast is auto-closed
43
+ */
44
+ onClose?: (event: any) => void;
45
+ /**
46
+ * Toast variant
47
+ */
48
+ variant?: "info" | "loading" | "error";
49
+ };
@@ -0,0 +1,31 @@
1
+ export const toastVariants: import("tailwind-variants").TVReturnType<{
2
+ variant: {
3
+ error: {
4
+ base: string;
5
+ };
6
+ };
7
+ }, {
8
+ base: string;
9
+ icon: string;
10
+ message: string;
11
+ }, undefined, {
12
+ variant: {
13
+ error: {
14
+ base: string;
15
+ };
16
+ };
17
+ }, {
18
+ base: string;
19
+ icon: string;
20
+ message: string;
21
+ }, import("tailwind-variants").TVReturnType<{
22
+ variant: {
23
+ error: {
24
+ base: string;
25
+ };
26
+ };
27
+ }, {
28
+ base: string;
29
+ icon: string;
30
+ message: string;
31
+ }, undefined, unknown, unknown, undefined>>;
@@ -0,0 +1,18 @@
1
+ import { tv } from "../../../../utils/tailwind.js";
2
+
3
+ const toastVariants = tv({
4
+ slots: {
5
+ base: "flex items-center gap-2 rounded-lg bg-neutral-900 px-4 py-2 text-neutral-200",
6
+ icon: "grid size-6 place-items-center",
7
+ message: "ui-select-label font-medium",
8
+ },
9
+ variants: {
10
+ variant: {
11
+ error: {
12
+ base: "bg-red-500",
13
+ },
14
+ },
15
+ },
16
+ });
17
+
18
+ export { toastVariants };
@@ -1,68 +1,100 @@
1
1
  <script>
2
- import { onMount } from "svelte";
3
- import { fly } from "svelte/transition";
2
+ import { settled } from "svelte";
4
3
  import { cubicOut } from "svelte/easing";
5
- import { Notification } from "../../../index.js";
4
+ import { fly } from "svelte/transition";
5
+ import { SvelteMap } from "svelte/reactivity";
6
+ import { NOTIFICATION_POSITIONS } from "../../../utils/notification.js";
6
7
  import { notificationGroupVariants } from "./notificationGroup.variants.js";
7
8
 
8
- onMount(() => {
9
- document.body.addEventListener("notification:show", addNotification);
9
+ const MOVE_MS = 400;
10
+ const IN_MS = 220;
11
+ const STAGGER = MOVE_MS - 200;
10
12
 
11
- return () => {
12
- document.body.removeEventListener("notification:show", addNotification);
13
- };
14
- });
13
+ /**
14
+ * @typedef {Object} Props
15
+ * @property {string} [class=""] Additional CSS classes to apply to each notification column
16
+ */
15
17
 
16
- const addNotification = (e) => {
17
- const { detail } = e;
18
+ /** @type {Props} */
19
+ let { class: className = "" } = $props();
18
20
 
19
- if (detail.position === "bottom-left") {
20
- dataBottomLeft.push(detail);
21
- } else if (detail.position === "bottom-right") {
22
- dataBottomRight.push(detail);
21
+ let data = new SvelteMap();
22
+ let showColumns = $state(false);
23
+
24
+ const zPoints = {};
25
+ let zOrder = $state([]);
26
+
27
+ let styles = $derived(notificationGroupVariants());
28
+
29
+ const handleShow = async (e) => {
30
+ if (!showColumns) {
31
+ showColumns = true;
32
+ await settled();
23
33
  }
34
+
35
+ const { detail } = e;
36
+ data.set(detail.id, detail);
37
+
38
+ updateZOrder(detail.position);
24
39
  };
25
40
 
26
- let dataBottomLeft = $state([]);
27
- let dataBottomRight = $state([]);
41
+ const handleClose = (id) => {
42
+ data.delete(id);
43
+ };
28
44
 
29
- let { bottomLeft, bottomRight } = $derived(notificationGroupVariants());
45
+ const handleLastClose = () => {
46
+ showColumns = false;
47
+ };
30
48
 
31
- const MOVE_MS = 400;
32
- const IN_MS = 220;
33
- const STAGGER = MOVE_MS - 200;
49
+ // ensure the column with the latest update is on top
50
+ const updateZOrder = (topPosition) => {
51
+ zPoints[topPosition] = Date.now();
34
52
 
35
- const handleClose = (id, position) => {
36
- if (position === "bottom-left") {
37
- const index = dataBottomLeft.findIndex((n) => n.id === id);
38
- if (index !== -1) dataBottomLeft.splice(index, 1);
39
- } else if (position === "bottom-right") {
40
- const index = dataBottomRight.findIndex((n) => n.id === id);
41
- if (index !== -1) dataBottomRight.splice(index, 1);
42
- }
53
+ zOrder = Object.entries(zPoints)
54
+ .sort(([, aTs], [, bTs]) => aTs - bTs)
55
+ .map(([key]) => key);
43
56
  };
44
57
  </script>
45
58
 
46
- {#each [{ data: dataBottomLeft, class: bottomLeft(), x: -32, position: "bottom-left" }, { data: dataBottomRight, class: bottomRight(), x: 32, position: "bottom-right" }] as { data, class: cls, x, position }, idx (idx)}
47
- <div class={cls}>
48
- {#each data as item, i (item.id)}
59
+ <svelte:body onnotification:show={handleShow} />
60
+
61
+ {#snippet column(position, x = 0, y = 0)}
62
+ {@const items = [...data.values()].filter((item) => item.position === position)}
63
+ <div
64
+ class={styles.base({
65
+ class: className,
66
+ position,
67
+ zOrder: zOrder.findIndex((key) => key === position),
68
+ })}>
69
+ {#each items as { id, el, props }, i (id)}
49
70
  <div
50
71
  in:fly={{
51
72
  x,
52
- y: 0,
73
+ y,
53
74
  duration: IN_MS,
54
- delay: i === data.length - 1 ? STAGGER : 0,
75
+ delay: i === items.length - 1 ? STAGGER : 0,
55
76
  easing: cubicOut,
56
77
  }}
57
78
  out:fly={{
58
79
  x,
59
- y: 0,
80
+ y,
60
81
  duration: IN_MS,
61
- delay: i === data.length - 1 ? STAGGER : 0,
82
+ delay: i === items.length - 1 ? STAGGER : 0,
62
83
  easing: cubicOut,
63
- }}>
64
- <Notification {...item} onClose={() => handleClose(item.id, position)} />
84
+ }}
85
+ onoutroend={data.size === 0 ? handleLastClose : null}>
86
+ {@render el?.({
87
+ ...props,
88
+ class: styles.item({ class: props.class }),
89
+ onClose: () => handleClose(id),
90
+ })}
65
91
  </div>
66
92
  {/each}
67
93
  </div>
68
- {/each}
94
+ {/snippet}
95
+
96
+ {#if showColumns}
97
+ {@render column(NOTIFICATION_POSITIONS.BOTTOM_LEFT, -32)}
98
+ {@render column(NOTIFICATION_POSITIONS.BOTTOM_CENTER, 0, 32)}
99
+ {@render column(NOTIFICATION_POSITIONS.BOTTOM_RIGHT, 32)}
100
+ {/if}
@@ -1,6 +1,17 @@
1
1
  export default NotificationGroup;
2
2
  type NotificationGroup = {
3
3
  $on?(type: string, callback: (e: any) => void): () => void;
4
- $set?(props: Partial<Record<string, never>>): void;
4
+ $set?(props: Partial<Props>): void;
5
+ };
6
+ declare const NotificationGroup: import("svelte").Component<{
7
+ /**
8
+ * Additional CSS classes to apply to each notification column
9
+ */
10
+ class?: string;
11
+ }, {}, "">;
12
+ type Props = {
13
+ /**
14
+ * Additional CSS classes to apply to each notification column
15
+ */
16
+ class?: string;
5
17
  };
6
- declare const NotificationGroup: import("svelte").Component<Record<string, never>, {}, "">;
@@ -1,24 +1,79 @@
1
1
  export const notificationGroupVariants: import("tailwind-variants").TVReturnType<{
2
- [key: string]: {
3
- [key: string]: import("tailwind-variants").ClassValue | {
4
- bottomLeft?: import("tailwind-variants").ClassValue;
5
- bottomRight?: import("tailwind-variants").ClassValue;
2
+ position: {
3
+ bc: {
4
+ base: string;
5
+ };
6
+ bl: {
7
+ base: string;
8
+ };
9
+ br: {
10
+ base: string;
11
+ };
12
+ };
13
+ zOrder: {
14
+ 0: {
15
+ base: string;
16
+ };
17
+ 1: {
18
+ base: string;
19
+ };
20
+ 2: {
21
+ base: string;
22
+ };
23
+ };
24
+ }, {
25
+ base: string;
26
+ item: string;
27
+ }, undefined, {
28
+ position: {
29
+ bc: {
30
+ base: string;
31
+ };
32
+ bl: {
33
+ base: string;
34
+ };
35
+ br: {
36
+ base: string;
6
37
  };
7
38
  };
8
- } | {
9
- [x: string]: {
10
- [x: string]: import("tailwind-variants").ClassValue | {
11
- bottomLeft?: import("tailwind-variants").ClassValue;
12
- bottomRight?: import("tailwind-variants").ClassValue;
39
+ zOrder: {
40
+ 0: {
41
+ base: string;
42
+ };
43
+ 1: {
44
+ base: string;
45
+ };
46
+ 2: {
47
+ base: string;
48
+ };
49
+ };
50
+ }, {
51
+ base: string;
52
+ item: string;
53
+ }, import("tailwind-variants").TVReturnType<{
54
+ position: {
55
+ bc: {
56
+ base: string;
57
+ };
58
+ bl: {
59
+ base: string;
60
+ };
61
+ br: {
62
+ base: string;
63
+ };
64
+ };
65
+ zOrder: {
66
+ 0: {
67
+ base: string;
68
+ };
69
+ 1: {
70
+ base: string;
71
+ };
72
+ 2: {
73
+ base: string;
13
74
  };
14
75
  };
15
76
  }, {
16
- bottomLeft: string;
17
- bottomRight: string;
18
- }, undefined, any, {
19
- bottomLeft: string;
20
- bottomRight: string;
21
- }, import("tailwind-variants").TVReturnType<any, {
22
- bottomLeft: string;
23
- bottomRight: string;
77
+ base: string;
78
+ item: string;
24
79
  }, undefined, unknown, unknown, undefined>>;
@@ -2,10 +2,20 @@ import { tv } from "../../../utils/tailwind.js";
2
2
 
3
3
  const notificationGroupVariants = tv({
4
4
  slots: {
5
- bottomLeft:
6
- "absolute bottom-4 left-4 z-snackbar flex flex-col gap-2 overflow-hidden",
7
- bottomRight:
8
- "absolute right-4 bottom-4 z-snackbar flex flex-col gap-2 overflow-hidden",
5
+ base: "pointer-events-none fixed inset-x-0 bottom-0 flex flex-col gap-2 overflow-hidden p-4",
6
+ item: "pointer-events-auto max-w-xs shadow-sm",
7
+ },
8
+ variants: {
9
+ position: {
10
+ bc: { base: "items-center" },
11
+ bl: { base: "items-start" },
12
+ br: { base: "items-end" },
13
+ },
14
+ zOrder: {
15
+ 0: { base: "z-10000" },
16
+ 1: { base: "z-10001" },
17
+ 2: { base: "z-10002" },
18
+ },
9
19
  },
10
20
  });
11
21
 
package/dist/index.css CHANGED
@@ -136,7 +136,7 @@
136
136
  --font-body: "Borna", "sans-serif";
137
137
  --font-sans: "Borna", sans-serif;
138
138
 
139
- /* Box Shadows */
139
+ /* Box Shadows - NOTE: dark mode values defined bellow theme */
140
140
 
141
141
  --shadow-sm: 0 2px 16px 0 rgba(0, 0, 0, 0.08);
142
142
  --shadow-toggle: 0 2px 4px 0 rgba(0, 0, 0, 0.24);
@@ -220,3 +220,11 @@
220
220
  --color-green-800: #2f461a;
221
221
  --color-green-900: #18230d;
222
222
  }
223
+
224
+ @layer utilities {
225
+ &:where(.dark, .dark *) {
226
+ .shadow-sm {
227
+ --tw-shadow: 0 2px 16px 0 var(--tw-shadow-color, rgba(0, 0, 0, 0.24));
228
+ }
229
+ }
230
+ }
package/dist/index.d.ts CHANGED
@@ -26,10 +26,11 @@ export { default as ComboBox } from "./components/molecules/comboBox/ComboBox.sv
26
26
  export { default as FormActions } from "./components/molecules/formActions/FormActions.svelte";
27
27
  export { default as FormField } from "./components/molecules/formField/FormField.svelte";
28
28
  export { default as FormLayout } from "./components/molecules/formLayout/FormLayout.svelte";
29
- export { default as Notification } from "./components/molecules/notification/Notification.svelte";
30
29
  export { default as RichTextEditor } from "./components/molecules/richTextEditor/RichTextEditor.svelte";
31
30
  export { default as SelectorCard } from "./components/molecules/selectorCard/SelectorCard.svelte";
31
+ export { default as SystemAlert } from "./components/molecules/notifications/systemAlert/SystemAlert.svelte";
32
32
  export { default as TagSelector } from "./components/molecules/tagSelector/TagSelector.svelte";
33
+ export { default as Toast } from "./components/molecules/notifications/toast/Toast.svelte";
33
34
  export { default as ToggleGroup } from "./components/molecules/toggleGroup/ToggleGroup.svelte";
34
35
  export { default as ToggleGroupItem } from "./components/molecules/toggleGroup/ToggleGroupItem.svelte";
35
36
  export * as Dialog from "./components/organisms/dialog/index.js";
package/dist/index.js CHANGED
@@ -36,10 +36,11 @@ export { default as ComboBox } from "./components/molecules/comboBox/ComboBox.sv
36
36
  export { default as FormActions } from "./components/molecules/formActions/FormActions.svelte";
37
37
  export { default as FormField } from "./components/molecules/formField/FormField.svelte";
38
38
  export { default as FormLayout } from "./components/molecules/formLayout/FormLayout.svelte";
39
- export { default as Notification } from "./components/molecules/notification/Notification.svelte";
40
39
  export { default as RichTextEditor } from "./components/molecules/richTextEditor/RichTextEditor.svelte";
41
40
  export { default as SelectorCard } from "./components/molecules/selectorCard/SelectorCard.svelte";
41
+ export { default as SystemAlert } from "./components/molecules/notifications/systemAlert/SystemAlert.svelte";
42
42
  export { default as TagSelector } from "./components/molecules/tagSelector/TagSelector.svelte";
43
+ export { default as Toast } from "./components/molecules/notifications/toast/Toast.svelte";
43
44
  export { default as ToggleGroup } from "./components/molecules/toggleGroup/ToggleGroup.svelte";
44
45
  export { default as ToggleGroupItem } from "./components/molecules/toggleGroup/ToggleGroupItem.svelte";
45
46
 
@@ -1 +1,6 @@
1
- export function showNotification(props: any): void;
1
+ export namespace NOTIFICATION_POSITIONS {
2
+ let BOTTOM_CENTER: string;
3
+ let BOTTOM_LEFT: string;
4
+ let BOTTOM_RIGHT: string;
5
+ }
6
+ export function showNotification(el: any, position?: string, props?: {}): string;
@@ -1,11 +1,25 @@
1
- import { v4 as uuidv4 } from "uuid";
1
+ import { generateId } from "./a11y.js";
2
+
3
+ const NOTIFICATION_POSITIONS = {
4
+ BOTTOM_CENTER: "bc",
5
+ BOTTOM_LEFT: "bl",
6
+ BOTTOM_RIGHT: "br",
7
+ };
8
+
9
+ const showNotification = (
10
+ el,
11
+ position = NOTIFICATION_POSITIONS.BOTTOM_LEFT,
12
+ props = {},
13
+ ) => {
14
+ const id = generateId();
2
15
 
3
- const showNotification = (props) => {
4
16
  document.body.dispatchEvent(
5
17
  new CustomEvent("notification:show", {
6
- detail: { id: uuidv4(), ...props },
18
+ detail: { el, id, position, props },
7
19
  }),
8
20
  );
21
+
22
+ return id;
9
23
  };
10
24
 
11
- export { showNotification };
25
+ export { NOTIFICATION_POSITIONS, showNotification };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@websline/system-components",
3
- "version": "1.3.25",
3
+ "version": "1.3.26",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -33,18 +33,17 @@
33
33
  }
34
34
  },
35
35
  "dependencies": {
36
- "@tiptap/core": "^3.20.4",
37
- "@tiptap/extension-color": "^3.20.4",
38
- "@tiptap/extension-highlight": "^3.20.4",
39
- "@tiptap/extension-placeholder": "^3.20.4",
40
- "@tiptap/extension-text-align": "^3.20.4",
41
- "@tiptap/extension-text-style": "^3.20.4",
42
- "@tiptap/pm": "^3.20.4",
43
- "@tiptap/starter-kit": "^3.20.4",
36
+ "@tiptap/core": "^3.20.5",
37
+ "@tiptap/extension-color": "^3.20.5",
38
+ "@tiptap/extension-highlight": "^3.20.5",
39
+ "@tiptap/extension-placeholder": "^3.20.5",
40
+ "@tiptap/extension-text-align": "^3.20.5",
41
+ "@tiptap/extension-text-style": "^3.20.5",
42
+ "@tiptap/pm": "^3.20.5",
43
+ "@tiptap/starter-kit": "^3.20.5",
44
44
  "bits-ui": "^2.16.3",
45
45
  "dompurify": "^3.3.3",
46
- "tailwind-variants": "^3.2.2",
47
- "uuid": "^13.0.0"
46
+ "tailwind-variants": "^3.2.2"
48
47
  },
49
48
  "peerDependencies": {
50
49
  "svelte": "^5.38.7"
@@ -52,10 +51,10 @@
52
51
  "devDependencies": {
53
52
  "@eslint/compat": "^2.0.3",
54
53
  "@eslint/js": "^10.0.1",
55
- "@storybook/addon-a11y": "^10.3.0",
56
- "@storybook/addon-docs": "^10.3.0",
57
- "@storybook/addon-svelte-csf": "^5.0.12",
58
- "@storybook/sveltekit": "^10.3.0",
54
+ "@storybook/addon-a11y": "^10.3.3",
55
+ "@storybook/addon-docs": "^10.3.3",
56
+ "@storybook/addon-svelte-csf": "^5.1.0",
57
+ "@storybook/sveltekit": "^10.3.3",
59
58
  "@sveltejs/adapter-auto": "^7.0.1",
60
59
  "@sveltejs/kit": "^2.55.0",
61
60
  "@sveltejs/package": "^2.5.7",
@@ -64,11 +63,11 @@
64
63
  "@tailwindcss/typography": "^0.5.19",
65
64
  "@tailwindcss/vite": "^4.2.2",
66
65
  "@types/node": "^25.5.0",
67
- "@vitest/browser": "^4.1.0",
68
- "eslint": "^10.0.3",
66
+ "@vitest/browser": "^4.1.1",
67
+ "eslint": "^10.1.0",
69
68
  "eslint-config-prettier": "^10.1.8",
70
- "eslint-plugin-storybook": "^10.3.0",
71
- "eslint-plugin-svelte": "^3.15.2",
69
+ "eslint-plugin-storybook": "^10.3.3",
70
+ "eslint-plugin-svelte": "^3.16.0",
72
71
  "globals": "^17.4.0",
73
72
  "playwright": "^1.58.2",
74
73
  "postcss-url": "^10.1.3",
@@ -76,12 +75,12 @@
76
75
  "prettier-plugin-svelte": "^3.5.1",
77
76
  "prettier-plugin-tailwindcss": "^0.7.2",
78
77
  "publint": "^0.3.18",
79
- "storybook": "^10.3.0",
80
- "svelte": "^5.54.0",
78
+ "storybook": "^10.3.3",
79
+ "svelte": "^5.55.0",
81
80
  "tailwindcss": "^4.2.2",
82
81
  "typescript": "^5.9.3",
83
- "vite": "^8.0.1",
84
- "vitest": "^4.1.0",
82
+ "vite": "^8.0.2",
83
+ "vitest": "^4.1.1",
85
84
  "vitest-browser-svelte": "^2.1.0"
86
85
  },
87
86
  "keywords": [
@@ -1,57 +0,0 @@
1
- export default Notification;
2
- type Notification = {
3
- $on?(type: string, callback: (e: any) => void): () => void;
4
- $set?(props: Partial<Props>): void;
5
- };
6
- declare const Notification: import("svelte").Component<{
7
- /**
8
- * Whether to automatically close the notification after a duration
9
- */
10
- autoClose?: boolean;
11
- /**
12
- * Additional CSS classes to apply to the component
13
- */
14
- class?: string;
15
- /**
16
- * Icon to display in the notification
17
- */
18
- icon?: string;
19
- /**
20
- * Message to display in the notification
21
- */
22
- message?: string;
23
- /**
24
- * Title to display in the notification
25
- */
26
- title?: string;
27
- /**
28
- * Notification variant
29
- */
30
- variant?: "info" | "success" | "warning" | "danger";
31
- }, {}, "">;
32
- type Props = {
33
- /**
34
- * Whether to automatically close the notification after a duration
35
- */
36
- autoClose?: boolean;
37
- /**
38
- * Additional CSS classes to apply to the component
39
- */
40
- class?: string;
41
- /**
42
- * Icon to display in the notification
43
- */
44
- icon?: string;
45
- /**
46
- * Message to display in the notification
47
- */
48
- message?: string;
49
- /**
50
- * Title to display in the notification
51
- */
52
- title?: string;
53
- /**
54
- * Notification variant
55
- */
56
- variant?: "info" | "success" | "warning" | "danger";
57
- };