@happyvertical/smrt-messages 0.34.5 → 0.34.7
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/dist/manifest.json +2 -2
- package/dist/smrt-knowledge.json +4 -4
- package/dist/svelte/components/AccountCard.svelte +22 -13
- package/dist/svelte/components/AttachmentChip.svelte +26 -7
- package/dist/svelte/components/AttachmentChip.svelte.d.ts +0 -3
- package/dist/svelte/components/AttachmentChip.svelte.d.ts.map +1 -1
- package/dist/svelte/components/AttachmentUpload.svelte +19 -7
- package/dist/svelte/components/AttachmentUpload.svelte.d.ts.map +1 -1
- package/dist/svelte/components/ComposeForm.svelte +66 -75
- package/dist/svelte/components/ComposeForm.svelte.d.ts.map +1 -1
- package/dist/svelte/components/EmailAccountManager.svelte +81 -115
- package/dist/svelte/components/EmailAccountManager.svelte.d.ts.map +1 -1
- package/dist/svelte/components/EmailFilterManager.svelte +94 -127
- package/dist/svelte/components/EmailFilterManager.svelte.d.ts.map +1 -1
- package/dist/svelte/components/FolderNav.svelte +22 -15
- package/dist/svelte/components/FolderNav.svelte.d.ts.map +1 -1
- package/dist/svelte/components/ForwardForm.svelte +16 -48
- package/dist/svelte/components/ForwardForm.svelte.d.ts.map +1 -1
- package/dist/svelte/components/MessageCard.svelte +14 -6
- package/dist/svelte/components/MessageCard.svelte.d.ts.map +1 -1
- package/dist/svelte/components/MessageDetail.svelte +19 -12
- package/dist/svelte/components/MessageFilters.svelte +47 -43
- package/dist/svelte/components/MessageFilters.svelte.d.ts.map +1 -1
- package/dist/svelte/components/MessageToolbar.svelte +28 -20
- package/dist/svelte/components/MessageToolbar.svelte.d.ts.map +1 -1
- package/dist/svelte/components/RecipientInput.svelte +32 -9
- package/dist/svelte/components/RecipientInput.svelte.d.ts.map +1 -1
- package/dist/svelte/components/ReplyForm.svelte +16 -48
- package/dist/svelte/components/ReplyForm.svelte.d.ts.map +1 -1
- package/dist/svelte/components/ThreadView.svelte +19 -12
- package/dist/svelte/components/ThreadView.svelte.d.ts.map +1 -1
- package/package.json +8 -7
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { useI18n } from '@happyvertical/smrt-ui/i18n';
|
|
7
|
+
import { Button } from '@happyvertical/smrt-ui/ui';
|
|
7
8
|
import type { Snippet } from 'svelte';
|
|
8
9
|
import { M } from '../i18n.messages.js';
|
|
9
10
|
import type { AccountData, MessageData } from '../types.js';
|
|
@@ -97,6 +98,7 @@ const _slackMeta = $derived.by(() => {
|
|
|
97
98
|
>
|
|
98
99
|
{#if onselect}
|
|
99
100
|
<div class="select-col">
|
|
101
|
+
<!-- raw-primitive-allow: native checkbox; no Provider-free checkbox primitive (Toggle is a switch with different semantics, CheckboxInput requires a Provider) -->
|
|
100
102
|
<input
|
|
101
103
|
type="checkbox"
|
|
102
104
|
checked={selected}
|
|
@@ -106,6 +108,7 @@ const _slackMeta = $derived.by(() => {
|
|
|
106
108
|
</div>
|
|
107
109
|
{/if}
|
|
108
110
|
|
|
111
|
+
<!-- raw-primitive-allow: large pressable message row wrapping rich header/subject/preview content (a structural selection surface with descendant compact-mode layout rules), not a standard action button -->
|
|
109
112
|
<button
|
|
110
113
|
class="message-content"
|
|
111
114
|
type="button"
|
|
@@ -162,15 +165,15 @@ const _slackMeta = $derived.by(() => {
|
|
|
162
165
|
</button>
|
|
163
166
|
|
|
164
167
|
{#if onflag}
|
|
165
|
-
<
|
|
168
|
+
<Button
|
|
169
|
+
variant="ghost"
|
|
166
170
|
class="flag-btn"
|
|
167
|
-
type="button"
|
|
168
171
|
onclick={() => onflag?.(message)}
|
|
169
172
|
title={message.isFlagged ? 'Unflag' : 'Flag'}
|
|
170
173
|
aria-label={message.isFlagged ? 'Unflag' : 'Flag'}
|
|
171
174
|
>
|
|
172
175
|
{message.isFlagged ? '⚑' : '⚐'}
|
|
173
|
-
</
|
|
176
|
+
</Button>
|
|
174
177
|
{/if}
|
|
175
178
|
</div>
|
|
176
179
|
|
|
@@ -308,21 +311,26 @@ const _slackMeta = $derived.by(() => {
|
|
|
308
311
|
color: var(--smrt-color-on-surface-variant, #43474e);
|
|
309
312
|
}
|
|
310
313
|
|
|
311
|
-
|
|
314
|
+
/*
|
|
315
|
+
* The flag toggle now renders through smrt-ui's <Button variant="ghost">.
|
|
316
|
+
* `.message-card :global(.flag-btn)` anchors on the real `.message-card`
|
|
317
|
+
* element and pierces the Button child scope to keep the dimmed icon-toggle
|
|
318
|
+
* styling (issue #1589).
|
|
319
|
+
*/
|
|
320
|
+
.message-card :global(.flag-btn) {
|
|
312
321
|
display: flex;
|
|
313
322
|
align-items: center;
|
|
314
323
|
justify-content: center;
|
|
315
324
|
width: 2rem;
|
|
316
325
|
border: none;
|
|
317
326
|
background: none;
|
|
318
|
-
cursor: pointer;
|
|
319
327
|
font-size: var(--smrt-typography-body-large-size, 1rem);
|
|
320
328
|
color: var(--smrt-color-on-surface-variant, #43474e);
|
|
321
329
|
opacity: 0.5;
|
|
322
330
|
transition: opacity var(--smrt-duration-short2, 150ms);
|
|
323
331
|
}
|
|
324
332
|
|
|
325
|
-
.flag-btn:hover {
|
|
333
|
+
.message-card :global(.flag-btn):hover {
|
|
326
334
|
opacity: 1;
|
|
327
335
|
}
|
|
328
336
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MessageCard.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/MessageCard.svelte.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"MessageCard.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/MessageCard.svelte.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAEtC,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAK5D,MAAM,WAAW,KAAK;IACpB,OAAO,EAAE,WAAW,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,CAAC;IACzC,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,CAAC;IAC1C,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,CAAC;IACxC,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,WAAW,CAAC,EAAE,OAAO,CAAC,CAAC;QAAE,OAAO,EAAE,WAAW,CAAA;KAAE,CAAC,CAAC,CAAC;CACnD;AAgJD,QAAA,MAAM,WAAW,2CAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* MessageDetail - Full message view with type-adaptive sections
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { Card } from '@happyvertical/smrt-ui/ui';
|
|
6
|
+
import { Button, Card } from '@happyvertical/smrt-ui/ui';
|
|
7
7
|
import type { Snippet } from 'svelte';
|
|
8
8
|
import type { AccountData, AttachmentData, MessageData } from '../types.js';
|
|
9
9
|
import AttachmentChip from './AttachmentChip.svelte';
|
|
@@ -162,19 +162,19 @@ const _displayBody = $derived.by(() => {
|
|
|
162
162
|
{#if onreply || onforward || ondelete}
|
|
163
163
|
<div class="actions">
|
|
164
164
|
{#if onreply}
|
|
165
|
-
<
|
|
165
|
+
<Button variant="ghost" class="action-btn" onclick={() => onreply?.(message)}>
|
|
166
166
|
↩ Reply
|
|
167
|
-
</
|
|
167
|
+
</Button>
|
|
168
168
|
{/if}
|
|
169
169
|
{#if onforward}
|
|
170
|
-
<
|
|
170
|
+
<Button variant="ghost" class="action-btn" onclick={() => onforward?.(message)}>
|
|
171
171
|
↪ Forward
|
|
172
|
-
</
|
|
172
|
+
</Button>
|
|
173
173
|
{/if}
|
|
174
174
|
{#if ondelete}
|
|
175
|
-
<
|
|
175
|
+
<Button variant="ghost" class="action-btn action-btn--danger" onclick={() => ondelete?.(message)}>
|
|
176
176
|
🗑 Delete
|
|
177
|
-
</
|
|
177
|
+
</Button>
|
|
178
178
|
{/if}
|
|
179
179
|
</div>
|
|
180
180
|
{/if}
|
|
@@ -280,27 +280,34 @@ const _displayBody = $derived.by(() => {
|
|
|
280
280
|
border-top: 1px solid var(--smrt-color-outline-variant, #c4c6d0);
|
|
281
281
|
}
|
|
282
282
|
|
|
283
|
-
|
|
283
|
+
/*
|
|
284
|
+
* Action buttons now render through smrt-ui's <Button variant="ghost">. The
|
|
285
|
+
* <button> is emitted inside the Button child, so `.actions :global(.action-btn)`
|
|
286
|
+
* anchors on the real `.actions` element and pierces the child scope to keep
|
|
287
|
+
* the original outlined-surface styling (issue #1589). The destructive button's
|
|
288
|
+
* modifier is `action-btn--danger`, NOT `danger`, to avoid colliding with
|
|
289
|
+
* Button's own `.danger` variant class.
|
|
290
|
+
*/
|
|
291
|
+
.actions :global(.action-btn) {
|
|
284
292
|
padding: 0.5rem 1rem;
|
|
285
293
|
border: 1px solid var(--smrt-color-outline, #72787e);
|
|
286
294
|
border-radius: var(--smrt-radius-small, 0.25rem);
|
|
287
295
|
background: var(--smrt-color-surface, #fefbff);
|
|
288
296
|
color: var(--smrt-color-on-surface, #1a1c1e);
|
|
289
297
|
font: var(--smrt-typography-label-large-font, 500 0.875rem / 1.25 sans-serif);
|
|
290
|
-
cursor: pointer;
|
|
291
298
|
transition: background var(--smrt-duration-short2, 150ms);
|
|
292
299
|
}
|
|
293
300
|
|
|
294
|
-
.action-btn:hover {
|
|
301
|
+
.actions :global(.action-btn):hover {
|
|
295
302
|
background: var(--smrt-color-surface-variant, #e1e2ec);
|
|
296
303
|
}
|
|
297
304
|
|
|
298
|
-
.action-btn--danger {
|
|
305
|
+
.actions :global(.action-btn--danger) {
|
|
299
306
|
color: var(--smrt-color-error, #ba1a1a);
|
|
300
307
|
border-color: var(--smrt-color-error, #ba1a1a);
|
|
301
308
|
}
|
|
302
309
|
|
|
303
|
-
.action-btn--danger:hover {
|
|
310
|
+
.actions :global(.action-btn--danger):hover {
|
|
304
311
|
background: var(--smrt-color-error-container, #ffdad6);
|
|
305
312
|
}
|
|
306
313
|
</style>
|
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* MessageFilters - Filter/sort controls bar
|
|
4
4
|
*/
|
|
5
|
+
import { Input, Select } from '@happyvertical/smrt-ui/forms';
|
|
5
6
|
import { useI18n } from '@happyvertical/smrt-ui/i18n';
|
|
7
|
+
import { Button } from '@happyvertical/smrt-ui/ui';
|
|
6
8
|
import { M } from '../i18n.messages.js';
|
|
7
9
|
import type {
|
|
8
10
|
AccountData,
|
|
@@ -77,7 +79,7 @@ const _hasActiveFilters = $derived(
|
|
|
77
79
|
|
|
78
80
|
<div class="message-filters" role="search" aria-label={t(M['messages.message_filters.filters_label'])}>
|
|
79
81
|
<div class="search-row">
|
|
80
|
-
<
|
|
82
|
+
<Input
|
|
81
83
|
type="search"
|
|
82
84
|
class="search-input"
|
|
83
85
|
placeholder={t(M['messages.message_filters.search_placeholder'])}
|
|
@@ -85,41 +87,41 @@ const _hasActiveFilters = $derived(
|
|
|
85
87
|
onkeydown={(e) => { if (e.key === 'Enter') handleSearch(); }}
|
|
86
88
|
aria-label={t(M['messages.message_filters.search_label'])}
|
|
87
89
|
/>
|
|
88
|
-
<
|
|
90
|
+
<Button variant="primary" class="search-btn" onclick={handleSearch}>Search</Button>
|
|
89
91
|
</div>
|
|
90
92
|
|
|
91
93
|
<div class="filter-row">
|
|
92
|
-
<
|
|
94
|
+
<Select
|
|
93
95
|
class="filter-select"
|
|
94
96
|
value={filters.type || ''}
|
|
95
|
-
onchange={(e) => updateFilter('type', (e.
|
|
97
|
+
onchange={(e) => updateFilter('type', (e.currentTarget as HTMLSelectElement).value)}
|
|
96
98
|
aria-label={t(M['messages.message_filters.filter_by_type'])}
|
|
97
99
|
>
|
|
98
100
|
<option value="">{t(M['messages.message_filters.all_types'])}</option>
|
|
99
101
|
{#each availableTypes as type}
|
|
100
102
|
<option value={type}>{type === 'email' ? 'Email' : type === 'tweet' ? 'Tweet' : type === 'slack' ? 'Slack' : type}</option>
|
|
101
103
|
{/each}
|
|
102
|
-
</
|
|
104
|
+
</Select>
|
|
103
105
|
|
|
104
106
|
{#if accounts.length > 0}
|
|
105
|
-
<
|
|
107
|
+
<Select
|
|
106
108
|
class="filter-select"
|
|
107
109
|
value={filters.accountId || ''}
|
|
108
|
-
onchange={(e) => updateFilter('accountId', (e.
|
|
110
|
+
onchange={(e) => updateFilter('accountId', (e.currentTarget as HTMLSelectElement).value)}
|
|
109
111
|
aria-label={t(M['messages.message_filters.filter_by_account'])}
|
|
110
112
|
>
|
|
111
113
|
<option value="">{t(M['messages.message_filters.all_accounts'])}</option>
|
|
112
114
|
{#each accounts as account}
|
|
113
115
|
<option value={account.id}>{account.name}</option>
|
|
114
116
|
{/each}
|
|
115
|
-
</
|
|
117
|
+
</Select>
|
|
116
118
|
{/if}
|
|
117
119
|
|
|
118
|
-
<
|
|
120
|
+
<Select
|
|
119
121
|
class="filter-select"
|
|
120
122
|
value={filters.isRead === undefined ? '' : filters.isRead ? 'read' : 'unread'}
|
|
121
123
|
onchange={(e) => {
|
|
122
|
-
const val = (e.
|
|
124
|
+
const val = (e.currentTarget as HTMLSelectElement).value;
|
|
123
125
|
updateFilter('isRead', val === '' ? undefined : val === 'read');
|
|
124
126
|
}}
|
|
125
127
|
aria-label={t(M['messages.message_filters.filter_by_read_status'])}
|
|
@@ -127,21 +129,22 @@ const _hasActiveFilters = $derived(
|
|
|
127
129
|
<option value="">Read & unread</option>
|
|
128
130
|
<option value="unread">{t(M['messages.message_filters.unread_only'])}</option>
|
|
129
131
|
<option value="read">{t(M['messages.message_filters.read_only'])}</option>
|
|
130
|
-
</
|
|
132
|
+
</Select>
|
|
131
133
|
|
|
132
134
|
<label class="filter-checkbox">
|
|
135
|
+
<!-- raw-primitive-allow: native checkbox; no Provider-free checkbox primitive (Toggle is a switch with different semantics, CheckboxInput requires a Provider) -->
|
|
133
136
|
<input
|
|
134
137
|
type="checkbox"
|
|
135
138
|
checked={filters.isFlagged === true}
|
|
136
|
-
onchange={(e) => updateFilter('isFlagged', (e.
|
|
139
|
+
onchange={(e) => updateFilter('isFlagged', (e.currentTarget as HTMLInputElement).checked ? true : undefined)}
|
|
137
140
|
/>
|
|
138
141
|
Flagged
|
|
139
142
|
</label>
|
|
140
143
|
|
|
141
144
|
{#if _hasActiveFilters}
|
|
142
|
-
<
|
|
145
|
+
<Button variant="ghost" size="sm" class="clear-btn" onclick={clearFilters}>
|
|
143
146
|
{t(M['messages.message_filters.clear_filters'])}
|
|
144
|
-
</
|
|
147
|
+
</Button>
|
|
145
148
|
{/if}
|
|
146
149
|
</div>
|
|
147
150
|
</div>
|
|
@@ -162,33 +165,26 @@ const _hasActiveFilters = $derived(
|
|
|
162
165
|
gap: 0.5rem;
|
|
163
166
|
}
|
|
164
167
|
|
|
165
|
-
|
|
168
|
+
/*
|
|
169
|
+
* The search field now renders through smrt-ui's <Input>. The primitive owns
|
|
170
|
+
* border / background / focus; `.search-row :global(.search-input)` only
|
|
171
|
+
* re-asserts `flex: 1` so it shares the row with the Search button, piercing
|
|
172
|
+
* the Input child scope (issue #1589).
|
|
173
|
+
*/
|
|
174
|
+
.search-row :global(.search-input) {
|
|
166
175
|
flex: 1;
|
|
167
|
-
padding: 0.5rem 0.75rem;
|
|
168
|
-
border: 1px solid var(--smrt-color-outline, #72787e);
|
|
169
|
-
border-radius: var(--smrt-radius-small, 0.25rem);
|
|
170
|
-
font: var(--smrt-typography-body-medium-font, 0.875rem / 1.25 sans-serif);
|
|
171
|
-
background: var(--smrt-color-surface, #fefbff);
|
|
172
|
-
color: var(--smrt-color-on-surface, #1a1c1e);
|
|
173
176
|
}
|
|
174
177
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
178
|
+
/*
|
|
179
|
+
* Search now renders through smrt-ui's <Button variant="primary">. The variant
|
|
180
|
+
* owns the filled-primary background + hover; `.search-row :global(.search-btn)`
|
|
181
|
+
* only re-asserts the original small radius and label font/padding by piercing
|
|
182
|
+
* the Button child scope (issue #1589).
|
|
183
|
+
*/
|
|
184
|
+
.search-row :global(.search-btn) {
|
|
181
185
|
padding: 0.5rem 1rem;
|
|
182
|
-
border: none;
|
|
183
186
|
border-radius: var(--smrt-radius-small, 0.25rem);
|
|
184
|
-
background: var(--smrt-color-primary, #005ac1);
|
|
185
|
-
color: var(--smrt-color-on-primary, #fff);
|
|
186
187
|
font: var(--smrt-typography-label-large-font, 500 0.875rem / 1.25 sans-serif);
|
|
187
|
-
cursor: pointer;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
.search-btn:hover {
|
|
191
|
-
opacity: 0.9;
|
|
192
188
|
}
|
|
193
189
|
|
|
194
190
|
.filter-row {
|
|
@@ -198,13 +194,16 @@ const _hasActiveFilters = $derived(
|
|
|
198
194
|
gap: 0.5rem;
|
|
199
195
|
}
|
|
200
196
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
197
|
+
/*
|
|
198
|
+
* The filter dropdowns now render through smrt-ui's <Select>. The primitive
|
|
199
|
+
* owns border / background / focus / chevron; `.filter-row :global(.filter-select)`
|
|
200
|
+
* re-asserts the compact filter-bar sizing (auto width, tighter padding, small
|
|
201
|
+
* font) by piercing the Select child scope (issue #1589).
|
|
202
|
+
*/
|
|
203
|
+
.filter-row :global(.filter-select) {
|
|
204
|
+
width: auto;
|
|
205
|
+
padding: 0.375rem 2rem 0.375rem 0.5rem;
|
|
205
206
|
font: var(--smrt-typography-body-small-font, 0.75rem / 1.33 sans-serif);
|
|
206
|
-
background: var(--smrt-color-surface, #fefbff);
|
|
207
|
-
color: var(--smrt-color-on-surface, #1a1c1e);
|
|
208
207
|
}
|
|
209
208
|
|
|
210
209
|
.filter-checkbox {
|
|
@@ -216,13 +215,18 @@ const _hasActiveFilters = $derived(
|
|
|
216
215
|
cursor: pointer;
|
|
217
216
|
}
|
|
218
217
|
|
|
219
|
-
|
|
218
|
+
/*
|
|
219
|
+
* Clear filters now renders through smrt-ui's <Button variant="ghost">.
|
|
220
|
+
* `.filter-row :global(.clear-btn)` anchors on the real `.filter-row` element
|
|
221
|
+
* and pierces the Button child scope to keep the underlined text-link look
|
|
222
|
+
* (issue #1589).
|
|
223
|
+
*/
|
|
224
|
+
.filter-row :global(.clear-btn) {
|
|
220
225
|
padding: 0.25rem 0.5rem;
|
|
221
226
|
border: none;
|
|
222
227
|
background: none;
|
|
223
228
|
font: var(--smrt-typography-label-medium-font, 500 0.75rem / 1.33 sans-serif);
|
|
224
229
|
color: var(--smrt-color-primary, #005ac1);
|
|
225
|
-
cursor: pointer;
|
|
226
230
|
text-decoration: underline;
|
|
227
231
|
}
|
|
228
232
|
</style>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MessageFilters.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/MessageFilters.svelte.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"MessageFilters.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/MessageFilters.svelte.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EACV,WAAW,EACX,kBAAkB,EAClB,WAAW,EACX,WAAW,EACZ,MAAM,aAAa,CAAC;AAGrB,MAAM,WAAW,KAAK;IACpB,OAAO,CAAC,EAAE,kBAAkB,CAAC;IAC7B,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,QAAQ,CAAC,EAAE,WAAW,EAAE,CAAC;IACzB,cAAc,CAAC,EAAE,WAAW,EAAE,CAAC;IAC/B,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,kBAAkB,KAAK,IAAI,CAAC;IACvD,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACpC;AAmHD,QAAA,MAAM,cAAc,2CAAwC,CAAC;AAC7D,KAAK,cAAc,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AACxD,eAAe,cAAc,CAAC"}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* MessageToolbar - Bulk action toolbar
|
|
4
4
|
*/
|
|
5
5
|
import { useI18n } from '@happyvertical/smrt-ui/i18n';
|
|
6
|
+
import { Button } from '@happyvertical/smrt-ui/ui';
|
|
6
7
|
import type { Snippet } from 'svelte';
|
|
7
8
|
import { M } from '../i18n.messages.js';
|
|
8
9
|
import type { BulkAction } from '../types.js';
|
|
@@ -33,37 +34,37 @@ const {
|
|
|
33
34
|
{#if selectedCount > 0}
|
|
34
35
|
<span class="count">{t(M['messages.message_toolbar.count_selected'], { selectedCount, totalCount })}</span>
|
|
35
36
|
{#if onclearselection}
|
|
36
|
-
<
|
|
37
|
+
<Button variant="ghost" size="sm" class="link-btn" onclick={onclearselection}>
|
|
37
38
|
Clear
|
|
38
|
-
</
|
|
39
|
+
</Button>
|
|
39
40
|
{/if}
|
|
40
41
|
{:else}
|
|
41
42
|
<span class="count">{totalCount} messages</span>
|
|
42
43
|
{/if}
|
|
43
44
|
{#if onselectall && selectedCount < totalCount}
|
|
44
|
-
<
|
|
45
|
+
<Button variant="ghost" size="sm" class="link-btn" onclick={onselectall}>
|
|
45
46
|
{t(M['messages.message_toolbar.select_all'])}
|
|
46
|
-
</
|
|
47
|
+
</Button>
|
|
47
48
|
{/if}
|
|
48
49
|
</div>
|
|
49
50
|
|
|
50
51
|
{#if selectedCount > 0 && onaction}
|
|
51
52
|
<div class="actions">
|
|
52
|
-
<
|
|
53
|
+
<Button variant="ghost" size="sm" class="action-btn" onclick={() => onaction?.('markRead')}>
|
|
53
54
|
{t(M['messages.message_toolbar.mark_read'])}
|
|
54
|
-
</
|
|
55
|
-
<
|
|
55
|
+
</Button>
|
|
56
|
+
<Button variant="ghost" size="sm" class="action-btn" onclick={() => onaction?.('markUnread')}>
|
|
56
57
|
{t(M['messages.message_toolbar.mark_unread'])}
|
|
57
|
-
</
|
|
58
|
-
<
|
|
58
|
+
</Button>
|
|
59
|
+
<Button variant="ghost" size="sm" class="action-btn" onclick={() => onaction?.('flag')}>
|
|
59
60
|
Flag
|
|
60
|
-
</
|
|
61
|
-
<
|
|
61
|
+
</Button>
|
|
62
|
+
<Button variant="ghost" size="sm" class="action-btn" onclick={() => onaction?.('unflag')}>
|
|
62
63
|
Unflag
|
|
63
|
-
</
|
|
64
|
-
<
|
|
64
|
+
</Button>
|
|
65
|
+
<Button variant="ghost" size="sm" class="action-btn action-btn--danger" onclick={() => onaction?.('delete')}>
|
|
65
66
|
Delete
|
|
66
|
-
</
|
|
67
|
+
</Button>
|
|
67
68
|
{#if extraActions}
|
|
68
69
|
{@render extraActions()}
|
|
69
70
|
{/if}
|
|
@@ -94,12 +95,20 @@ const {
|
|
|
94
95
|
color: var(--smrt-color-on-surface-variant, #43474e);
|
|
95
96
|
}
|
|
96
97
|
|
|
97
|
-
|
|
98
|
+
/*
|
|
99
|
+
* The link and action buttons now render through smrt-ui's <Button
|
|
100
|
+
* variant="ghost">. The <button> is emitted inside the Button child, so a
|
|
101
|
+
* plain scoped rule would not match — anchoring on the real `.selection-info`
|
|
102
|
+
* / `.actions` elements and piercing with `:global(...)` keeps the original
|
|
103
|
+
* text-link / outlined-surface styling (issue #1589). The destructive button's
|
|
104
|
+
* modifier is `action-btn--danger`, NOT `danger`, so it never collides with
|
|
105
|
+
* Button's own `.danger` variant class.
|
|
106
|
+
*/
|
|
107
|
+
.selection-info :global(.link-btn) {
|
|
98
108
|
border: none;
|
|
99
109
|
background: none;
|
|
100
110
|
font: var(--smrt-typography-label-medium-font, 500 0.75rem / 1.33 sans-serif);
|
|
101
111
|
color: var(--smrt-color-primary, #005ac1);
|
|
102
|
-
cursor: pointer;
|
|
103
112
|
text-decoration: underline;
|
|
104
113
|
padding: 0;
|
|
105
114
|
}
|
|
@@ -110,21 +119,20 @@ const {
|
|
|
110
119
|
flex-wrap: wrap;
|
|
111
120
|
}
|
|
112
121
|
|
|
113
|
-
.action-btn {
|
|
122
|
+
.actions :global(.action-btn) {
|
|
114
123
|
padding: 0.25rem 0.625rem;
|
|
115
124
|
border: 1px solid var(--smrt-color-outline, #72787e);
|
|
116
125
|
border-radius: var(--smrt-radius-small, 0.25rem);
|
|
117
126
|
background: var(--smrt-color-surface, #fefbff);
|
|
118
127
|
color: var(--smrt-color-on-surface, #1a1c1e);
|
|
119
128
|
font: var(--smrt-typography-label-small-font, 500 0.6875rem / 1 sans-serif);
|
|
120
|
-
cursor: pointer;
|
|
121
129
|
}
|
|
122
130
|
|
|
123
|
-
.action-btn:hover {
|
|
131
|
+
.actions :global(.action-btn):hover {
|
|
124
132
|
background: var(--smrt-color-surface-variant, #e1e2ec);
|
|
125
133
|
}
|
|
126
134
|
|
|
127
|
-
.action-btn--danger {
|
|
135
|
+
.actions :global(.action-btn--danger) {
|
|
128
136
|
color: var(--smrt-color-error, #ba1a1a);
|
|
129
137
|
border-color: var(--smrt-color-error, #ba1a1a);
|
|
130
138
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MessageToolbar.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/MessageToolbar.svelte.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"MessageToolbar.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/MessageToolbar.svelte.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAEtC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAG9C,MAAM,WAAW,KAAK;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,IAAI,CAAC;IACxC,WAAW,CAAC,EAAE,MAAM,IAAI,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,IAAI,CAAC;IAC9B,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAqED,QAAA,MAAM,cAAc,2CAAwC,CAAC;AAC7D,KAAK,cAAc,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AACxD,eAAe,cAAc,CAAC"}
|
|
@@ -10,6 +10,9 @@ export interface Props {
|
|
|
10
10
|
</script>
|
|
11
11
|
|
|
12
12
|
<script lang="ts">
|
|
13
|
+
import { Input } from '@happyvertical/smrt-ui/forms';
|
|
14
|
+
import { Button } from '@happyvertical/smrt-ui/ui';
|
|
15
|
+
|
|
13
16
|
let {
|
|
14
17
|
label = 'To',
|
|
15
18
|
recipients = [],
|
|
@@ -61,17 +64,18 @@ export interface Props {
|
|
|
61
64
|
{#each recipients as recipient, i}
|
|
62
65
|
<span class="chip" class:invalid={!recipient.isValid}>
|
|
63
66
|
<span class="chip-text">{recipient.name || recipient.address}</span>
|
|
64
|
-
<
|
|
67
|
+
<Button
|
|
68
|
+
variant="ghost"
|
|
69
|
+
size="sm"
|
|
65
70
|
class="chip-remove"
|
|
66
71
|
onclick={() => removeRecipient(i)}
|
|
67
|
-
|
|
68
|
-
>×</button>
|
|
72
|
+
>×</Button>
|
|
69
73
|
</span>
|
|
70
74
|
{/each}
|
|
71
|
-
<
|
|
75
|
+
<Input
|
|
72
76
|
id={inputId}
|
|
73
77
|
type="text"
|
|
74
|
-
class="
|
|
78
|
+
class="recipient-field"
|
|
75
79
|
bind:value={inputValue}
|
|
76
80
|
{placeholder}
|
|
77
81
|
onkeydown={handleKeydown}
|
|
@@ -122,29 +126,48 @@ export interface Props {
|
|
|
122
126
|
color: var(--smrt-color-on-error-container, #410002);
|
|
123
127
|
}
|
|
124
128
|
|
|
125
|
-
|
|
129
|
+
/*
|
|
130
|
+
* The chip remove button now renders through smrt-ui's <Button variant="ghost">.
|
|
131
|
+
* `.chips-container :global(.chip-remove)` anchors on the real `.chips-container`
|
|
132
|
+
* element and pierces the Button child scope to keep the compact icon styling.
|
|
133
|
+
* `color: inherit` keeps it matching the chip text color (including the invalid
|
|
134
|
+
* chip's error color) — issue #1589.
|
|
135
|
+
*/
|
|
136
|
+
.chips-container :global(.chip-remove) {
|
|
126
137
|
background: none;
|
|
127
138
|
border: none;
|
|
128
|
-
cursor: pointer;
|
|
129
139
|
padding: 0 var(--smrt-spacing-1, 4px);
|
|
130
140
|
font-size: var(--smrt-typography-label-large-size, 14px);
|
|
131
141
|
color: inherit;
|
|
132
142
|
opacity: 0.7;
|
|
133
143
|
}
|
|
134
144
|
|
|
135
|
-
.chip-remove:hover {
|
|
145
|
+
.chips-container :global(.chip-remove):hover {
|
|
136
146
|
opacity: 1;
|
|
137
147
|
}
|
|
138
148
|
|
|
139
|
-
|
|
149
|
+
/*
|
|
150
|
+
* The chip-bar field now renders through smrt-ui's <Input>. This is a naked,
|
|
151
|
+
* borderless inline field that sits among the recipient chips, so
|
|
152
|
+
* `.chips-container :global(.recipient-field)` pierces the Input child scope to
|
|
153
|
+
* strip the primitive's border / background / block padding / focus box-shadow
|
|
154
|
+
* and restore the flush inline look (issue #1589).
|
|
155
|
+
*/
|
|
156
|
+
.chips-container :global(.recipient-field) {
|
|
140
157
|
flex: 1;
|
|
141
158
|
min-width: 120px;
|
|
142
159
|
border: none;
|
|
143
160
|
outline: none;
|
|
161
|
+
box-shadow: none;
|
|
144
162
|
font-family: var(--smrt-font-family, system-ui);
|
|
145
163
|
font-size: var(--smrt-typography-body-medium-size, 14px);
|
|
146
164
|
padding: var(--smrt-spacing-1, 4px) 0;
|
|
147
165
|
background: transparent;
|
|
148
166
|
color: var(--smrt-color-on-surface, #1c1b1f);
|
|
149
167
|
}
|
|
168
|
+
|
|
169
|
+
.chips-container :global(.recipient-field):focus {
|
|
170
|
+
border: none;
|
|
171
|
+
box-shadow: none;
|
|
172
|
+
}
|
|
150
173
|
</style>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RecipientInput.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/RecipientInput.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,MAAM,WAAW,KAAK;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,cAAc,EAAE,CAAC;IAC7B,QAAQ,CAAC,EAAE,CAAC,UAAU,EAAE,cAAc,EAAE,KAAK,IAAI,CAAC;IAClD,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;
|
|
1
|
+
{"version":3,"file":"RecipientInput.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/RecipientInput.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,MAAM,WAAW,KAAK;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,cAAc,EAAE,CAAC;IAC7B,QAAQ,CAAC,EAAE,CAAC,UAAU,EAAE,cAAc,EAAE,KAAK,IAAI,CAAC;IAClD,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AA0ED,QAAA,MAAM,cAAc,2CAAwC,CAAC;AAC7D,KAAK,cAAc,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AACxD,eAAe,cAAc,CAAC"}
|
|
@@ -11,6 +11,8 @@ export interface Props {
|
|
|
11
11
|
|
|
12
12
|
<script lang="ts">
|
|
13
13
|
import { useI18n } from '@happyvertical/smrt-ui/i18n';
|
|
14
|
+
import { Textarea } from '@happyvertical/smrt-ui/forms';
|
|
15
|
+
import { Button } from '@happyvertical/smrt-ui/ui';
|
|
14
16
|
import { M } from '../i18n.js';
|
|
15
17
|
|
|
16
18
|
const { t } = useI18n();
|
|
@@ -59,12 +61,11 @@ export interface Props {
|
|
|
59
61
|
{replyAll ? 'Reply All' : 'Reply'} to {originalMessage.senderName || originalMessage.senderAddress}
|
|
60
62
|
</div>
|
|
61
63
|
|
|
62
|
-
<
|
|
63
|
-
class="reply-body"
|
|
64
|
+
<Textarea
|
|
64
65
|
bind:value={body}
|
|
65
66
|
placeholder={t(M['messages.reply_form.body_placeholder'])}
|
|
66
67
|
rows={5}
|
|
67
|
-
|
|
68
|
+
/>
|
|
68
69
|
|
|
69
70
|
<div class="quoted-original">
|
|
70
71
|
<pre class="quoted-text">{quotedBody}</pre>
|
|
@@ -77,17 +78,16 @@ export interface Props {
|
|
|
77
78
|
{/if}
|
|
78
79
|
|
|
79
80
|
<div class="actions">
|
|
80
|
-
<
|
|
81
|
-
|
|
82
|
-
class="btn-primary"
|
|
81
|
+
<Button
|
|
82
|
+
variant="primary"
|
|
83
83
|
disabled={isSending || !body.trim()}
|
|
84
84
|
onclick={handleSend}
|
|
85
85
|
>
|
|
86
86
|
{isSending ? 'Sending...' : 'Send Reply'}
|
|
87
|
-
</
|
|
88
|
-
<
|
|
87
|
+
</Button>
|
|
88
|
+
<Button variant="ghost" class="btn-text" onclick={() => oncancel?.()}>
|
|
89
89
|
Cancel
|
|
90
|
-
</
|
|
90
|
+
</Button>
|
|
91
91
|
</div>
|
|
92
92
|
</div>
|
|
93
93
|
|
|
@@ -109,22 +109,6 @@ export interface Props {
|
|
|
109
109
|
font-weight: var(--smrt-typography-weight-medium, 500);
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
-
.reply-body {
|
|
113
|
-
width: 100%;
|
|
114
|
-
border: 1px solid var(--smrt-color-outline-variant, #cac4d0);
|
|
115
|
-
border-radius: var(--smrt-radius-sm, 8px);
|
|
116
|
-
padding: var(--smrt-spacing-2, 8px);
|
|
117
|
-
font-family: var(--smrt-font-family, system-ui);
|
|
118
|
-
font-size: var(--smrt-typography-body-medium-size, 14px);
|
|
119
|
-
resize: vertical;
|
|
120
|
-
box-sizing: border-box;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
.reply-body:focus {
|
|
124
|
-
outline: 2px solid var(--smrt-color-primary, #6750a4);
|
|
125
|
-
outline-offset: -1px;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
112
|
.quoted-original {
|
|
129
113
|
padding: var(--smrt-spacing-2, 8px);
|
|
130
114
|
border-left: 3px solid var(--smrt-color-outline-variant, #cac4d0);
|
|
@@ -155,29 +139,13 @@ export interface Props {
|
|
|
155
139
|
gap: var(--smrt-spacing-2, 8px);
|
|
156
140
|
}
|
|
157
141
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
font-size: var(--smrt-typography-label-large-size, 14px);
|
|
166
|
-
cursor: pointer;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
.btn-primary:disabled {
|
|
170
|
-
opacity: 0.6;
|
|
171
|
-
cursor: not-allowed;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
.btn-text {
|
|
175
|
-
padding: var(--smrt-spacing-2, 8px) var(--smrt-spacing-4, 16px);
|
|
176
|
-
border: none;
|
|
177
|
-
background: transparent;
|
|
142
|
+
/*
|
|
143
|
+
* Send Reply now uses Button's primary variant directly (dead .btn-primary CSS
|
|
144
|
+
* removed). The Cancel button keeps its neutral on-surface-variant text via
|
|
145
|
+
* `.actions :global(.btn-text)` (Button's ghost uses the primary color) —
|
|
146
|
+
* issue #1589.
|
|
147
|
+
*/
|
|
148
|
+
.actions :global(.btn-text) {
|
|
178
149
|
color: var(--smrt-color-on-surface-variant, #49454f);
|
|
179
|
-
font-family: var(--smrt-font-family, system-ui);
|
|
180
|
-
font-size: var(--smrt-typography-label-large-size, 14px);
|
|
181
|
-
cursor: pointer;
|
|
182
150
|
}
|
|
183
151
|
</style>
|