@rettangoli/ui 1.6.0 → 1.6.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rettangoli/ui",
3
- "version": "1.6.0",
3
+ "version": "1.6.1",
4
4
  "description": "A UI component library for building web interfaces.",
5
5
  "main": "dist/rettangoli-esm.min.js",
6
6
  "type": "module",
@@ -1,6 +1,7 @@
1
1
  const VALID_DIALOG_SIZES = new Set(["sm", "md", "lg", "f"]);
2
2
  const VALID_TOAST_SIZES = new Set(["sm", "md", "lg"]);
3
3
  const VALID_TOAST_PHASES = new Set(["active", "exiting"]);
4
+ const VALID_TOAST_POSITIONS = new Set(["top", "bottom"]);
4
5
  const VALID_COMPONENT_DIALOG_ROLES = new Set(["confirm", "cancel"]);
5
6
 
6
7
  const DEFAULT_COMPONENT_DIALOG_BUTTONS = Object.freeze([
@@ -39,6 +40,10 @@ const normalizeToastPhase = (value, fallback = "active") => {
39
40
  return VALID_TOAST_PHASES.has(value) ? value : fallback;
40
41
  };
41
42
 
43
+ const normalizeToastPosition = (value, fallback = "top") => {
44
+ return VALID_TOAST_POSITIONS.has(value) ? value : fallback;
45
+ };
46
+
42
47
  const normalizeComponentDialogActions = (value) => {
43
48
  const sourceButtons = Array.isArray(value?.buttons) && value.buttons.length > 0
44
49
  ? value.buttons
@@ -223,6 +228,7 @@ export const addToast = ({ state }, options = {}) => {
223
228
  id: `toast-${nextToastId}`,
224
229
  message: options.message,
225
230
  size: normalizeToastSize(options.size ?? options.s, "sm"),
231
+ position: normalizeToastPosition(options.position, "top"),
226
232
  phase: "active",
227
233
  };
228
234
 
@@ -277,6 +283,14 @@ export const selectViewData = ({ state }) => {
277
283
  const isFormDialogOpen = state.isOpen && state.uiType === "formDialog";
278
284
  const isComponentDialogOpen = state.isOpen && state.uiType === "componentDialog";
279
285
  const componentDialogConfig = state.componentDialogConfig ?? createDefaultComponentDialogConfig();
286
+ const normalizedToasts = Array.isArray(state.toasts)
287
+ ? state.toasts.map((toast) => ({
288
+ ...toast,
289
+ size: normalizeToastSize(toast.size, "sm"),
290
+ position: normalizeToastPosition(toast.position, "top"),
291
+ phase: normalizeToastPhase(toast.phase, "active"),
292
+ }))
293
+ : [];
280
294
 
281
295
  return {
282
296
  isOpen: state.isOpen,
@@ -305,13 +319,9 @@ export const selectViewData = ({ state }) => {
305
319
  actions: componentDialogConfig.actions ?? normalizeComponentDialogActions(),
306
320
  key: componentDialogConfig.key ?? 0,
307
321
  },
308
- toasts: Array.isArray(state.toasts)
309
- ? state.toasts.map((toast) => ({
310
- ...toast,
311
- size: normalizeToastSize(toast.size, "sm"),
312
- phase: normalizeToastPhase(toast.phase, "active"),
313
- }))
314
- : [],
322
+ toasts: normalizedToasts,
323
+ topToasts: normalizedToasts.filter((toast) => toast.position === "top"),
324
+ bottomToasts: normalizedToasts.filter((toast) => toast.position === "bottom"),
315
325
  isDialogOpen,
316
326
  isFormDialogOpen,
317
327
  isComponentDialogOpen,
@@ -38,34 +38,53 @@ styles:
38
38
  max-width: calc(100vw - 2 * var(--spacing-lg))
39
39
  opacity: 1
40
40
  transform: translateY(0) scale(1)
41
- animation: toast-in 220ms cubic-bezier(0.16, 1, 0.3, 1)
42
41
  transition: opacity 180ms cubic-bezier(0.16, 1, 0.3, 1), transform 180ms cubic-bezier(0.16, 1, 0.3, 1)
43
42
  will-change: opacity, transform
43
+ .toast-card-top:
44
+ animation: toast-in-top 220ms cubic-bezier(0.16, 1, 0.3, 1)
45
+ .toast-card-bottom:
46
+ animation: toast-in-bottom 220ms cubic-bezier(0.16, 1, 0.3, 1)
44
47
  .toast-card-md:
45
48
  width: 50vw
46
49
  .toast-card-lg:
47
50
  width: 80vw
48
- .toast-card-exiting:
51
+ .toast-card-top.toast-card-exiting:
49
52
  opacity: 0
50
53
  transform: translateY(calc(var(--spacing-sm) * -0.5)) scale(0.98)
54
+ .toast-card-bottom.toast-card-exiting:
55
+ opacity: 0
56
+ transform: translateY(calc(var(--spacing-sm) * 0.5)) scale(0.98)
51
57
  .toast-message:
52
58
  overflow-wrap: anywhere
53
- '@keyframes toast-in':
59
+ '@keyframes toast-in-top':
54
60
  from:
55
61
  opacity: 0
56
62
  transform: translateY(calc(var(--spacing-sm) * -1)) scale(0.96)
57
63
  to:
58
64
  opacity: 1
59
65
  transform: translateY(0) scale(1)
66
+ '@keyframes toast-in-bottom':
67
+ from:
68
+ opacity: 0
69
+ transform: translateY(var(--spacing-sm)) scale(0.96)
70
+ to:
71
+ opacity: 1
72
+ transform: translateY(0) scale(1)
60
73
  '@media (prefers-reduced-motion: reduce)':
61
74
  .toast-card:
62
75
  animation: none
63
76
  transition: none
64
77
  template:
65
- - rtgl-view class=toast-layer pos=fix edge=t z=2100 w=f ah=c g=sm p=md:
66
- - $for toast, i in toasts:
67
- - 'rtgl-view class="toast-card toast-card-${toast.size} toast-card-${toast.phase}" key=toast-${toast.id} bgc=su bc=bo bw=xs br=md shadow=md ph=lg pv=md':
68
- - rtgl-text class=toast-message ta=c w=f: ${toast.message}
78
+ - $if topToasts.length > 0:
79
+ - rtgl-view class="toast-layer toast-layer-top" pos=fix edge=t z=2100 w=f ah=c g=sm ph=md pt=xl:
80
+ - $for toast, i in topToasts:
81
+ - 'rtgl-view class="toast-card toast-card-top toast-card-${toast.size} toast-card-${toast.phase}" key=toast-${toast.id} bgc=su bc=bo bw=xs br=md shadow=md ph=xl pv=lg':
82
+ - rtgl-text class=toast-message ta=c w=f: ${toast.message}
83
+ - $if bottomToasts.length > 0:
84
+ - rtgl-view class="toast-layer toast-layer-bottom" pos=fix edge=b z=2100 w=f ah=c g=sm ph=md pb=xl:
85
+ - $for toast, i in bottomToasts:
86
+ - 'rtgl-view class="toast-card toast-card-bottom toast-card-${toast.size} toast-card-${toast.phase}" key=toast-${toast.id} bgc=su bc=bo bw=xs br=md shadow=md ph=xl pv=lg':
87
+ - rtgl-text class=toast-message ta=c w=f: ${toast.message}
69
88
  - rtgl-dialog#dialog ?open=${isDialogContainerOpen} s=${dialogSize}:
70
89
  - $if isFormDialogOpen:
71
90
  - rtgl-form#formDialog slot=content :form=${formDialogConfig.form} :defaultValues=${formDialogConfig.defaultValues} :context=${formDialogConfig.context} ?disabled=${formDialogConfig.disabled} key=form-dialog-${formDialogConfig.key}: null
@@ -163,11 +163,12 @@ const createGlobalUI = (globalUIElement) => {
163
163
  },
164
164
 
165
165
  /**
166
- * Shows a top-centered toast message that auto-dismisses after 3 seconds.
166
+ * Shows a toast message that auto-dismisses after 3 seconds.
167
167
  *
168
168
  * @param {Object} options - Toast configuration options
169
169
  * @param {string} options.message - The toast message (required)
170
170
  * @param {('sm'|'md'|'lg')} [options.size] - Toast width preset matching dialog sizing (default: "sm")
171
+ * @param {('top'|'bottom')} [options.position] - Vertical viewport placement (default: "top")
171
172
  * @returns {void}
172
173
  * @throws {Error} If globalUIElement is not initialized
173
174
  */