-
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathAlert.svelte
More file actions
123 lines (115 loc) 路 3.38 KB
/
Copy pathAlert.svelte
File metadata and controls
123 lines (115 loc) 路 3.38 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
<script lang="ts">
import type { Snippet } from 'svelte';
import type { HTMLAttributes, SvelteHTMLElements } from 'svelte/elements';
import { alert_status_options, type AlertStatus } from './alert.ts';
/**
* @see {@link https://www.w3.org/WAI/ARIA/apg/patterns/alert/}
*
* @module
*/
// TODO think through Alert+Card APIs together, one can be a button and the other a link atm
const {
status = 'inform',
color,
onclick,
disabled,
icon,
button_attrs,
div_attrs,
children,
...rest
}: // generic element attrs, the common denominator of the rendered roots - // branch-specific attributes go in `button_attrs`/`div_attrs`
HTMLAttributes<HTMLElement> & {
status?: AlertStatus;
color?: string;
// TODO maybe an explicit `AlertButton` is better,
// or rethink the design because `role="alert"` can't be put on buttons -
// `disabled` and `button_attrs` only make sense when `onclick` is defined
/** Renders the alert as a `<button>` when provided. */
onclick?: (() => void) | undefined;
disabled?: boolean;
icon?: string | Snippet<[icon: string]> | null | undefined; // TODO experimenting with this, gets complex in the impl
/** Button attributes, applied only when `onclick` renders the alert as a `<button>`. */
button_attrs?: SvelteHTMLElements['button'];
/** Div attributes, applied only when the alert renders as a `<div>` (no `onclick`). */
div_attrs?: SvelteHTMLElements['div'];
children: Snippet;
} = $props();
const options = $derived(alert_status_options[status]);
// TODO change this to use the hue and put transparency on the borders, or add a borderColor option
const { color: status_color, icon: status_icon } = $derived(options);
const final_color = $derived(color ?? status_color);
const final_icon = $derived(
typeof icon === 'string' ? icon : (status_icon ?? alert_status_options.inform.icon!)
);
</script>
{#if onclick}
<button
type="button"
{...rest}
{...button_attrs}
class="alert {rest.class} {button_attrs?.class}"
style:--text_color={final_color}
{onclick}
disabled={disabled ?? button_attrs?.disabled}
>
{@render content()}
</button>
{:else}
<div
role="alert"
{...rest}
{...div_attrs}
class="alert panel {rest.class} {div_attrs?.class}"
style:--text_color={final_color}
>
{@render content()}
</div>
{/if}
{#snippet content()}
{#if icon !== null}
<div class="icon">
{#if !icon || typeof icon === 'string'}
{final_icon}
{:else}
{@render icon(final_icon)}
{/if}
</div>
{/if}
<!-- TODO conflict in the names of content as the snippet and this inner div -->
<div class="content">
{@render children()}
</div>
{/snippet}
<style>
.alert {
min-height: var(--alert_min_height);
width: 100%;
color: var(--text_color);
font-size: var(--font_size_md);
border-width: var(--border_width_1);
border-style: var(--border_style);
border-color: var(--text_color);
display: flex;
justify-content: flex-start; /* override button horizontal centering */
align-items: center;
padding: var(--space_xs2) var(--space_lg) var(--space_xs2) var(--space_xs);
margin-bottom: var(--space_lg);
}
.alert:last-child {
margin-bottom: 0;
}
.icon {
display: flex;
align-items: center;
justify-content: center;
margin-right: var(--space_md);
font-size: var(--font_size_xl2);
min-width: var(--font_size_xl2);
text-align: center;
}
.content {
text-align: left;
flex: 1;
}
</style>