@lavalogic/scoria 0.40.32 → 0.40.33
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 +58 -58
- package/dist/Components/AccordionGroup.svelte +28 -28
- package/dist/Components/AccordionGroupButton.svelte +60 -60
- package/dist/Components/Action.svelte +48 -48
- package/dist/Components/AlertModal.svelte +102 -102
- package/dist/Components/Base/ContextWrapper.svelte +45 -45
- package/dist/Components/Base/Pagination.svelte +135 -135
- package/dist/Components/Base/Snippets.svelte +25 -25
- package/dist/Components/BigRadioSet.svelte +34 -34
- package/dist/Components/Bubble.svelte +38 -38
- package/dist/Components/Button.svelte +83 -83
- package/dist/Components/Checkbox.svelte +71 -71
- package/dist/Components/CollapsibleCard.svelte +59 -59
- package/dist/Components/CollapsibleMenuGroup.svelte +43 -43
- package/dist/Components/ConditionBuilder/ConditionBuilder.svelte +101 -101
- package/dist/Components/Contexts/ContextMenu.svelte +76 -76
- package/dist/Components/Contexts/ContextMenuDivider.svelte +15 -15
- package/dist/Components/Contexts/ContextMenuOption.svelte +38 -38
- package/dist/Components/Contexts/Toast.svelte +71 -71
- package/dist/Components/Contexts/ToastContainer.svelte +34 -34
- package/dist/Components/Contexts/Tooltip.svelte +32 -32
- package/dist/Components/DataMatrixIcon.svelte +37 -37
- package/dist/Components/DateInput.svelte +80 -80
- package/dist/Components/DatePicker.svelte +86 -86
- package/dist/Components/DesktopModal.svelte +65 -65
- package/dist/Components/Dial.svelte +35 -35
- package/dist/Components/DimensionInput.svelte +95 -95
- package/dist/Components/DragMenu/DragMenuHolder.svelte +53 -53
- package/dist/Components/DragMenu/DraggableCard.svelte +49 -49
- package/dist/Components/FileCard.svelte +18 -18
- package/dist/Components/FileUpload.svelte +115 -115
- package/dist/Components/GridInput.svelte +123 -123
- package/dist/Components/GridInputSet.svelte +21 -21
- package/dist/Components/HorizontalTabGroup.svelte +54 -54
- package/dist/Components/HorizontalTabGroupButton.svelte +32 -32
- package/dist/Components/Icon.svelte +68 -68
- package/dist/Components/InfoAlert.svelte +32 -32
- package/dist/Components/LoadingAnimation.svelte +228 -227
- package/dist/Components/LoadingModal.svelte +19 -19
- package/dist/Components/LoadingOverlay.svelte +20 -20
- package/dist/Components/Modal.svelte +127 -127
- package/dist/Components/MultiSelect.svelte +126 -126
- package/dist/Components/Nav/Nav.svelte +71 -71
- package/dist/Components/Nav/NavServiceMenuList.svelte +37 -37
- package/dist/Components/Nav/NavTab.svelte +46 -46
- package/dist/Components/Nav/ServiceMenuItem.svelte +62 -62
- package/dist/Components/Nav/ServicesNav.svelte +50 -50
- package/dist/Components/NumberInput.svelte +98 -98
- package/dist/Components/OptionCards.svelte +58 -58
- package/dist/Components/PageHeading.svelte +21 -21
- package/dist/Components/PasswordInput.svelte +61 -61
- package/dist/Components/PopoverButton.svelte +53 -53
- package/dist/Components/Portal.svelte +7 -7
- package/dist/Components/ProgressBubble.svelte +42 -42
- package/dist/Components/QuerySelect.svelte +102 -102
- package/dist/Components/ResourceCalendar/ResourceCalendar.svelte +241 -241
- package/dist/Components/SidebarPanel.svelte +32 -32
- package/dist/Components/SingleSelect.svelte +122 -122
- package/dist/Components/StepButton.svelte +48 -48
- package/dist/Components/StepForm.svelte +58 -58
- package/dist/Components/Switch.svelte +43 -43
- package/dist/Components/Table/ARCHITECTURE.md +298 -298
- package/dist/Components/Table/Body/Rows/ColumnHighlight.svelte +30 -30
- package/dist/Components/Table/Body/Rows/ColumnList.svelte +24 -24
- package/dist/Components/Table/Body/Rows/ColumnListRow.svelte +165 -165
- package/dist/Components/Table/Body/Rows/Columns/AccessorComponent.svelte +13 -13
- package/dist/Components/Table/Body/Rows/Columns/ActionsCell.svelte +120 -120
- package/dist/Components/Table/Body/Rows/Columns/BubbleCell.svelte +81 -81
- package/dist/Components/Table/Body/Rows/Columns/ExpandCell.svelte +92 -92
- package/dist/Components/Table/Body/Rows/Columns/FocusableImmutableCell.svelte +110 -110
- package/dist/Components/Table/Body/Rows/Columns/ProgressCell.svelte +54 -54
- package/dist/Components/Table/Body/Rows/Columns/TableAction.svelte +35 -35
- package/dist/Components/Table/Body/Rows/Columns/TableCheckbox.svelte +64 -64
- package/dist/Components/Table/Body/Rows/Columns/TableColumn.svelte +82 -82
- package/dist/Components/Table/Body/Rows/Columns/TableDatePicker.svelte +88 -88
- package/dist/Components/Table/Body/Rows/Columns/TableNumberInput.svelte +90 -90
- package/dist/Components/Table/Body/Rows/Columns/TableQuerySelect.svelte +96 -96
- package/dist/Components/Table/Body/Rows/Columns/TableRowSelectionCheckbox.svelte +71 -71
- package/dist/Components/Table/Body/Rows/Columns/TableSelect.svelte +124 -124
- package/dist/Components/Table/Body/Rows/Columns/TableTextInput.svelte +91 -91
- package/dist/Components/Table/Body/Rows/Columns/ValidityCell.svelte +88 -88
- package/dist/Components/Table/Body/Rows/TableRow.svelte +127 -127
- package/dist/Components/Table/Body/TableBody.svelte +92 -92
- package/dist/Components/Table/Headers/HighlightAllHeader.svelte +25 -25
- package/dist/Components/Table/Headers/SelectAllHeader.svelte +20 -20
- package/dist/Components/Table/Headers/TableHead.svelte +175 -175
- package/dist/Components/Table/Misc/ColumnPanel.svelte +32 -32
- package/dist/Components/Table/Misc/ColumnPanelModal.svelte +29 -29
- package/dist/Components/Table/Misc/FilterInput.svelte +326 -326
- package/dist/Components/Table/Misc/FilterPanel.svelte +79 -79
- package/dist/Components/Table/Misc/FilterPanelModal.svelte +21 -21
- package/dist/Components/Table/Misc/ShareViewModal.svelte +118 -118
- package/dist/Components/Table/Misc/SidePanel.svelte +24 -24
- package/dist/Components/Table/Misc/TableConfigurationModal.svelte +318 -318
- package/dist/Components/Table/Misc/TableFooter.svelte +201 -201
- package/dist/Components/Table/Misc/TableHorizontalBar.svelte +112 -112
- package/dist/Components/Table/Misc/TableSidebar.svelte +116 -116
- package/dist/Components/Table/Misc/TableViewDropdown.svelte +25 -25
- package/dist/Components/Table/Misc/ToolboxPanel.svelte +60 -60
- package/dist/Components/Table/Misc/ToolboxPanelModal.svelte +63 -63
- package/dist/Components/Table/Misc/UpdateViewOptionsModal.svelte +101 -101
- package/dist/Components/Table/NestedTable.svelte +29 -29
- package/dist/Components/Table/Table.svelte +140 -140
- package/dist/Components/Test/TestContextConsumer.svelte +8 -8
- package/dist/Components/TextArea.svelte +92 -90
- package/dist/Components/TextInput.svelte +128 -117
- package/dist/Components/ThreeStateRadio.svelte +101 -101
- package/dist/Components/Touch/BoolCard.svelte +27 -27
- package/dist/Components/Touch/DateModal.svelte +98 -98
- package/dist/Components/Touch/EditCard.svelte +44 -44
- package/dist/Components/Touch/InfoCard.svelte +25 -25
- package/dist/Components/Touch/InfoTextCard.svelte +22 -22
- package/dist/Components/Touch/ModalExplanation.svelte +13 -13
- package/dist/Components/Touch/NumberKeyboard.svelte +81 -81
- package/dist/Components/Touch/NumberModal.svelte +49 -49
- package/dist/Components/Touch/PlusMinusCard.svelte +40 -40
- package/dist/Components/Touch/SelectModal.svelte +86 -86
- package/dist/Components/Touch/SummaryCard.svelte +74 -74
- package/dist/Components/Touch/TextAreaModal.svelte +53 -53
- package/dist/Components/Touch/TextModal.svelte +56 -56
- package/dist/Components/Touch/TouchCard.svelte +28 -28
- package/dist/Components/Touch/TouchModal.svelte +141 -141
- package/dist/Components/Touch/UtilityButton.svelte +63 -63
- package/dist/Components/VerticalTabGroup.svelte +38 -38
- package/dist/Components/VerticalTabGroupButton.svelte +46 -46
- package/dist/Helpers/Datamatrix.d.ts +19 -19
- package/dist/Helpers/Datamatrix.js +291 -291
- package/dist/Types/Examples/ExampleModal.svelte +27 -27
- package/dist/paraglide/README.md +1 -1
- package/dist/scss/_basics.scss +12 -12
- package/dist/scss/_colours.scss +140 -140
- package/dist/scss/_motion.scss +57 -57
- package/dist/scss/_sizing.scss +85 -85
- package/dist/scss/_transitions.scss +8 -8
- package/package.json +1 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
<svelte:options runes />
|
|
2
|
-
|
|
3
|
-
<script
|
|
4
|
-
lang="ts"
|
|
5
|
-
generics="T extends object, IdType extends Primitive"
|
|
1
|
+
<svelte:options runes />
|
|
2
|
+
|
|
3
|
+
<script
|
|
4
|
+
lang="ts"
|
|
5
|
+
generics="T extends object, IdType extends Primitive"
|
|
6
6
|
>import Button from "../../Button.svelte";
|
|
7
7
|
import DesktopModal from "../../DesktopModal.svelte";
|
|
8
8
|
import HorizontalTabGroup from "../../HorizontalTabGroup.svelte";
|
|
@@ -284,318 +284,318 @@ const sectionTabs = [{
|
|
|
284
284
|
}
|
|
285
285
|
}];
|
|
286
286
|
const selectedSectionTab = $derived(sectionTabs.find((t) => t.args.kind === section) ?? sectionTabs[0]);
|
|
287
|
-
</script>
|
|
288
|
-
|
|
289
|
-
<!-- Left-nav section pane: the top horizontal tabs for the active kind. -->
|
|
290
|
-
{#snippet sectionPane(_args: { kind: DatatableViewKind })}
|
|
291
|
-
<div class="pane">
|
|
292
|
-
<VerticalTabGroup
|
|
293
|
-
tabs={innerTabs}
|
|
294
|
-
selected={selectedInnerTab}
|
|
295
|
-
nohistory
|
|
296
|
-
/>
|
|
297
|
-
</div>
|
|
298
|
-
{/snippet}
|
|
299
|
-
|
|
300
|
-
<!-- Body of a single top tab (My / Shared / Save). -->
|
|
301
|
-
{#snippet tabBody(args: { tab: ConfigTab })}
|
|
302
|
-
<div class="tab-body">
|
|
303
|
-
{#if errorMessage}
|
|
304
|
-
<div
|
|
305
|
-
class="error-banner"
|
|
306
|
-
role="alert"
|
|
307
|
-
>
|
|
308
|
-
<Icon
|
|
309
|
-
name="circle-exclamation"
|
|
310
|
-
size={Size.Small}
|
|
311
|
-
/>
|
|
312
|
-
<span>{errorMessage}</span>
|
|
313
|
-
</div>
|
|
314
|
-
{/if}
|
|
315
|
-
|
|
316
|
-
{#if args.tab === 'mine'}
|
|
317
|
-
{#if loadingViews}
|
|
318
|
-
<InfoAlert>{pair(i18n['scoria.message.loading_layouts'], i18n['scoria.message.loading_filters'])}</InfoAlert>
|
|
319
|
-
{:else if myViews.length === 0}
|
|
320
|
-
<InfoAlert>
|
|
321
|
-
{pair(i18n['scoria.text.no_layouts_saved'], i18n['scoria.text.no_filters_saved'])}
|
|
322
|
-
</InfoAlert>
|
|
323
|
-
{:else}
|
|
324
|
-
<ul class="card-list">
|
|
325
|
-
{#each myViews as view (view.id)}
|
|
326
|
-
<li class="view-card">
|
|
327
|
-
<div class="view-card-head">
|
|
328
|
-
<span class="view-name">{view.name}</span>
|
|
329
|
-
<div class="view-actions">
|
|
330
|
-
<Button
|
|
331
|
-
variant={Variant.Secondary}
|
|
332
|
-
size={Size.MediumSmall}
|
|
333
|
-
nowrap
|
|
334
|
-
onclick={() => {
|
|
335
|
-
applyView(view);
|
|
336
|
-
}}>{i18n['scoria.action.apply']()}</Button
|
|
337
|
-
>
|
|
338
|
-
<!--
|
|
339
|
-
"Save Layout" / "Save Filter" overwrites THIS saved
|
|
340
|
-
view's stored body with the table's current live state.
|
|
341
|
-
Typical flow: user applies the view, tweaks the
|
|
342
|
-
columns or filter inputs, then clicks Save to make
|
|
343
|
-
the tweaks part of the saved view. Distinct from
|
|
344
|
-
"Save a new …" (which always creates a fresh record).
|
|
345
|
-
-->
|
|
346
|
-
<Button
|
|
347
|
-
variant={Variant.Secondary}
|
|
348
|
-
size={Size.MediumSmall}
|
|
349
|
-
nowrap
|
|
350
|
-
iconLeft={{ name: 'circle-tick', size: Size.Small }}
|
|
351
|
-
onclick={() => {
|
|
352
|
-
saveLiveStateInto(view);
|
|
353
|
-
}}>{pair(i18n['scoria.action.save_layout'], i18n['scoria.action.save_filter'])}</Button
|
|
354
|
-
>
|
|
355
|
-
<!--
|
|
356
|
-
"Update Options" opens a dedicated modal that edits
|
|
357
|
-
the view's name and sharing visibility (private /
|
|
358
|
-
public). Kept separate from the inline body-save
|
|
359
|
-
above so a quick name fix never accidentally
|
|
360
|
-
overwrites the saved layout / filter.
|
|
361
|
-
-->
|
|
362
|
-
<Button
|
|
363
|
-
variant={Variant.Secondary}
|
|
364
|
-
size={Size.MediumSmall}
|
|
365
|
-
nowrap
|
|
366
|
-
iconLeft={{ name: 'edit', size: Size.Small }}
|
|
367
|
-
onclick={() => {
|
|
368
|
-
updatingOptionsView = view;
|
|
369
|
-
}}>{i18n['scoria.action.update_options']()}</Button
|
|
370
|
-
>
|
|
371
|
-
<Button
|
|
372
|
-
variant={Variant.Secondary}
|
|
373
|
-
size={Size.MediumSmall}
|
|
374
|
-
nowrap
|
|
375
|
-
iconLeft={{ name: 'external-link', size: Size.Small }}
|
|
376
|
-
onclick={() => {
|
|
377
|
-
// Open the share dialog rather than switching to
|
|
378
|
-
// the "Shared" tab - subscribing OTHER users to
|
|
379
|
-
// this view is a distinct flow from "Subscribe
|
|
380
|
-
// to Additional" (which is the current user
|
|
381
|
-
// joining a public view).
|
|
382
|
-
sharingView = view;
|
|
383
|
-
}}>{i18n['scoria.action.share']()}</Button
|
|
384
|
-
>
|
|
385
|
-
<Button
|
|
386
|
-
variant={Variant.Secondary}
|
|
387
|
-
size={Size.MediumSmall}
|
|
388
|
-
nowrap
|
|
389
|
-
iconLeft={{ name: 'circle-cross', size: Size.Small }}
|
|
390
|
-
onclick={() => {
|
|
391
|
-
deleteView(view);
|
|
392
|
-
}}>{i18n['scoria.action.delete']()}</Button
|
|
393
|
-
>
|
|
394
|
-
</div>
|
|
395
|
-
</div>
|
|
396
|
-
{#if view.subscribers && view.subscribers.length > 0}
|
|
397
|
-
<ul class="subscriber-list">
|
|
398
|
-
{#each view.subscribers as subscriber (subscriber.userId)}
|
|
399
|
-
<li class="subscriber">
|
|
400
|
-
<Icon
|
|
401
|
-
name="user-3"
|
|
402
|
-
size={Size.Small}
|
|
403
|
-
/>
|
|
404
|
-
<span>{subscriber.displayName ?? subscriber.userId}</span>
|
|
405
|
-
<Button
|
|
406
|
-
variant={Variant.Secondary}
|
|
407
|
-
size={Size.Tiny}
|
|
408
|
-
nowrap
|
|
409
|
-
onclick={() => {
|
|
410
|
-
removeSubscriber(view, subscriber.userId);
|
|
411
|
-
}}>{i18n['scoria.action.remove']()}</Button
|
|
412
|
-
>
|
|
413
|
-
</li>
|
|
414
|
-
{/each}
|
|
415
|
-
</ul>
|
|
416
|
-
{/if}
|
|
417
|
-
</li>
|
|
418
|
-
{/each}
|
|
419
|
-
</ul>
|
|
420
|
-
{/if}
|
|
421
|
-
{:else if args.tab === 'shared'}
|
|
422
|
-
<div class="shared-head">
|
|
423
|
-
<Button
|
|
424
|
-
variant={Variant.Secondary}
|
|
425
|
-
size={Size.MediumSmall}
|
|
426
|
-
nowrap
|
|
427
|
-
disabled={loadingPublicViews}
|
|
428
|
-
iconLeft={{ name: 'circle-plus', size: Size.Small }}
|
|
429
|
-
onclick={loadPublicViews}>{i18n['scoria.action.subscribe_additional']()}</Button
|
|
430
|
-
>
|
|
431
|
-
</div>
|
|
432
|
-
|
|
433
|
-
{#if publicViews !== null}
|
|
434
|
-
{#if publicViews.length === 0}
|
|
435
|
-
<InfoAlert>
|
|
436
|
-
{pair(i18n['scoria.text.no_further_layouts'], i18n['scoria.text.no_further_filters'])}
|
|
437
|
-
</InfoAlert>
|
|
438
|
-
{:else}
|
|
439
|
-
<ul class="card-list">
|
|
440
|
-
{#each publicViews as view (view.id)}
|
|
441
|
-
<li class="view-card">
|
|
442
|
-
<div class="view-card-head">
|
|
443
|
-
<span class="view-name">{view.name}</span>
|
|
444
|
-
<div class="view-actions">
|
|
445
|
-
<Button
|
|
446
|
-
variant={Variant.Secondary}
|
|
447
|
-
size={Size.MediumSmall}
|
|
448
|
-
nowrap
|
|
449
|
-
disabled={currentUserId === undefined}
|
|
450
|
-
onclick={() => {
|
|
451
|
-
subscribe(view);
|
|
452
|
-
}}>{i18n['scoria.action.subscribe']()}</Button
|
|
453
|
-
>
|
|
454
|
-
</div>
|
|
455
|
-
</div>
|
|
456
|
-
</li>
|
|
457
|
-
{/each}
|
|
458
|
-
</ul>
|
|
459
|
-
{/if}
|
|
460
|
-
{/if}
|
|
461
|
-
|
|
462
|
-
{#if sharedViews.length === 0}
|
|
463
|
-
<InfoAlert>
|
|
464
|
-
{pair(i18n['scoria.text.no_layouts_shared'], i18n['scoria.text.no_filters_shared'])}
|
|
465
|
-
</InfoAlert>
|
|
466
|
-
{:else}
|
|
467
|
-
<ul class="card-list">
|
|
468
|
-
{#each sharedViews as view (view.id)}
|
|
469
|
-
<li class="view-card">
|
|
470
|
-
<div class="view-card-head">
|
|
471
|
-
<span class="view-name">{view.name}</span>
|
|
472
|
-
<div class="view-actions">
|
|
473
|
-
<Button
|
|
474
|
-
variant={Variant.Secondary}
|
|
475
|
-
size={Size.MediumSmall}
|
|
476
|
-
nowrap
|
|
477
|
-
onclick={() => {
|
|
478
|
-
applyView(view);
|
|
479
|
-
}}>{i18n['scoria.action.apply']()}</Button
|
|
480
|
-
>
|
|
481
|
-
<Button
|
|
482
|
-
variant={Variant.Secondary}
|
|
483
|
-
size={Size.MediumSmall}
|
|
484
|
-
nowrap
|
|
485
|
-
disabled={currentUserId === undefined}
|
|
486
|
-
onclick={() => {
|
|
487
|
-
unsubscribe(view);
|
|
488
|
-
}}>{i18n['scoria.action.unsubscribe']()}</Button
|
|
489
|
-
>
|
|
490
|
-
</div>
|
|
491
|
-
</div>
|
|
492
|
-
</li>
|
|
493
|
-
{/each}
|
|
494
|
-
</ul>
|
|
495
|
-
{/if}
|
|
496
|
-
{:else}
|
|
497
|
-
<div class="save-tab">
|
|
498
|
-
<InfoAlert>
|
|
499
|
-
{pair(i18n['scoria.text.save_layout_intro'], i18n['scoria.text.save_filter_intro'])}
|
|
500
|
-
</InfoAlert>
|
|
501
|
-
<!-- The InfoAlert above spans the full tab body so the
|
|
502
|
-
empty-state visuals are consistent across all three tabs.
|
|
503
|
-
The name input + Save button below stay grouped in a
|
|
504
|
-
narrower form column (`.save-tab-form`) so they keep
|
|
505
|
-
comfortable form proportions instead of stretching across
|
|
506
|
-
the whole 50vw modal. -->
|
|
507
|
-
<div class="save-tab-form">
|
|
508
|
-
<TextInput
|
|
509
|
-
placeholder={pair(i18n['scoria.label.layout_name_placeholder'], i18n['scoria.label.filter_name_placeholder'])}
|
|
510
|
-
bind:value={newViewName}
|
|
511
|
-
onkeydown={(e: KeyboardEvent) => {
|
|
512
|
-
if (e.key === 'Enter') {
|
|
513
|
-
e.preventDefault();
|
|
514
|
-
saveNewView();
|
|
515
|
-
}
|
|
516
|
-
}}
|
|
517
|
-
/>
|
|
518
|
-
<!-- Wrap the button in a row so the flex column's default
|
|
519
|
-
`align-items: stretch` does not blow it up to the input's
|
|
520
|
-
width. The row itself can stretch; the button inside sits
|
|
521
|
-
at its natural content width like every other Button. -->
|
|
522
|
-
<div class="save-tab-actions">
|
|
523
|
-
<Button
|
|
524
|
-
disabled={!newViewName.trim()}
|
|
525
|
-
onclick={saveNewView}>{i18n['scoria.action.save_current']()}</Button
|
|
526
|
-
>
|
|
527
|
-
</div>
|
|
528
|
-
</div>
|
|
529
|
-
</div>
|
|
530
|
-
{/if}
|
|
531
|
-
</div>
|
|
532
|
-
{/snippet}
|
|
533
|
-
|
|
534
|
-
<DesktopModal
|
|
535
|
-
headerLabel={i18n['scoria.action.table_configuration']()}
|
|
536
|
-
headerSubtitle={tableContext.datatableUuid
|
|
537
|
-
? i18n['scoria.label.table_identifier']({ id: tableContext.datatableUuid })
|
|
538
|
-
: undefined}
|
|
539
|
-
onclose={() => onclose?.()}
|
|
540
|
-
buttons={[{ label: i18n['scoria.action.close'](), callback: () => onclose?.() }]}
|
|
541
|
-
width={cssLength('50vw')}
|
|
542
|
-
minWidth={cssLength('40rem')}
|
|
543
|
-
>
|
|
544
|
-
{#if !remoteAvailable}
|
|
545
|
-
<div class="empty-state">
|
|
546
|
-
<Icon
|
|
547
|
-
name="circle-information"
|
|
548
|
-
size={Size.Large}
|
|
549
|
-
/>
|
|
550
|
-
<p>{i18n['scoria.text.views_unavailable']()}</p>
|
|
551
|
-
</div>
|
|
552
|
-
{:else}
|
|
553
|
-
<div class="config">
|
|
554
|
-
<HorizontalTabGroup
|
|
555
|
-
tabs={sectionTabs}
|
|
556
|
-
selected={selectedSectionTab}
|
|
557
|
-
grow
|
|
558
|
-
/>
|
|
559
|
-
</div>
|
|
560
|
-
{/if}
|
|
561
|
-
</DesktopModal>
|
|
562
|
-
|
|
563
|
-
<!--
|
|
564
|
-
Share dialog stacked on top of the Configuration modal. Rendered as a
|
|
565
|
-
sibling (not nested in the modal body) so the second modal's overlay
|
|
566
|
-
covers the first - matching how every other layered modal in scoria
|
|
567
|
-
stacks. After a successful Save we refresh the saved-views list so the
|
|
568
|
-
owner's card's subscriber strip updates immediately, then close.
|
|
569
|
-
-->
|
|
570
|
-
{#if sharingView}
|
|
571
|
-
<ShareViewModal
|
|
572
|
-
view={sharingView}
|
|
573
|
-
onclose={() => {
|
|
574
|
-
sharingView = null;
|
|
575
|
-
}}
|
|
576
|
-
onsaved={async () => {
|
|
577
|
-
await tableContext.refreshSavedViews();
|
|
578
|
-
}}
|
|
579
|
-
/>
|
|
580
|
-
{/if}
|
|
581
|
-
|
|
582
|
-
<!--
|
|
583
|
-
Update-Options dialog stacked the same way as the Share dialog.
|
|
584
|
-
After a successful Save we refresh the saved views so the My-* card
|
|
585
|
-
picks up the new name / visibility immediately.
|
|
586
|
-
-->
|
|
587
|
-
{#if updatingOptionsView}
|
|
588
|
-
<UpdateViewOptionsModal
|
|
589
|
-
view={updatingOptionsView}
|
|
590
|
-
onclose={() => {
|
|
591
|
-
updatingOptionsView = null;
|
|
592
|
-
}}
|
|
593
|
-
onsaved={async () => {
|
|
594
|
-
await tableContext.refreshSavedViews();
|
|
595
|
-
}}
|
|
596
|
-
/>
|
|
597
|
-
{/if}
|
|
598
|
-
|
|
287
|
+
</script>
|
|
288
|
+
|
|
289
|
+
<!-- Left-nav section pane: the top horizontal tabs for the active kind. -->
|
|
290
|
+
{#snippet sectionPane(_args: { kind: DatatableViewKind })}
|
|
291
|
+
<div class="pane">
|
|
292
|
+
<VerticalTabGroup
|
|
293
|
+
tabs={innerTabs}
|
|
294
|
+
selected={selectedInnerTab}
|
|
295
|
+
nohistory
|
|
296
|
+
/>
|
|
297
|
+
</div>
|
|
298
|
+
{/snippet}
|
|
299
|
+
|
|
300
|
+
<!-- Body of a single top tab (My / Shared / Save). -->
|
|
301
|
+
{#snippet tabBody(args: { tab: ConfigTab })}
|
|
302
|
+
<div class="tab-body">
|
|
303
|
+
{#if errorMessage}
|
|
304
|
+
<div
|
|
305
|
+
class="error-banner"
|
|
306
|
+
role="alert"
|
|
307
|
+
>
|
|
308
|
+
<Icon
|
|
309
|
+
name="circle-exclamation"
|
|
310
|
+
size={Size.Small}
|
|
311
|
+
/>
|
|
312
|
+
<span>{errorMessage}</span>
|
|
313
|
+
</div>
|
|
314
|
+
{/if}
|
|
315
|
+
|
|
316
|
+
{#if args.tab === 'mine'}
|
|
317
|
+
{#if loadingViews}
|
|
318
|
+
<InfoAlert>{pair(i18n['scoria.message.loading_layouts'], i18n['scoria.message.loading_filters'])}</InfoAlert>
|
|
319
|
+
{:else if myViews.length === 0}
|
|
320
|
+
<InfoAlert>
|
|
321
|
+
{pair(i18n['scoria.text.no_layouts_saved'], i18n['scoria.text.no_filters_saved'])}
|
|
322
|
+
</InfoAlert>
|
|
323
|
+
{:else}
|
|
324
|
+
<ul class="card-list">
|
|
325
|
+
{#each myViews as view (view.id)}
|
|
326
|
+
<li class="view-card">
|
|
327
|
+
<div class="view-card-head">
|
|
328
|
+
<span class="view-name">{view.name}</span>
|
|
329
|
+
<div class="view-actions">
|
|
330
|
+
<Button
|
|
331
|
+
variant={Variant.Secondary}
|
|
332
|
+
size={Size.MediumSmall}
|
|
333
|
+
nowrap
|
|
334
|
+
onclick={() => {
|
|
335
|
+
applyView(view);
|
|
336
|
+
}}>{i18n['scoria.action.apply']()}</Button
|
|
337
|
+
>
|
|
338
|
+
<!--
|
|
339
|
+
"Save Layout" / "Save Filter" overwrites THIS saved
|
|
340
|
+
view's stored body with the table's current live state.
|
|
341
|
+
Typical flow: user applies the view, tweaks the
|
|
342
|
+
columns or filter inputs, then clicks Save to make
|
|
343
|
+
the tweaks part of the saved view. Distinct from
|
|
344
|
+
"Save a new …" (which always creates a fresh record).
|
|
345
|
+
-->
|
|
346
|
+
<Button
|
|
347
|
+
variant={Variant.Secondary}
|
|
348
|
+
size={Size.MediumSmall}
|
|
349
|
+
nowrap
|
|
350
|
+
iconLeft={{ name: 'circle-tick', size: Size.Small }}
|
|
351
|
+
onclick={() => {
|
|
352
|
+
saveLiveStateInto(view);
|
|
353
|
+
}}>{pair(i18n['scoria.action.save_layout'], i18n['scoria.action.save_filter'])}</Button
|
|
354
|
+
>
|
|
355
|
+
<!--
|
|
356
|
+
"Update Options" opens a dedicated modal that edits
|
|
357
|
+
the view's name and sharing visibility (private /
|
|
358
|
+
public). Kept separate from the inline body-save
|
|
359
|
+
above so a quick name fix never accidentally
|
|
360
|
+
overwrites the saved layout / filter.
|
|
361
|
+
-->
|
|
362
|
+
<Button
|
|
363
|
+
variant={Variant.Secondary}
|
|
364
|
+
size={Size.MediumSmall}
|
|
365
|
+
nowrap
|
|
366
|
+
iconLeft={{ name: 'edit', size: Size.Small }}
|
|
367
|
+
onclick={() => {
|
|
368
|
+
updatingOptionsView = view;
|
|
369
|
+
}}>{i18n['scoria.action.update_options']()}</Button
|
|
370
|
+
>
|
|
371
|
+
<Button
|
|
372
|
+
variant={Variant.Secondary}
|
|
373
|
+
size={Size.MediumSmall}
|
|
374
|
+
nowrap
|
|
375
|
+
iconLeft={{ name: 'external-link', size: Size.Small }}
|
|
376
|
+
onclick={() => {
|
|
377
|
+
// Open the share dialog rather than switching to
|
|
378
|
+
// the "Shared" tab - subscribing OTHER users to
|
|
379
|
+
// this view is a distinct flow from "Subscribe
|
|
380
|
+
// to Additional" (which is the current user
|
|
381
|
+
// joining a public view).
|
|
382
|
+
sharingView = view;
|
|
383
|
+
}}>{i18n['scoria.action.share']()}</Button
|
|
384
|
+
>
|
|
385
|
+
<Button
|
|
386
|
+
variant={Variant.Secondary}
|
|
387
|
+
size={Size.MediumSmall}
|
|
388
|
+
nowrap
|
|
389
|
+
iconLeft={{ name: 'circle-cross', size: Size.Small }}
|
|
390
|
+
onclick={() => {
|
|
391
|
+
deleteView(view);
|
|
392
|
+
}}>{i18n['scoria.action.delete']()}</Button
|
|
393
|
+
>
|
|
394
|
+
</div>
|
|
395
|
+
</div>
|
|
396
|
+
{#if view.subscribers && view.subscribers.length > 0}
|
|
397
|
+
<ul class="subscriber-list">
|
|
398
|
+
{#each view.subscribers as subscriber (subscriber.userId)}
|
|
399
|
+
<li class="subscriber">
|
|
400
|
+
<Icon
|
|
401
|
+
name="user-3"
|
|
402
|
+
size={Size.Small}
|
|
403
|
+
/>
|
|
404
|
+
<span>{subscriber.displayName ?? subscriber.userId}</span>
|
|
405
|
+
<Button
|
|
406
|
+
variant={Variant.Secondary}
|
|
407
|
+
size={Size.Tiny}
|
|
408
|
+
nowrap
|
|
409
|
+
onclick={() => {
|
|
410
|
+
removeSubscriber(view, subscriber.userId);
|
|
411
|
+
}}>{i18n['scoria.action.remove']()}</Button
|
|
412
|
+
>
|
|
413
|
+
</li>
|
|
414
|
+
{/each}
|
|
415
|
+
</ul>
|
|
416
|
+
{/if}
|
|
417
|
+
</li>
|
|
418
|
+
{/each}
|
|
419
|
+
</ul>
|
|
420
|
+
{/if}
|
|
421
|
+
{:else if args.tab === 'shared'}
|
|
422
|
+
<div class="shared-head">
|
|
423
|
+
<Button
|
|
424
|
+
variant={Variant.Secondary}
|
|
425
|
+
size={Size.MediumSmall}
|
|
426
|
+
nowrap
|
|
427
|
+
disabled={loadingPublicViews}
|
|
428
|
+
iconLeft={{ name: 'circle-plus', size: Size.Small }}
|
|
429
|
+
onclick={loadPublicViews}>{i18n['scoria.action.subscribe_additional']()}</Button
|
|
430
|
+
>
|
|
431
|
+
</div>
|
|
432
|
+
|
|
433
|
+
{#if publicViews !== null}
|
|
434
|
+
{#if publicViews.length === 0}
|
|
435
|
+
<InfoAlert>
|
|
436
|
+
{pair(i18n['scoria.text.no_further_layouts'], i18n['scoria.text.no_further_filters'])}
|
|
437
|
+
</InfoAlert>
|
|
438
|
+
{:else}
|
|
439
|
+
<ul class="card-list">
|
|
440
|
+
{#each publicViews as view (view.id)}
|
|
441
|
+
<li class="view-card">
|
|
442
|
+
<div class="view-card-head">
|
|
443
|
+
<span class="view-name">{view.name}</span>
|
|
444
|
+
<div class="view-actions">
|
|
445
|
+
<Button
|
|
446
|
+
variant={Variant.Secondary}
|
|
447
|
+
size={Size.MediumSmall}
|
|
448
|
+
nowrap
|
|
449
|
+
disabled={currentUserId === undefined}
|
|
450
|
+
onclick={() => {
|
|
451
|
+
subscribe(view);
|
|
452
|
+
}}>{i18n['scoria.action.subscribe']()}</Button
|
|
453
|
+
>
|
|
454
|
+
</div>
|
|
455
|
+
</div>
|
|
456
|
+
</li>
|
|
457
|
+
{/each}
|
|
458
|
+
</ul>
|
|
459
|
+
{/if}
|
|
460
|
+
{/if}
|
|
461
|
+
|
|
462
|
+
{#if sharedViews.length === 0}
|
|
463
|
+
<InfoAlert>
|
|
464
|
+
{pair(i18n['scoria.text.no_layouts_shared'], i18n['scoria.text.no_filters_shared'])}
|
|
465
|
+
</InfoAlert>
|
|
466
|
+
{:else}
|
|
467
|
+
<ul class="card-list">
|
|
468
|
+
{#each sharedViews as view (view.id)}
|
|
469
|
+
<li class="view-card">
|
|
470
|
+
<div class="view-card-head">
|
|
471
|
+
<span class="view-name">{view.name}</span>
|
|
472
|
+
<div class="view-actions">
|
|
473
|
+
<Button
|
|
474
|
+
variant={Variant.Secondary}
|
|
475
|
+
size={Size.MediumSmall}
|
|
476
|
+
nowrap
|
|
477
|
+
onclick={() => {
|
|
478
|
+
applyView(view);
|
|
479
|
+
}}>{i18n['scoria.action.apply']()}</Button
|
|
480
|
+
>
|
|
481
|
+
<Button
|
|
482
|
+
variant={Variant.Secondary}
|
|
483
|
+
size={Size.MediumSmall}
|
|
484
|
+
nowrap
|
|
485
|
+
disabled={currentUserId === undefined}
|
|
486
|
+
onclick={() => {
|
|
487
|
+
unsubscribe(view);
|
|
488
|
+
}}>{i18n['scoria.action.unsubscribe']()}</Button
|
|
489
|
+
>
|
|
490
|
+
</div>
|
|
491
|
+
</div>
|
|
492
|
+
</li>
|
|
493
|
+
{/each}
|
|
494
|
+
</ul>
|
|
495
|
+
{/if}
|
|
496
|
+
{:else}
|
|
497
|
+
<div class="save-tab">
|
|
498
|
+
<InfoAlert>
|
|
499
|
+
{pair(i18n['scoria.text.save_layout_intro'], i18n['scoria.text.save_filter_intro'])}
|
|
500
|
+
</InfoAlert>
|
|
501
|
+
<!-- The InfoAlert above spans the full tab body so the
|
|
502
|
+
empty-state visuals are consistent across all three tabs.
|
|
503
|
+
The name input + Save button below stay grouped in a
|
|
504
|
+
narrower form column (`.save-tab-form`) so they keep
|
|
505
|
+
comfortable form proportions instead of stretching across
|
|
506
|
+
the whole 50vw modal. -->
|
|
507
|
+
<div class="save-tab-form">
|
|
508
|
+
<TextInput
|
|
509
|
+
placeholder={pair(i18n['scoria.label.layout_name_placeholder'], i18n['scoria.label.filter_name_placeholder'])}
|
|
510
|
+
bind:value={newViewName}
|
|
511
|
+
onkeydown={(e: KeyboardEvent) => {
|
|
512
|
+
if (e.key === 'Enter') {
|
|
513
|
+
e.preventDefault();
|
|
514
|
+
saveNewView();
|
|
515
|
+
}
|
|
516
|
+
}}
|
|
517
|
+
/>
|
|
518
|
+
<!-- Wrap the button in a row so the flex column's default
|
|
519
|
+
`align-items: stretch` does not blow it up to the input's
|
|
520
|
+
width. The row itself can stretch; the button inside sits
|
|
521
|
+
at its natural content width like every other Button. -->
|
|
522
|
+
<div class="save-tab-actions">
|
|
523
|
+
<Button
|
|
524
|
+
disabled={!newViewName.trim()}
|
|
525
|
+
onclick={saveNewView}>{i18n['scoria.action.save_current']()}</Button
|
|
526
|
+
>
|
|
527
|
+
</div>
|
|
528
|
+
</div>
|
|
529
|
+
</div>
|
|
530
|
+
{/if}
|
|
531
|
+
</div>
|
|
532
|
+
{/snippet}
|
|
533
|
+
|
|
534
|
+
<DesktopModal
|
|
535
|
+
headerLabel={i18n['scoria.action.table_configuration']()}
|
|
536
|
+
headerSubtitle={tableContext.datatableUuid
|
|
537
|
+
? i18n['scoria.label.table_identifier']({ id: tableContext.datatableUuid })
|
|
538
|
+
: undefined}
|
|
539
|
+
onclose={() => onclose?.()}
|
|
540
|
+
buttons={[{ label: i18n['scoria.action.close'](), callback: () => onclose?.() }]}
|
|
541
|
+
width={cssLength('50vw')}
|
|
542
|
+
minWidth={cssLength('40rem')}
|
|
543
|
+
>
|
|
544
|
+
{#if !remoteAvailable}
|
|
545
|
+
<div class="empty-state">
|
|
546
|
+
<Icon
|
|
547
|
+
name="circle-information"
|
|
548
|
+
size={Size.Large}
|
|
549
|
+
/>
|
|
550
|
+
<p>{i18n['scoria.text.views_unavailable']()}</p>
|
|
551
|
+
</div>
|
|
552
|
+
{:else}
|
|
553
|
+
<div class="config">
|
|
554
|
+
<HorizontalTabGroup
|
|
555
|
+
tabs={sectionTabs}
|
|
556
|
+
selected={selectedSectionTab}
|
|
557
|
+
grow
|
|
558
|
+
/>
|
|
559
|
+
</div>
|
|
560
|
+
{/if}
|
|
561
|
+
</DesktopModal>
|
|
562
|
+
|
|
563
|
+
<!--
|
|
564
|
+
Share dialog stacked on top of the Configuration modal. Rendered as a
|
|
565
|
+
sibling (not nested in the modal body) so the second modal's overlay
|
|
566
|
+
covers the first - matching how every other layered modal in scoria
|
|
567
|
+
stacks. After a successful Save we refresh the saved-views list so the
|
|
568
|
+
owner's card's subscriber strip updates immediately, then close.
|
|
569
|
+
-->
|
|
570
|
+
{#if sharingView}
|
|
571
|
+
<ShareViewModal
|
|
572
|
+
view={sharingView}
|
|
573
|
+
onclose={() => {
|
|
574
|
+
sharingView = null;
|
|
575
|
+
}}
|
|
576
|
+
onsaved={async () => {
|
|
577
|
+
await tableContext.refreshSavedViews();
|
|
578
|
+
}}
|
|
579
|
+
/>
|
|
580
|
+
{/if}
|
|
581
|
+
|
|
582
|
+
<!--
|
|
583
|
+
Update-Options dialog stacked the same way as the Share dialog.
|
|
584
|
+
After a successful Save we refresh the saved views so the My-* card
|
|
585
|
+
picks up the new name / visibility immediately.
|
|
586
|
+
-->
|
|
587
|
+
{#if updatingOptionsView}
|
|
588
|
+
<UpdateViewOptionsModal
|
|
589
|
+
view={updatingOptionsView}
|
|
590
|
+
onclose={() => {
|
|
591
|
+
updatingOptionsView = null;
|
|
592
|
+
}}
|
|
593
|
+
onsaved={async () => {
|
|
594
|
+
await tableContext.refreshSavedViews();
|
|
595
|
+
}}
|
|
596
|
+
/>
|
|
597
|
+
{/if}
|
|
598
|
+
|
|
599
599
|
<style>.empty-state {
|
|
600
600
|
display: flex;
|
|
601
601
|
flex-flow: column nowrap;
|
|
@@ -754,4 +754,4 @@ const selectedSectionTab = $derived(sectionTabs.find((t) => t.args.kind === sect
|
|
|
754
754
|
display: flex;
|
|
755
755
|
flex-flow: row nowrap;
|
|
756
756
|
justify-content: flex-start;
|
|
757
|
-
}</style>
|
|
757
|
+
}</style>
|