innoboxrr-form-core 2.3.0 → 2.5.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.
- package/README.md +131 -29
- package/index.js +20 -4
- package/package.json +3 -2
- package/src/components.css +638 -11
- package/src/feedback.js +273 -0
- package/src/icons.js +16 -0
- package/src/theme.js +57 -0
- package/src/tokens.css +41 -0
package/src/feedback.js
ADDED
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Avisos y confirmaciones de toda la aplicación, como estado de módulo.
|
|
3
|
+
*
|
|
4
|
+
* Hasta ahora no había forma de avisar: un formulario generado se tragaba
|
|
5
|
+
* cualquier error que no fuera de validación, la tabla solo lo escribía en la
|
|
6
|
+
* consola y la confirmación de borrar era un SweetAlert de otro paquete. Cada
|
|
7
|
+
* pieza que quisiera decir algo tendría que haber montado su propio aviso.
|
|
8
|
+
*
|
|
9
|
+
* Vive aquí, fuera de Vue y de React, por la misma razón que el tema: los dos
|
|
10
|
+
* paquetes de componentes pintan la misma cola, y quien avisa —un store, un
|
|
11
|
+
* contrato de modelo, la tabla— no tiene por qué saber qué framework hay
|
|
12
|
+
* detrás.
|
|
13
|
+
*
|
|
14
|
+
* import { notify, confirmAction } from 'innoboxrr-form-core'
|
|
15
|
+
*
|
|
16
|
+
* notify({ message: 'Producto creado', variant: 'success' })
|
|
17
|
+
*
|
|
18
|
+
* if (await confirmAction({ message: '¿Borrar el producto?', variant: 'danger' })) {
|
|
19
|
+
* // …
|
|
20
|
+
* }
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @typedef {'info'|'success'|'warning'|'danger'} Variant
|
|
25
|
+
* @typedef {{ id: number, title: string|null, message: string, variant: Variant, duration: number }} Toast
|
|
26
|
+
* @typedef {{ id: number, title: string, message: string, confirmLabel: string, cancelLabel: string, variant: 'primary'|'danger' }} Confirmation
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
const VARIANTS = ['info', 'success', 'warning', 'danger']
|
|
30
|
+
|
|
31
|
+
const DURATION = 5000
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Más avisos a la vez no se leen: tapan la pantalla. El que sobra es el más
|
|
35
|
+
* antiguo.
|
|
36
|
+
*/
|
|
37
|
+
const MAX_TOASTS = 5
|
|
38
|
+
|
|
39
|
+
// AVISOS
|
|
40
|
+
|
|
41
|
+
/** @type {Toast[]} */
|
|
42
|
+
let toasts = []
|
|
43
|
+
|
|
44
|
+
let nextToast = 1
|
|
45
|
+
|
|
46
|
+
/** @type {Map<number, ReturnType<typeof setTimeout>>} */
|
|
47
|
+
const timers = new Map()
|
|
48
|
+
|
|
49
|
+
/** @type {Set<(toasts: Toast[]) => void>} */
|
|
50
|
+
const toastListeners = new Set()
|
|
51
|
+
|
|
52
|
+
const emitToasts = () => toastListeners.forEach((listener) => listener(toasts))
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Muestra un aviso y devuelve su id.
|
|
56
|
+
*
|
|
57
|
+
* Un aviso de peligro no se cierra solo por defecto: quien no lo leyó a tiempo
|
|
58
|
+
* no sabría qué falló. El resto se va a los cinco segundos. `duration: 0` lo
|
|
59
|
+
* deja hasta que se cierre a mano.
|
|
60
|
+
*
|
|
61
|
+
* @param {string|{ message: string, title?: string, variant?: Variant, duration?: number }} options
|
|
62
|
+
* @returns {number}
|
|
63
|
+
*/
|
|
64
|
+
export function notify(options) {
|
|
65
|
+
const input = typeof options === 'string' ? { message: options } : (options ?? {})
|
|
66
|
+
const variant = VARIANTS.includes(input.variant) ? input.variant : 'info'
|
|
67
|
+
const duration = input.duration ?? (variant === 'danger' ? 0 : DURATION)
|
|
68
|
+
|
|
69
|
+
/** @type {Toast} */
|
|
70
|
+
const toast = {
|
|
71
|
+
id: nextToast++,
|
|
72
|
+
title: input.title ?? null,
|
|
73
|
+
message: String(input.message ?? ''),
|
|
74
|
+
variant,
|
|
75
|
+
duration,
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Se reemplaza el array en vez de mutarlo: React compara la instantánea
|
|
79
|
+
// por referencia y, con el mismo array, no repintaría.
|
|
80
|
+
toasts = [...toasts, toast]
|
|
81
|
+
|
|
82
|
+
while (toasts.length > MAX_TOASTS) {
|
|
83
|
+
forget(toasts[0].id)
|
|
84
|
+
toasts = toasts.slice(1)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (duration > 0) {
|
|
88
|
+
timers.set(toast.id, setTimeout(() => dismiss(toast.id), duration))
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
emitToasts()
|
|
92
|
+
|
|
93
|
+
return toast.id
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* @param {string} message
|
|
98
|
+
* @param {{ title?: string, duration?: number }} [options]
|
|
99
|
+
*/
|
|
100
|
+
export function notifySuccess(message, options = {}) {
|
|
101
|
+
return notify({ ...options, message, variant: 'success' })
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* @param {string} message
|
|
106
|
+
* @param {{ title?: string, duration?: number }} [options]
|
|
107
|
+
*/
|
|
108
|
+
export function notifyError(message, options = {}) {
|
|
109
|
+
return notify({ ...options, message, variant: 'danger' })
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* @param {number} id
|
|
114
|
+
*/
|
|
115
|
+
export function dismiss(id) {
|
|
116
|
+
forget(id)
|
|
117
|
+
|
|
118
|
+
const remaining = toasts.filter((toast) => toast.id !== id)
|
|
119
|
+
|
|
120
|
+
if (remaining.length !== toasts.length) {
|
|
121
|
+
toasts = remaining
|
|
122
|
+
emitToasts()
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** @returns {Toast[]} */
|
|
127
|
+
export function getToasts() {
|
|
128
|
+
return toasts
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* @param {(toasts: Toast[]) => void} listener
|
|
133
|
+
* @returns {() => void} para dejar de escuchar
|
|
134
|
+
*/
|
|
135
|
+
export function onToastsChange(listener) {
|
|
136
|
+
toastListeners.add(listener)
|
|
137
|
+
|
|
138
|
+
return () => toastListeners.delete(listener)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Vacía la cola. Sobre todo para las pruebas: sin esto un aviso de una prueba
|
|
143
|
+
* aparecería en la siguiente.
|
|
144
|
+
*/
|
|
145
|
+
export function resetToasts() {
|
|
146
|
+
timers.forEach((timer) => clearTimeout(timer))
|
|
147
|
+
timers.clear()
|
|
148
|
+
|
|
149
|
+
toasts = []
|
|
150
|
+
nextToast = 1
|
|
151
|
+
|
|
152
|
+
emitToasts()
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* @param {number} id
|
|
157
|
+
*/
|
|
158
|
+
function forget(id) {
|
|
159
|
+
clearTimeout(timers.get(id))
|
|
160
|
+
timers.delete(id)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// CONFIRMACIONES
|
|
164
|
+
|
|
165
|
+
/** @type {Confirmation|null} */
|
|
166
|
+
let confirmation = null
|
|
167
|
+
|
|
168
|
+
/** @type {((value: boolean) => void)|null} */
|
|
169
|
+
let resolver = null
|
|
170
|
+
|
|
171
|
+
let nextConfirmation = 1
|
|
172
|
+
|
|
173
|
+
/** @type {Set<(confirmation: Confirmation|null) => void>} */
|
|
174
|
+
const confirmationListeners = new Set()
|
|
175
|
+
|
|
176
|
+
const emitConfirmation = () => confirmationListeners.forEach((listener) => listener(confirmation))
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Pregunta y espera la respuesta: `true` si se confirma, `false` si no.
|
|
180
|
+
*
|
|
181
|
+
* La pinta el ConfirmHostComponent que la aplicación monta una vez. Si no hay
|
|
182
|
+
* ninguno escuchando se usa `window.confirm`, que es feo pero contesta: una
|
|
183
|
+
* promesa que no se resolviera nunca dejaría colgada la acción que la espera.
|
|
184
|
+
*
|
|
185
|
+
* Una pregunta nueva con otra pendiente da la anterior por cancelada. Dos
|
|
186
|
+
* confirmaciones a la vez no tienen una respuesta clara.
|
|
187
|
+
*
|
|
188
|
+
* @param {string|{ message: string, title?: string, confirmLabel?: string, cancelLabel?: string, variant?: 'primary'|'danger' }} options
|
|
189
|
+
* @returns {Promise<boolean>}
|
|
190
|
+
*/
|
|
191
|
+
export function confirmAction(options) {
|
|
192
|
+
const input = typeof options === 'string' ? { message: options } : (options ?? {})
|
|
193
|
+
const message = String(input.message ?? '')
|
|
194
|
+
|
|
195
|
+
if (confirmationListeners.size === 0) {
|
|
196
|
+
const native = typeof window !== 'undefined' ? window.confirm : undefined
|
|
197
|
+
|
|
198
|
+
return Promise.resolve(typeof native === 'function' ? Boolean(native.call(window, message)) : false)
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (resolver) {
|
|
202
|
+
settle(false)
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return new Promise((resolve) => {
|
|
206
|
+
resolver = resolve
|
|
207
|
+
|
|
208
|
+
confirmation = {
|
|
209
|
+
id: nextConfirmation++,
|
|
210
|
+
title: input.title ?? '¿Confirmas?',
|
|
211
|
+
message,
|
|
212
|
+
confirmLabel: input.confirmLabel ?? 'Confirmar',
|
|
213
|
+
cancelLabel: input.cancelLabel ?? 'Cancelar',
|
|
214
|
+
variant: input.variant === 'danger' ? 'danger' : 'primary',
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
emitConfirmation()
|
|
218
|
+
})
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* La respuesta a la confirmación pendiente. La llama el componente que la pinta.
|
|
223
|
+
*
|
|
224
|
+
* @param {boolean} value
|
|
225
|
+
*/
|
|
226
|
+
export function resolveConfirmation(value) {
|
|
227
|
+
if (resolver) {
|
|
228
|
+
settle(Boolean(value))
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/** @returns {Confirmation|null} */
|
|
233
|
+
export function getConfirmation() {
|
|
234
|
+
return confirmation
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* @param {(confirmation: Confirmation|null) => void} listener
|
|
239
|
+
* @returns {() => void} para dejar de escuchar
|
|
240
|
+
*/
|
|
241
|
+
export function onConfirmationChange(listener) {
|
|
242
|
+
confirmationListeners.add(listener)
|
|
243
|
+
|
|
244
|
+
return () => confirmationListeners.delete(listener)
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Cancela lo pendiente. Sobre todo para las pruebas.
|
|
249
|
+
*/
|
|
250
|
+
export function resetConfirmation() {
|
|
251
|
+
if (resolver) {
|
|
252
|
+
settle(false)
|
|
253
|
+
|
|
254
|
+
return
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
confirmation = null
|
|
258
|
+
emitConfirmation()
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* @param {boolean} value
|
|
263
|
+
*/
|
|
264
|
+
function settle(value) {
|
|
265
|
+
const resolve = resolver
|
|
266
|
+
|
|
267
|
+
resolver = null
|
|
268
|
+
confirmation = null
|
|
269
|
+
|
|
270
|
+
emitConfirmation()
|
|
271
|
+
|
|
272
|
+
resolve?.(value)
|
|
273
|
+
}
|
package/src/icons.js
CHANGED
|
@@ -41,8 +41,12 @@ export const defaultIcons = {
|
|
|
41
41
|
hide: 'fa6-solid:eye-slash',
|
|
42
42
|
actions: 'fa6-solid:gears',
|
|
43
43
|
refresh: 'fa6-solid:rotate',
|
|
44
|
+
restore: 'fa6-solid:rotate-left',
|
|
44
45
|
search: 'fa6-solid:magnifying-glass',
|
|
45
46
|
filter: 'fa6-solid:filter',
|
|
47
|
+
more: 'fa6-solid:ellipsis',
|
|
48
|
+
external: 'fa6-solid:arrow-up-right-from-square',
|
|
49
|
+
logout: 'fa6-solid:right-from-bracket',
|
|
46
50
|
|
|
47
51
|
// ESTADO
|
|
48
52
|
help: 'fa6-solid:circle-question',
|
|
@@ -58,6 +62,18 @@ export const defaultIcons = {
|
|
|
58
62
|
up: 'fa6-solid:chevron-up',
|
|
59
63
|
down: 'fa6-solid:chevron-down',
|
|
60
64
|
close: 'fa6-solid:xmark',
|
|
65
|
+
home: 'fa6-solid:house',
|
|
66
|
+
menu: 'fa6-solid:bars',
|
|
67
|
+
sidebar: 'fa6-solid:table-columns',
|
|
68
|
+
command: 'fa6-solid:terminal',
|
|
69
|
+
settings: 'fa6-solid:gear',
|
|
70
|
+
|
|
71
|
+
// SELECCION Y ORDEN
|
|
72
|
+
check: 'fa6-solid:check',
|
|
73
|
+
minus: 'fa6-solid:minus',
|
|
74
|
+
sort: 'fa6-solid:sort',
|
|
75
|
+
sortUp: 'fa6-solid:sort-up',
|
|
76
|
+
sortDown: 'fa6-solid:sort-down',
|
|
61
77
|
|
|
62
78
|
// OBJETOS
|
|
63
79
|
box: 'fa6-solid:box',
|
package/src/theme.js
CHANGED
|
@@ -52,6 +52,7 @@ export const defaultTheme = {
|
|
|
52
52
|
buttonSecondary: 'fe-button-secondary',
|
|
53
53
|
buttonDanger: 'fe-button-danger',
|
|
54
54
|
buttonLink: 'fe-button-link',
|
|
55
|
+
iconButton: 'fe-icon-button',
|
|
55
56
|
|
|
56
57
|
// NAVEGACION Y ACCIONES
|
|
57
58
|
breadcrumb: 'fe-breadcrumb',
|
|
@@ -61,25 +62,81 @@ export const defaultTheme = {
|
|
|
61
62
|
actionMenu: 'fe-action-menu',
|
|
62
63
|
actionMenuItem: 'fe-action-item',
|
|
63
64
|
actionMenuDanger: 'fe-action-danger',
|
|
65
|
+
kbd: 'fe-kbd',
|
|
64
66
|
|
|
65
67
|
// SUPERFICIES Y PIEZAS DE APLICACION
|
|
66
68
|
surface: 'fe-surface',
|
|
67
69
|
surfaceRaised: 'fe-surface-raised',
|
|
68
70
|
toolbar: 'fe-toolbar',
|
|
69
71
|
badge: 'fe-badge',
|
|
72
|
+
badgePrimary: 'fe-badge-primary',
|
|
73
|
+
badgeSuccess: 'fe-badge-success',
|
|
74
|
+
badgeDanger: 'fe-badge-danger',
|
|
75
|
+
badgeWarning: 'fe-badge-warning',
|
|
70
76
|
skeleton: 'fe-skeleton',
|
|
77
|
+
skeletonText: 'fe-skeleton-text',
|
|
78
|
+
skeletonCircle: 'fe-skeleton-circle',
|
|
79
|
+
skeletonBlock: 'fe-skeleton-block',
|
|
80
|
+
|
|
81
|
+
// DIALOGOS Y DRAWERS, sobre <dialog> y showModal()
|
|
71
82
|
overlay: 'fe-overlay',
|
|
72
83
|
dialog: 'fe-dialog',
|
|
84
|
+
dialogSmall: 'fe-dialog-sm',
|
|
85
|
+
dialogLarge: 'fe-dialog-lg',
|
|
86
|
+
dialogHeader: 'fe-dialog-header',
|
|
87
|
+
dialogTitle: 'fe-dialog-title',
|
|
88
|
+
dialogBody: 'fe-dialog-body',
|
|
89
|
+
dialogFooter: 'fe-dialog-footer',
|
|
73
90
|
drawer: 'fe-drawer',
|
|
91
|
+
drawerStart: 'fe-drawer-start',
|
|
92
|
+
drawerHeader: 'fe-drawer-header',
|
|
93
|
+
drawerTitle: 'fe-drawer-title',
|
|
94
|
+
drawerBody: 'fe-drawer-body',
|
|
95
|
+
drawerFooter: 'fe-drawer-footer',
|
|
96
|
+
|
|
97
|
+
// MENUS, sobre el atributo popover
|
|
74
98
|
menu: 'fe-menu',
|
|
99
|
+
menuList: 'fe-menu-list',
|
|
75
100
|
menuItem: 'fe-menu-item',
|
|
101
|
+
menuItemDanger: 'fe-menu-item-danger',
|
|
102
|
+
menuSeparator: 'fe-menu-separator',
|
|
103
|
+
menuLabel: 'fe-menu-label',
|
|
104
|
+
|
|
105
|
+
// PALETA DE COMANDOS
|
|
106
|
+
command: 'fe-command',
|
|
107
|
+
commandInput: 'fe-command-input',
|
|
108
|
+
commandList: 'fe-command-list',
|
|
109
|
+
commandGroup: 'fe-command-group',
|
|
110
|
+
commandItem: 'fe-command-item',
|
|
111
|
+
commandEmpty: 'fe-command-empty',
|
|
112
|
+
|
|
113
|
+
// AVISOS
|
|
76
114
|
toast: 'fe-toast',
|
|
77
115
|
toastDanger: 'fe-toast-danger',
|
|
78
116
|
toastSuccess: 'fe-toast-success',
|
|
117
|
+
toastWarning: 'fe-toast-warning',
|
|
118
|
+
toastTitle: 'fe-toast-title',
|
|
119
|
+
toastClose: 'fe-toast-close',
|
|
120
|
+
toastRegion: 'fe-toast-region',
|
|
79
121
|
|
|
80
122
|
// TABLA
|
|
81
123
|
table: 'fe-table',
|
|
82
124
|
tableNumeric: 'fe-numeric',
|
|
125
|
+
tableSticky: 'fe-table-sticky',
|
|
126
|
+
tableSelect: 'fe-table-select',
|
|
127
|
+
tableResizer: 'fe-table-resizer',
|
|
128
|
+
tableEmpty: 'fe-table-empty',
|
|
129
|
+
bulkBar: 'fe-bulk-bar',
|
|
130
|
+
bulkCount: 'fe-bulk-count',
|
|
131
|
+
|
|
132
|
+
// EDICION EN LINEA
|
|
133
|
+
editable: 'fe-editable',
|
|
134
|
+
|
|
135
|
+
// SHELL DE APLICACION
|
|
136
|
+
shell: 'fe-shell',
|
|
137
|
+
shellHeader: 'fe-shell-header',
|
|
138
|
+
shellSidebar: 'fe-shell-sidebar',
|
|
139
|
+
shellMain: 'fe-shell-main',
|
|
83
140
|
}
|
|
84
141
|
|
|
85
142
|
/** @type {Theme} */
|
package/src/tokens.css
CHANGED
|
@@ -51,8 +51,15 @@
|
|
|
51
51
|
--fe-warning: #8a6212;
|
|
52
52
|
--fe-warning-soft: #f8f0dd;
|
|
53
53
|
|
|
54
|
+
--fe-info: #1f6fb2;
|
|
55
|
+
--fe-info-soft: #e5f0f9;
|
|
56
|
+
|
|
54
57
|
--fe-focus: #4b5bd7;
|
|
55
58
|
|
|
59
|
+
/* Lo que queda detrás de un diálogo o un drawer abierto. Era un rgba
|
|
60
|
+
escrito dentro de .fe-overlay, igual en claro que en oscuro. */
|
|
61
|
+
--fe-overlay: rgba(16, 18, 27, 0.45);
|
|
62
|
+
|
|
56
63
|
/* ---------- FORMA ---------- */
|
|
57
64
|
|
|
58
65
|
--fe-radius-sm: 4px;
|
|
@@ -97,6 +104,30 @@
|
|
|
97
104
|
/* ---------- MOVIMIENTO ---------- */
|
|
98
105
|
|
|
99
106
|
--fe-transition: 120ms ease;
|
|
107
|
+
--fe-duration-fast: 120ms;
|
|
108
|
+
--fe-duration: 200ms;
|
|
109
|
+
/* Rápido al empezar y suave al llegar: lo que se abre tiene que sentirse
|
|
110
|
+
inmediato, no animado. */
|
|
111
|
+
--fe-ease-out: cubic-bezier(0.2, 0.8, 0.2, 1);
|
|
112
|
+
|
|
113
|
+
/* ---------- CAPAS ---------- */
|
|
114
|
+
|
|
115
|
+
/* Un orden y no un número por componente. Lo que se abre con showModal() o
|
|
116
|
+
con el atributo popover ya vive en la capa superior del navegador y no
|
|
117
|
+
necesita z-index; esto es para lo que no: la cabecera fija de una tabla,
|
|
118
|
+
un overlay dibujado a mano, los tooltips. */
|
|
119
|
+
--fe-z-sticky: 10;
|
|
120
|
+
--fe-z-dropdown: 40;
|
|
121
|
+
--fe-z-overlay: 50;
|
|
122
|
+
--fe-z-tooltip: 60;
|
|
123
|
+
--fe-z-toast: 70;
|
|
124
|
+
|
|
125
|
+
/* ---------- MEDIDAS DE APLICACION ---------- */
|
|
126
|
+
|
|
127
|
+
--fe-dialog-width: 32rem;
|
|
128
|
+
--fe-drawer-width: 32rem;
|
|
129
|
+
--fe-command-width: 40rem;
|
|
130
|
+
--fe-sidebar-width: 15rem;
|
|
100
131
|
}
|
|
101
132
|
|
|
102
133
|
/* La elección explícita de claro gana sobre un sistema en oscuro, de ahí el
|
|
@@ -133,8 +164,13 @@
|
|
|
133
164
|
--fe-warning: #d9ac53;
|
|
134
165
|
--fe-warning-soft: #2a2417;
|
|
135
166
|
|
|
167
|
+
--fe-info: #7cb3e6;
|
|
168
|
+
--fe-info-soft: #16222e;
|
|
169
|
+
|
|
136
170
|
--fe-focus: #8b95f0;
|
|
137
171
|
|
|
172
|
+
--fe-overlay: rgba(0, 0, 0, 0.6);
|
|
173
|
+
|
|
138
174
|
--fe-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.4);
|
|
139
175
|
--fe-shadow: 0 4px 12px rgba(0, 0, 0, 0.45);
|
|
140
176
|
--fe-shadow-lg: 0 16px 40px rgba(0, 0, 0, 0.55);
|
|
@@ -173,8 +209,13 @@
|
|
|
173
209
|
--fe-warning: #d9ac53;
|
|
174
210
|
--fe-warning-soft: #2a2417;
|
|
175
211
|
|
|
212
|
+
--fe-info: #7cb3e6;
|
|
213
|
+
--fe-info-soft: #16222e;
|
|
214
|
+
|
|
176
215
|
--fe-focus: #8b95f0;
|
|
177
216
|
|
|
217
|
+
--fe-overlay: rgba(0, 0, 0, 0.6);
|
|
218
|
+
|
|
178
219
|
--fe-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.4);
|
|
179
220
|
--fe-shadow: 0 4px 12px rgba(0, 0, 0, 0.45);
|
|
180
221
|
--fe-shadow-lg: 0 16px 40px rgba(0, 0, 0, 0.55);
|