@coyalabs/bts-style 1.3.22 → 1.3.23

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.
@@ -10,8 +10,8 @@
10
10
  import { VARIANT_FILLED } from '../../Base/variantTypes.js';
11
11
 
12
12
  // Subscribe to the actual stack
13
- /** @type {Array<{id: string, title: string, subtitle?: string, component: any, props: any, widthRatio: number}>} */
14
- let popupStack = [];
13
+ /** @type {Array<{id: string, title: string, subtitle?: string, component: any, props: any, widthRatio: number, disableClose?: boolean}>} */
14
+ let popupStack = [];
15
15
  popupStore.stack.subscribe((s) => {
16
16
  popupStack = s;
17
17
  });
@@ -19,8 +19,16 @@
19
19
  /** @type {{submit?: () => void, confirm?: () => void, cancel?: () => void, focusInput?: () => void} | null} */
20
20
  let activePopupInstance = null;
21
21
 
22
- /** @type {HTMLDivElement | null} */
23
- let activeDialogRef = null;
22
+ /** @type {HTMLDivElement | null} */
23
+ let activeDialogRef = null;
24
+ let pointerStartedOnOverlay = false;
25
+
26
+ $: activePopup = popupStack[popupStack.length - 1] || null;
27
+
28
+ function closeTopPopup() {
29
+ if (activePopup?.disableClose) return;
30
+ popupStore.close();
31
+ }
24
32
 
25
33
  function focusActivePopup() {
26
34
  void tick().then(() => {
@@ -50,11 +58,12 @@
50
58
  }
51
59
  }
52
60
 
53
- if (event.key === 'Escape') {
54
- event.preventDefault();
55
- if (activePopupInstance?.cancel) {
56
- activePopupInstance.cancel();
57
- } else {
61
+ if (event.key === 'Escape') {
62
+ event.preventDefault();
63
+ if (activePopup?.disableClose) return;
64
+ if (activePopupInstance?.cancel) {
65
+ activePopupInstance.cancel();
66
+ } else {
58
67
  popupStore.close();
59
68
  }
60
69
  return;
@@ -82,17 +91,31 @@
82
91
  activePopupInstance.submit();
83
92
  return;
84
93
  }
85
-
86
- activePopupInstance?.confirm?.();
87
- }
94
+
95
+ activePopupInstance?.confirm?.();
96
+ }
97
+
98
+ function handleOverlayPointerdown(event) {
99
+ pointerStartedOnOverlay = event.target === event.currentTarget;
100
+ }
101
+
102
+ function handleOverlayClick(event) {
103
+ if (!pointerStartedOnOverlay || event.target !== event.currentTarget) {
104
+ pointerStartedOnOverlay = false;
105
+ return;
106
+ }
107
+
108
+ pointerStartedOnOverlay = false;
109
+ closeTopPopup();
110
+ }
88
111
 
89
112
  $: if (popupStack.length > 0) {
90
113
  focusActivePopup();
91
114
  }
92
115
  </script>
93
116
 
94
- {#if popupStack.length > 0}
95
- <div class="overlay" transition:fade={{ duration: 200 }} on:click={popupStore.close} role="presentation">
117
+ {#if popupStack.length > 0}
118
+ <div class="overlay" transition:fade={{ duration: 200 }} on:pointerdown={handleOverlayPointerdown} on:click={handleOverlayClick} role="presentation">
96
119
  {#each popupStack as popup, index (popup.id)}
97
120
  {@const isTop = index === popupStack.length - 1}
98
121
  {@const depth = popupStack.length - 1 - index}
@@ -116,12 +139,14 @@
116
139
  tabindex="-1"
117
140
  >
118
141
  <BaseContainer padding="1.5rem" borderRadiusTopLeft="35px" borderRadiusTopRight="35px" borderRadiusBottomLeft="35px" borderRadiusBottomRight="35px" theme={VARIANT_FILLED}>
119
- <header>
120
- <TextHeader title={popup.title} subtitle={popup.subtitle || ''} />
121
- <div class="close-btn">
122
- <IconButton svg={icons.cross} size="20px" variant="toned" on:click={popupStore.close} />
123
- </div>
124
- </header>
142
+ <header>
143
+ <TextHeader title={popup.title} subtitle={popup.subtitle || ''} />
144
+ {#if !popup.disableClose}
145
+ <div class="close-btn">
146
+ <IconButton svg={icons.cross} size="20px" variant="toned" on:click={popupStore.close} />
147
+ </div>
148
+ {/if}
149
+ </header>
125
150
  <main>
126
151
  <svelte:component bind:this={activePopupInstance} this={popup.component} {...popup.props} />
127
152
  </main>
@@ -6,6 +6,7 @@ export namespace popupStore {
6
6
  component: any;
7
7
  props: {};
8
8
  widthRatio?: undefined;
9
+ disableClose?: undefined;
9
10
  } | {
10
11
  isOpen: boolean;
11
12
  title: string;
@@ -13,6 +14,7 @@ export namespace popupStore {
13
14
  component: any;
14
15
  props: any;
15
16
  widthRatio: number;
17
+ disableClose: boolean;
16
18
  }>, invalidate?: () => void) => import("svelte/store").Unsubscriber;
17
19
  namespace stack {
18
20
  let subscribe_1: (this: void, run: import("svelte/store").Subscriber<PopupState[]>, invalidate?: () => void) => import("svelte/store").Unsubscriber;
@@ -21,17 +23,20 @@ export namespace popupStore {
21
23
  function open(title: string, component: any, props?: {}, subtitle?: string): void;
22
24
  function close(): void;
23
25
  function closeAll(): void;
26
+ function setCloseDisabled(disabled: boolean): void;
24
27
  function confirm(title: string, message: string, options?: {
25
28
  onConfirm?: (() => void) | undefined;
26
29
  onCancel?: (() => void) | undefined;
27
30
  confirmText?: string | undefined;
28
31
  cancelText?: string | undefined;
29
32
  popupWidthRatio?: number | undefined;
33
+ disableClose?: boolean | undefined;
30
34
  }): void;
31
35
  function alert(title: string, message: string, options?: {
32
36
  onOk?: (() => void) | undefined;
33
37
  okText?: string | undefined;
34
38
  popupWidthRatio?: number | undefined;
39
+ disableClose?: boolean | undefined;
35
40
  }): void;
36
41
  function prompt(title: string, message: string, options?: {
37
42
  onSubmit?: ((value: string) => void) | undefined;
@@ -41,6 +46,7 @@ export namespace popupStore {
41
46
  cancelText?: string | undefined;
42
47
  label?: string | undefined;
43
48
  popupWidthRatio?: number | undefined;
49
+ disableClose?: boolean | undefined;
44
50
  }): void;
45
51
  }
46
52
  export type PopupState = {
@@ -50,4 +56,5 @@ export type PopupState = {
50
56
  props: any;
51
57
  id: string;
52
58
  widthRatio: number;
59
+ disableClose: boolean;
53
60
  };
@@ -8,9 +8,10 @@ import PromptPopup from './PromptPopup.svelte';
8
8
  * @property {string} title
9
9
  * @property {string} [subtitle]
10
10
  * @property {any} component
11
- * @property {any} props
12
- * @property {string} id
13
- * @property {number} widthRatio
11
+ * @property {any} props
12
+ * @property {string} id
13
+ * @property {number} widthRatio
14
+ * @property {boolean} disableClose
14
15
  */
15
16
 
16
17
  /**
@@ -32,13 +33,14 @@ function splitPopupProps(rawProps = {}) {
32
33
  };
33
34
  }
34
35
 
35
- const { popupWidthRatio, ...props } = rawProps;
36
-
37
- return {
38
- props,
39
- widthRatio: normalizePopupWidthRatio(popupWidthRatio)
40
- };
41
- }
36
+ const { popupWidthRatio, disableClose = false, ...props } = rawProps;
37
+
38
+ return {
39
+ props,
40
+ widthRatio: normalizePopupWidthRatio(popupWidthRatio),
41
+ disableClose: disableClose === true
42
+ };
43
+ }
42
44
 
43
45
  /**
44
46
  * @param {string} title
@@ -48,17 +50,18 @@ function splitPopupProps(rawProps = {}) {
48
50
  * @returns {PopupState}
49
51
  */
50
52
  function createPopupEntry(title, subtitle, component, rawProps = {}) {
51
- const { props, widthRatio } = splitPopupProps(rawProps);
53
+ const { props, widthRatio, disableClose } = splitPopupProps(rawProps);
52
54
 
53
55
  return {
54
56
  id: crypto.randomUUID(),
55
57
  title,
56
- subtitle,
57
- component,
58
- props,
59
- widthRatio
60
- };
61
- }
58
+ subtitle,
59
+ component,
60
+ props,
61
+ widthRatio,
62
+ disableClose
63
+ };
64
+ }
62
65
 
63
66
  function createPopupStore() {
64
67
  /** @type {import('svelte/store').Writable<PopupState[]>} */
@@ -79,12 +82,13 @@ function createPopupStore() {
79
82
  return {
80
83
  isOpen: true,
81
84
  title: top.title,
82
- subtitle: top.subtitle || '',
83
- component: top.component,
84
- props: top.props,
85
- widthRatio: top.widthRatio
86
- };
87
- });
85
+ subtitle: top.subtitle || '',
86
+ component: top.component,
87
+ props: top.props,
88
+ widthRatio: top.widthRatio,
89
+ disableClose: top.disableClose
90
+ };
91
+ });
88
92
 
89
93
  return {
90
94
  subscribe: currentPopup.subscribe,
@@ -100,9 +104,19 @@ function createPopupStore() {
100
104
  stack.update(s => s.slice(0, -1));
101
105
  },
102
106
 
103
- closeAll: () => {
104
- stack.set([]);
105
- },
107
+ closeAll: () => {
108
+ stack.set([]);
109
+ },
110
+
111
+ setCloseDisabled: (/** @type {boolean} */ disabled) => {
112
+ stack.update(s => {
113
+ if (s.length === 0) return s;
114
+ const next = [...s];
115
+ const top = next[next.length - 1];
116
+ next[next.length - 1] = { ...top, disableClose: disabled === true };
117
+ return next;
118
+ });
119
+ },
106
120
 
107
121
  /**
108
122
  * Show a confirmation popup with two buttons
@@ -112,8 +126,9 @@ function createPopupStore() {
112
126
  * @param {() => void} [options.onConfirm]
113
127
  * @param {() => void} [options.onCancel]
114
128
  * @param {string} [options.confirmText]
115
- * @param {string} [options.cancelText]
116
- * @param {number} [options.popupWidthRatio]
129
+ * @param {string} [options.cancelText]
130
+ * @param {number} [options.popupWidthRatio]
131
+ * @param {boolean} [options.disableClose]
117
132
  */
118
133
  confirm: (title, message, options = {}) => {
119
134
  stack.update(s => [...s, createPopupEntry(title, message, ConfirmPopup, options)]);
@@ -125,8 +140,9 @@ function createPopupStore() {
125
140
  * @param {string} message
126
141
  * @param {Object} options
127
142
  * @param {() => void} [options.onOk]
128
- * @param {string} [options.okText]
129
- * @param {number} [options.popupWidthRatio]
143
+ * @param {string} [options.okText]
144
+ * @param {number} [options.popupWidthRatio]
145
+ * @param {boolean} [options.disableClose]
130
146
  */
131
147
  alert: (title, message, options = {}) => {
132
148
  stack.update(s => [...s, createPopupEntry(title, message, AlertPopup, options)]);
@@ -141,9 +157,10 @@ function createPopupStore() {
141
157
  * @param {() => void} [options.onCancel]
142
158
  * @param {string} [options.placeholder]
143
159
  * @param {string} [options.submitText]
144
- * @param {string} [options.cancelText]
145
- * @param {string} [options.label]
146
- * @param {number} [options.popupWidthRatio]
160
+ * @param {string} [options.cancelText]
161
+ * @param {string} [options.label]
162
+ * @param {number} [options.popupWidthRatio]
163
+ * @param {boolean} [options.disableClose]
147
164
  */
148
165
  prompt: (title, message, options = {}) => {
149
166
  stack.update(s => [...s, createPopupEntry(title, message, PromptPopup, options)]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coyalabs/bts-style",
3
- "version": "1.3.22",
3
+ "version": "1.3.23",
4
4
  "description": "BTS Theme Svelte component templates",
5
5
  "type": "module",
6
6
  "exports": {