@marianmeres/stuic 2.60.0 → 2.62.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.
@@ -97,7 +97,7 @@
97
97
  classNotifContent,
98
98
  classNotifButton,
99
99
  classNotifButtonX,
100
- buttonXStrokeWidth = 1.5,
100
+ buttonXStrokeWidth = 3,
101
101
  //
102
102
  classProgress,
103
103
  classProgressBar,
@@ -110,6 +110,8 @@
110
110
  iconFns = {},
111
111
  }: Props = $props();
112
112
 
113
+ let popoverEl: HTMLDivElement | null = $state(null);
114
+
113
115
  let { x, y, xMobile, yMobile } = $derived.by(() => {
114
116
  const x = X_POSITIONS.includes(posX) ? posX : DEFAULT.posX;
115
117
  const xMobile = X_POSITIONS.includes(posXMobile) ? posXMobile : DEFAULT.posXMobile;
@@ -118,11 +120,6 @@
118
120
  return { x, y, xMobile, yMobile };
119
121
  });
120
122
 
121
- const maybeComponent = (n: Notification): { Cmp: any; props: any } => {
122
- // todo when needed
123
- return { Cmp: null, props: null };
124
- };
125
-
126
123
  let _iconFns = $derived({ ...notificationsDefaultIcons, ...iconFns });
127
124
 
128
125
  const maybeIcon = (n: Notification) => {
@@ -132,50 +129,51 @@
132
129
  };
133
130
 
134
131
  const _classWrapX = `
135
- fixed z-50 flex flex-row inset-0
132
+ fixed z-50 flex flex-row inset-0
136
133
  pointer-events-none bg-transparent`;
137
134
 
138
135
  const _classWrapY = `
139
- p-4 space-y-4
140
- flex flex-col inset-0
136
+ p-4 space-y-4
137
+ flex flex-col inset-0
138
+ w-full sm:w-auto
141
139
  pointer-events-none bg-transparent`;
142
140
 
143
141
  const _classNotifBox = `
144
- relative flex
145
- pointer-events-auto
146
- w-xs sm:w-sm max-w-sm
147
- rounded-lg
148
- shadow-lg
149
- border border-notif-border dark:border-notif-border-dark
150
- bg-notif-bg text-notif-text
142
+ relative flex
143
+ pointer-events-auto
144
+ w-full sm:w-sm max-w-full sm:max-w-sm
145
+ rounded-lg
146
+ shadow-lg
147
+ border border-notif-border dark:border-notif-border-dark
148
+ bg-notif-bg text-notif-text
151
149
  dark:bg-notif-bg-dark dark:text-notif-text-dark`;
152
150
 
153
151
  const _classNotifCount = `
154
- absolute -top-2 -right-2
155
- w-auto h-auto
156
- flex items-center justify-center
157
- px-2 py-1 rounded-full
158
- leading-none text-xs
152
+ absolute -top-2 -right-2
153
+ w-auto h-auto
154
+ flex items-center justify-center
155
+ px-2 py-1 rounded-full
156
+ leading-none text-xs
159
157
  bg-neutral-950 text-neutral-50`;
160
158
 
161
159
  const _classNotifIcon = `
162
- flex items-center justify-center
163
- pt-4 pr-0 pb-4 pl-4
160
+ flex items-center justify-center
161
+ pt-4 pr-0 pb-4 pl-4
164
162
  text-neutral-200`;
165
163
 
166
164
  const _classNotifContent = `
167
- flex-1
168
- flex flex-col justify-center
169
- text-sm tracking-tight
165
+ flex-1
166
+ flex flex-col justify-center
167
+ tracking-tight
170
168
  pl-4 pr-1 py-3`;
171
169
 
172
170
  const _classNotifButton = `
173
- flex flex-col items-center justify-center
174
- leading-none
175
- px-3
176
- hover:bg-neutral-950/10
177
- focus-visible:bg-neutral-950/10 focus-visible:outline-none focus-visible:ring-0
178
- group
171
+ flex flex-col items-center justify-center
172
+ leading-none
173
+ px-3
174
+ hover:bg-neutral-950/10
175
+ focus-visible:bg-neutral-950/10 focus-visible:outline-none focus-visible:ring-0
176
+ group
179
177
  rounded-tr-md rounded-br-md`;
180
178
 
181
179
  const _classNotifButtonX = `opacity-75 group-hover:opacity-100`;
@@ -202,11 +200,32 @@
202
200
  `--color-notif-border-dark: var(--color-border-dark-${type}, var(--color-${theme}-700));`,
203
201
  ].join("");
204
202
  };
203
+
204
+ // Manage popover visibility based on notifications
205
+ $effect(() => {
206
+ if (!popoverEl) return;
207
+
208
+ const hasNotifications = notifications.stack.length > 0;
209
+
210
+ try {
211
+ if (hasNotifications && !popoverEl.matches(":popover-open")) {
212
+ popoverEl.showPopover();
213
+ } else if (!hasNotifications && popoverEl.matches(":popover-open")) {
214
+ popoverEl.hidePopover();
215
+ }
216
+ } catch {
217
+ // Popover API not supported - element remains in DOM flow
218
+ }
219
+ });
205
220
  </script>
206
221
 
207
- {#if notifications.stack.length}
222
+ <div
223
+ bind:this={popoverEl}
224
+ popover="manual"
225
+ class="stuic-notifs-popover"
226
+ aria-live="assertive"
227
+ >
208
228
  <div
209
- aria-live="assertive"
210
229
  class={twMerge(
211
230
  "stuic-notifs wrap-x",
212
231
  _classWrapX,
@@ -217,75 +236,95 @@
217
236
  >
218
237
  <div class={twMerge("wrap-y", _classWrapY, YMAP_M[yMobile], YMAP[y], classWrapY)}>
219
238
  {#each notifications.stack as n (n.id)}
220
- {@const { Cmp, props } = maybeComponent(n)}
221
239
  {@const iconHtml = maybeIcon(n)}
222
240
  {@const showXButton = !noXButton || n.ttl > 1000}
223
- {#if Cmp}
224
- <Cmp {...props || {}} notification={n} {notifications} />
225
- {:else}
241
+ <div
242
+ class={twMerge("box", _classNotifBox, classNotifBox)}
243
+ transition:fade|global={{ duration }}
244
+ role="alert"
245
+ style={_buildTheme(n.type)}
246
+ >
247
+ {#if n.ttl && !noProgress}
248
+ <Progress
249
+ progress={100 - n._ttlProgress * 100}
250
+ class={twMerge(_classProgress, classProgress)}
251
+ classBar={twMerge(_classProgressBar, classProgressBar)}
252
+ styleBar="transition-duration: {notifications.options.disposeInterval}ms;"
253
+ />
254
+ {/if}
255
+
256
+ {#if n.count > 1}
257
+ <div class={twMerge("count", _classNotifCount, classNotifCount)}>
258
+ {n.count}
259
+ </div>
260
+ {/if}
261
+ {#if !noIcons && iconHtml}
262
+ <div class={twMerge("icon", _classNotifIcon, classNotifIcon)}>
263
+ {@html iconHtml}
264
+ </div>
265
+ {/if}
266
+
226
267
  <div
227
- class={twMerge("box", _classNotifBox, classNotifBox)}
228
- transition:fade|global={{ duration }}
229
- role="alert"
230
- style={_buildTheme(n.type)}
268
+ class={twMerge(
269
+ "content",
270
+ _classNotifContent,
271
+ classNotifContent,
272
+ !showXButton && "pr-4"
273
+ )}
231
274
  >
232
- {#if n.ttl && !noProgress}
233
- <Progress
234
- progress={100 - n._ttlProgress * 100}
235
- class={twMerge(_classProgress, classProgress)}
236
- classBar={twMerge(_classProgressBar, classProgressBar)}
237
- styleBar="transition-duration: {notifications.options.disposeInterval}ms;"
238
- />
239
- {/if}
240
-
241
- {#if n.count > 1}
242
- <div class={twMerge("count", _classNotifCount, classNotifCount)}>
243
- {n.count}
244
- </div>
245
- {/if}
246
- {#if !noIcons && iconHtml}
247
- <div class={twMerge("icon", _classNotifIcon, classNotifIcon)}>
248
- {@html iconHtml}
249
- </div>
250
- {/if}
275
+ <Thc
276
+ thc={n.content}
277
+ forceAsHtml={n.forceAsHtml ?? forceAsHtml}
278
+ notification={n}
279
+ {notifications}
280
+ />
281
+ </div>
251
282
 
252
- <div
253
- class={twMerge(
254
- "content",
255
- _classNotifContent,
256
- classNotifContent,
257
- !showXButton && "pr-4"
258
- )}
283
+ {#if showXButton}
284
+ <button
285
+ type="button"
286
+ class={twMerge("button", _classNotifButton, classNotifButton)}
287
+ aria-label={ariaCloseLabel}
288
+ onclick={(e) => {
289
+ e.preventDefault();
290
+ e.stopPropagation();
291
+ e.stopImmediatePropagation();
292
+ notifications.removeById(n.id);
293
+ }}
259
294
  >
260
- <Thc
261
- thc={n.content}
262
- forceAsHtml={n.forceAsHtml ?? forceAsHtml}
263
- notification={n}
264
- {notifications}
295
+ <X
296
+ class={twMerge("x", _classNotifButtonX, classNotifButtonX)}
297
+ strokeWidth={buttonXStrokeWidth}
265
298
  />
266
- </div>
267
-
268
- {#if showXButton}
269
- <button
270
- type="button"
271
- class={twMerge("button", _classNotifButton, classNotifButton)}
272
- aria-label={ariaCloseLabel}
273
- onclick={(e) => {
274
- e.preventDefault();
275
- e.stopPropagation();
276
- e.stopImmediatePropagation();
277
- notifications.removeById(n.id);
278
- }}
279
- >
280
- <X
281
- class={twMerge("x", _classNotifButtonX, classNotifButtonX)}
282
- strokeWidth={buttonXStrokeWidth}
283
- />
284
- </button>
285
- {/if}
286
- </div>
287
- {/if}
299
+ </button>
300
+ {/if}
301
+ </div>
288
302
  {/each}
289
303
  </div>
290
304
  </div>
291
- {/if}
305
+ </div>
306
+
307
+ <style>
308
+ /* Override default popover positioning */
309
+ .stuic-notifs-popover {
310
+ /* Reset popover defaults */
311
+ position: fixed;
312
+ inset: 0;
313
+ margin: 0;
314
+ padding: 0;
315
+ border: none;
316
+ background: transparent;
317
+ max-width: none;
318
+ max-height: none;
319
+ width: 100%;
320
+ height: 100%;
321
+ overflow: visible;
322
+ pointer-events: none;
323
+ }
324
+
325
+ /* Transparent backdrop so content behind is clickable */
326
+ .stuic-notifs-popover::backdrop {
327
+ background: transparent;
328
+ pointer-events: none;
329
+ }
330
+ </style>
@@ -1,7 +1,7 @@
1
1
  import { iconAlertWarning, iconAlertSuccess, iconAlertInfo, iconAlertError, } from "../../icons/index.js";
2
2
  export const notificationsDefaultIcons = {
3
- info: () => iconAlertInfo({}),
4
- success: () => iconAlertSuccess({}),
5
- warn: () => iconAlertWarning({}),
6
- error: () => iconAlertError({}),
3
+ info: () => iconAlertInfo({ size: 29 }),
4
+ success: () => iconAlertSuccess({ size: 29 }),
5
+ warn: () => iconAlertWarning({ size: 29 }),
6
+ error: () => iconAlertError({ size: 29 }),
7
7
  };
@@ -8,7 +8,7 @@
8
8
  <script lang="ts">
9
9
  import { twMerge } from "../../index.js";
10
10
 
11
- let { class: classProps, strokeWidth = 2.5 }: Props = $props();
11
+ let { class: classProps, strokeWidth = 3 }: Props = $props();
12
12
 
13
13
  // size-6 = 1.5rem = 24px
14
14
  </script>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marianmeres/stuic",
3
- "version": "2.60.0",
3
+ "version": "2.62.0",
4
4
  "files": [
5
5
  "dist",
6
6
  "!dist/**/*.test.*",