@apliteni/apliteni-ui 0.77.0 → 0.78.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/guidelines/file-drop.md +87 -0
- package/package.json +1 -1
- package/react/README.md +28 -0
- package/react/dist/index.css +131 -0
- package/react/dist/index.d.ts +42 -1
- package/react/dist/index.js +281 -129
- package/src/index.css +1 -0
- package/src/inline.js +2 -0
- package/src/styles/file-drop.css +83 -0
- package/src/styles/tap-zone.css +9 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# File drop
|
|
2
|
+
|
|
3
|
+
## Spend one row at rest
|
|
4
|
+
|
|
5
|
+
<!-- rule: one-row -->
|
|
6
|
+
|
|
7
|
+
**Rule:** At rest, a file drop is one row: the button that opens the picker and the accepted types beside it.
|
|
8
|
+
|
|
9
|
+
**Why:** A tall box holds the page's best space open for a file that arrives once a month.
|
|
10
|
+
|
|
11
|
+
**Do:** Put an Upload button and “PDF or CSV, up to 10 MB” on one line above the list they feed.
|
|
12
|
+
|
|
13
|
+
**Don't:** Draw a tall dashed box with a glyph, an invitation and a button stacked inside it.
|
|
14
|
+
|
|
15
|
+
**Except:** A page whose whole purpose is receiving one file may use the field-sized picker, which carries a label of its own.
|
|
16
|
+
|
|
17
|
+
## Paint the target only while a file is over it
|
|
18
|
+
|
|
19
|
+
<!-- rule: on-drag -->
|
|
20
|
+
|
|
21
|
+
**Rule:** Show the drop target while a file is over the region that accepts it, and nowhere else.
|
|
22
|
+
|
|
23
|
+
**Why:** A target drawn before there is a file to catch covers content the reader still needs.
|
|
24
|
+
|
|
25
|
+
**Do:** Cover the panel that accepts the file the moment the file crosses it.
|
|
26
|
+
|
|
27
|
+
**Don't:** Cover the whole window when one panel accepts the file.
|
|
28
|
+
|
|
29
|
+
## Keep the file in the row it arrived in
|
|
30
|
+
|
|
31
|
+
<!-- rule: in-the-row -->
|
|
32
|
+
|
|
33
|
+
**Rule:** After the drop, the file's name, size and progress take the place the button was in, with remove on the name's line.
|
|
34
|
+
|
|
35
|
+
**Why:** A row that changes in place keeps the page still while the file uploads.
|
|
36
|
+
|
|
37
|
+
**Do:** Give the name a line with remove at its end, put the size under it, and run the progress track the full width below both.
|
|
38
|
+
|
|
39
|
+
**Don't:** Push the list down with a progress card, or open a dialog over the page.
|
|
40
|
+
|
|
41
|
+
## Fail in place
|
|
42
|
+
|
|
43
|
+
<!-- rule: failure -->
|
|
44
|
+
|
|
45
|
+
**Rule:** A refused file keeps its place, says what it did wrong under its name, and offers Retry beside Remove.
|
|
46
|
+
|
|
47
|
+
**Why:** A row that drops the file sends the reader back to choosing one. The reason is the one string here that has to be read in full, so it gets a line rather than a share of one.
|
|
48
|
+
|
|
49
|
+
**Do:** Keep the name on its line and say “Larger than 10 MB” under it, with Retry.
|
|
50
|
+
|
|
51
|
+
**Don't:** Replace the row with “Upload failed”, losing both the file and the retry.
|
|
52
|
+
|
|
53
|
+
## Offer a button, not only a drag
|
|
54
|
+
|
|
55
|
+
<!-- rule: button-path -->
|
|
56
|
+
|
|
57
|
+
**Rule:** Give the reader a button that opens the picker, and let dragging be the shortcut.
|
|
58
|
+
|
|
59
|
+
**Why:** A keyboard cannot drag a file.
|
|
60
|
+
|
|
61
|
+
**Do:** Make the Upload button the control, reachable by keyboard and showing the focus ring.
|
|
62
|
+
|
|
63
|
+
**Don't:** Write “Drag a file here” with nothing to press.
|
|
64
|
+
|
|
65
|
+
## State the limits once
|
|
66
|
+
|
|
67
|
+
<!-- rule: limits-once -->
|
|
68
|
+
|
|
69
|
+
**Rule:** Name the accepted types and the size limit once, beside the button, in the product's own words.
|
|
70
|
+
|
|
71
|
+
**Why:** Repeated limits teach the reader to skip them.
|
|
72
|
+
|
|
73
|
+
**Do:** Write “PDF or CSV, up to 10 MB” beside the button, and let a failure say what this file did wrong.
|
|
74
|
+
|
|
75
|
+
**Don't:** Repeat the limits under the button, in a tooltip and again in the error.
|
|
76
|
+
|
|
77
|
+
## Choose the row, the region or the dialog
|
|
78
|
+
|
|
79
|
+
<!-- rule: which-shape -->
|
|
80
|
+
|
|
81
|
+
**Rule:** Use a row where the file joins a list on the page, a region when one panel owns the file, and a dialog only when the upload needs answers of its own.
|
|
82
|
+
|
|
83
|
+
**Why:** A dialog that asks nothing is one more thing to dismiss. The rule picks between three containers, so a drawing would show one of them and not the choice.
|
|
84
|
+
|
|
85
|
+
**Do:** Take a statement into the row under the statements it joins, and a replacement logo into the panel that shows it.
|
|
86
|
+
|
|
87
|
+
**Don't:** Open a dialog for one file that the row behind it could have taken.
|
package/package.json
CHANGED
package/react/README.md
CHANGED
|
@@ -493,6 +493,34 @@ places a decorative kit glyph with `icon` — the same glyph names `Button` and
|
|
|
493
493
|
`onFileChange`. Use `accept` and `hint` to describe allowed file types and size.
|
|
494
494
|
Your application must validate files before upload.
|
|
495
495
|
|
|
496
|
+
## File drop
|
|
497
|
+
|
|
498
|
+
`FileDrop` is the compact drop: one row with the button that opens the picker and
|
|
499
|
+
the accepted types beside it, and a drop target painted only while a file is over
|
|
500
|
+
the region. Use it where a file joins a list on the page; use `FileField` where a
|
|
501
|
+
labelled, field-sized picker belongs. The guideline page states when to use a row,
|
|
502
|
+
a region or a dialog.
|
|
503
|
+
|
|
504
|
+
```tsx
|
|
505
|
+
<FileDrop note="PDF or CSV, up to 10 MB" accept=".pdf,.csv"
|
|
506
|
+
file={upload} onFile={send} onRemove={clear} onRetry={send} />
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
Your application owns the upload and supplies the `file` it is holding: the
|
|
510
|
+
name, the `size` already written for a reader, `progress` while it uploads, and
|
|
511
|
+
the `error` when it failed. A file with no `status` is uploading, so a file
|
|
512
|
+
handed over before the request starts never reads as uploaded. A status the row
|
|
513
|
+
draws shows a mark and a word; where you give `progress` the track says it
|
|
514
|
+
instead, and the word goes to the track's accessible name. The file reads as a
|
|
515
|
+
stack: the name on top with the actions at its end, the size — or the failure
|
|
516
|
+
message — under it, and the track the full width below. No tier takes a second
|
|
517
|
+
line. The name truncates its stem and keeps its extension, with the whole name in
|
|
518
|
+
a `title`, and nothing is hidden or moved at any width. `Remove` is the icon-only
|
|
519
|
+
`x`, named by `removeLabel`; `Retry` keeps its word. Both appear only when you
|
|
520
|
+
handle them. Children render above the row, inside the region the target covers;
|
|
521
|
+
pass `dragging` to drive that target from a parent. `accept` filters the system
|
|
522
|
+
picker only — validate type and size yourself.
|
|
523
|
+
|
|
496
524
|
Use `Field` when an existing labelled control needs the kit frame. Spread its
|
|
497
525
|
render-prop attributes onto one labelable control, and apply the existing control
|
|
498
526
|
class and invalid styling as appropriate:
|
package/react/dist/index.css
CHANGED
|
@@ -82,6 +82,14 @@
|
|
|
82
82
|
.ui-table__act .ui-btn + .ui-btn {
|
|
83
83
|
margin-inline-start: var(--tap-gap) !important;
|
|
84
84
|
}
|
|
85
|
+
.ui-drop__row {
|
|
86
|
+
--tap-clear-y: var(--tap-gap);
|
|
87
|
+
}
|
|
88
|
+
.ui-drop__actions {
|
|
89
|
+
gap: var(--tap-gap) !important;
|
|
90
|
+
--tap-clear-x: var(--tap-gap);
|
|
91
|
+
--tap-clear-y: var(--tap-gap);
|
|
92
|
+
}
|
|
85
93
|
.ui-table__act {
|
|
86
94
|
--tap-clear-x: var(--tap-gap);
|
|
87
95
|
--tap-clear-y: 0px;
|
|
@@ -498,6 +506,129 @@
|
|
|
498
506
|
cursor: not-allowed;
|
|
499
507
|
}
|
|
500
508
|
|
|
509
|
+
/* ../src/styles/file-drop.css */
|
|
510
|
+
.ui-drop {
|
|
511
|
+
position: relative;
|
|
512
|
+
}
|
|
513
|
+
.ui-drop > * + .ui-drop__row {
|
|
514
|
+
margin-top: var(--space-3);
|
|
515
|
+
}
|
|
516
|
+
.ui-drop__row {
|
|
517
|
+
display: flex;
|
|
518
|
+
flex-wrap: wrap;
|
|
519
|
+
align-items: center;
|
|
520
|
+
min-height: var(--space-8);
|
|
521
|
+
gap: var(--space-2) var(--space-3);
|
|
522
|
+
font-size: var(--text-sm);
|
|
523
|
+
color: var(--text);
|
|
524
|
+
overflow-wrap: anywhere;
|
|
525
|
+
}
|
|
526
|
+
.ui-drop__note {
|
|
527
|
+
margin: 0;
|
|
528
|
+
}
|
|
529
|
+
.ui-drop__file {
|
|
530
|
+
display: grid;
|
|
531
|
+
flex: 1 1 auto;
|
|
532
|
+
gap: var(--space-2);
|
|
533
|
+
min-width: 0;
|
|
534
|
+
}
|
|
535
|
+
.ui-drop__line {
|
|
536
|
+
display: flex;
|
|
537
|
+
flex-wrap: nowrap;
|
|
538
|
+
align-items: center;
|
|
539
|
+
gap: var(--space-3);
|
|
540
|
+
min-width: 0;
|
|
541
|
+
}
|
|
542
|
+
.ui-drop__name {
|
|
543
|
+
display: flex;
|
|
544
|
+
flex: 1 1 auto;
|
|
545
|
+
min-width: var(--space-10);
|
|
546
|
+
color: var(--strong);
|
|
547
|
+
font-weight: var(--weight-medium);
|
|
548
|
+
}
|
|
549
|
+
.ui-drop__stem {
|
|
550
|
+
overflow: hidden;
|
|
551
|
+
text-overflow: ellipsis;
|
|
552
|
+
white-space: nowrap;
|
|
553
|
+
}
|
|
554
|
+
.ui-drop__ext {
|
|
555
|
+
flex: none;
|
|
556
|
+
}
|
|
557
|
+
.ui-drop__facts {
|
|
558
|
+
display: flex;
|
|
559
|
+
flex-wrap: nowrap;
|
|
560
|
+
align-items: center;
|
|
561
|
+
gap: var(--space-3);
|
|
562
|
+
min-width: 0;
|
|
563
|
+
}
|
|
564
|
+
.ui-drop__size {
|
|
565
|
+
flex: none;
|
|
566
|
+
white-space: nowrap;
|
|
567
|
+
}
|
|
568
|
+
.ui-drop__word {
|
|
569
|
+
overflow: hidden;
|
|
570
|
+
text-overflow: ellipsis;
|
|
571
|
+
white-space: nowrap;
|
|
572
|
+
flex: 0 1 auto;
|
|
573
|
+
min-width: 0;
|
|
574
|
+
}
|
|
575
|
+
.ui-drop__state {
|
|
576
|
+
display: inline-flex;
|
|
577
|
+
flex: 0 1 auto;
|
|
578
|
+
align-items: center;
|
|
579
|
+
gap: var(--space-2);
|
|
580
|
+
min-width: 0;
|
|
581
|
+
}
|
|
582
|
+
.ui-drop__state svg {
|
|
583
|
+
width: 16px;
|
|
584
|
+
height: 16px;
|
|
585
|
+
flex: none;
|
|
586
|
+
stroke-width: 2.4;
|
|
587
|
+
}
|
|
588
|
+
.ui-drop__error {
|
|
589
|
+
color: var(--pink);
|
|
590
|
+
}
|
|
591
|
+
.ui-drop__actions {
|
|
592
|
+
display: flex;
|
|
593
|
+
flex: none;
|
|
594
|
+
align-items: center;
|
|
595
|
+
gap: var(--space-2);
|
|
596
|
+
margin-left: auto;
|
|
597
|
+
}
|
|
598
|
+
.ui-drop__bar {
|
|
599
|
+
display: block;
|
|
600
|
+
width: 100%;
|
|
601
|
+
height: 6px;
|
|
602
|
+
border-radius: var(--radius-pill);
|
|
603
|
+
background: var(--surface-2);
|
|
604
|
+
overflow: hidden;
|
|
605
|
+
}
|
|
606
|
+
.ui-drop__bar span {
|
|
607
|
+
display: block;
|
|
608
|
+
height: 100%;
|
|
609
|
+
background: var(--accent);
|
|
610
|
+
}
|
|
611
|
+
.ui-drop__target {
|
|
612
|
+
position: absolute;
|
|
613
|
+
inset: 0;
|
|
614
|
+
display: flex;
|
|
615
|
+
align-items: center;
|
|
616
|
+
justify-content: center;
|
|
617
|
+
padding: var(--space-3);
|
|
618
|
+
border: 1px dashed var(--accent);
|
|
619
|
+
border-radius: var(--radius-md);
|
|
620
|
+
background: var(--surface);
|
|
621
|
+
color: var(--strong);
|
|
622
|
+
font-size: var(--text-sm);
|
|
623
|
+
text-align: center;
|
|
624
|
+
}
|
|
625
|
+
.ui-drop.is-disabled .ui-drop__row {
|
|
626
|
+
color: var(--disabled-ink);
|
|
627
|
+
}
|
|
628
|
+
.ui-drop.is-disabled .ui-drop__name {
|
|
629
|
+
color: var(--disabled-ink);
|
|
630
|
+
}
|
|
631
|
+
|
|
501
632
|
/* src/Toast.css */
|
|
502
633
|
.rx-toast-stack {
|
|
503
634
|
position: fixed;
|
package/react/dist/index.d.ts
CHANGED
|
@@ -607,6 +607,47 @@ type FileFieldProps = FieldMessage & Omit<InputHTMLAttributes<HTMLInputElement>,
|
|
|
607
607
|
};
|
|
608
608
|
declare function FileField({ label, hint, error, required, disabled, onFileChange, className, ...props }: FileFieldProps): react.JSX.Element;
|
|
609
609
|
|
|
610
|
+
type FileDropStatus = 'uploading' | 'done' | 'error';
|
|
611
|
+
/** The file the consumer is uploading. */
|
|
612
|
+
type FileDropFile = {
|
|
613
|
+
name: string;
|
|
614
|
+
/** Already written for a reader; the kit picks no unit. */
|
|
615
|
+
size?: string;
|
|
616
|
+
/** Left out, the file is uploading: the kit never reports a success nobody claimed. */
|
|
617
|
+
status?: FileDropStatus;
|
|
618
|
+
/** 0 to 100. Left out, the word stands without a bar. */
|
|
619
|
+
progress?: number;
|
|
620
|
+
/** What this file did wrong, when the status is an error. */
|
|
621
|
+
error?: string;
|
|
622
|
+
/** Replaces the word the status carries. */
|
|
623
|
+
state?: string;
|
|
624
|
+
};
|
|
625
|
+
type FileDropProps = {
|
|
626
|
+
/** The button that opens the picker. */
|
|
627
|
+
label?: string;
|
|
628
|
+
/** The accepted types and the size limit, stated once beside the button. */
|
|
629
|
+
note?: string;
|
|
630
|
+
/** Filters the system picker only; the consumer still validates the file. */
|
|
631
|
+
accept?: string;
|
|
632
|
+
/** The words on the drop target. */
|
|
633
|
+
dropLabel?: string;
|
|
634
|
+
/** Names the icon-only remove button; it carries no visible word. */
|
|
635
|
+
removeLabel?: string;
|
|
636
|
+
retryLabel?: string;
|
|
637
|
+
/** The file the consumer is holding. Left out, the row is at rest. */
|
|
638
|
+
file?: FileDropFile | null;
|
|
639
|
+
/** Drives the target from a parent that owns the drag region. */
|
|
640
|
+
dragging?: boolean;
|
|
641
|
+
disabled?: boolean;
|
|
642
|
+
onFile?: (file: File) => void;
|
|
643
|
+
onRemove?: () => void;
|
|
644
|
+
onRetry?: () => void;
|
|
645
|
+
/** What the region accepts the file for; the target covers it while dragging. */
|
|
646
|
+
children?: ReactNode;
|
|
647
|
+
className?: string;
|
|
648
|
+
};
|
|
649
|
+
declare function FileDrop({ label, note, accept, dropLabel, removeLabel, retryLabel, file, dragging, disabled, onFile, onRemove, onRetry, children, className, }: FileDropProps): react.JSX.Element;
|
|
650
|
+
|
|
610
651
|
type SearchFieldProps = Omit<InputHTMLAttributes<HTMLInputElement>, 'type' | 'aria-label'> & {
|
|
611
652
|
/** The control's name. A toolbar search box shows no label, so this is the
|
|
612
653
|
only name it has. */
|
|
@@ -827,4 +868,4 @@ declare const SidebarNav: react.ForwardRefExoticComponent<Omit<Omit<react.Detail
|
|
|
827
868
|
declare const PAGE_SIZES: readonly number[];
|
|
828
869
|
declare const DEFAULT_PAGE_SIZE: number;
|
|
829
870
|
|
|
830
|
-
export { AppShell, type AppShellProps, type AppShellSection, BackLink, type BackLinkOwnProps, type BackLinkProps, Badge, BusyRegion, type BusyRegionProps, Button, type ButtonLinkProps, type ButtonProps, Callout, type CalloutProps, type CalloutVariant, Card, Checkbox, type CheckboxProps, type Column, type CommandGroup, type CommandItem, CommandPalette, type CommandPaletteProps, Confirm, type ConfirmProps, DEFAULT_PAGE_SIZE, DataTable, type DataTableProps, DeltaValue, type DeltaValueProps, Denied, type DeniedProps, Drawer, type DrawerProps, DrawerSection, type DrawerSectionProps, Dropdown, type DropdownBadge, type DropdownEntry, type DropdownItem, type DropdownProps, type DropdownRowProps, type DropdownSearch, type DropdownSection, type DropdownSeparator, EmptyState, type EmptyStateProps, type EmptyStateVariant, Field, type FieldControlProps, type FieldProps, FileField, type FileFieldProps, type Filter, FilterBar, type FilterBarProps, Icon, KeyValueList, type KeyValueListProps, type KeyValueRow, Modal, type ModalProps, type NativeButtonProps, NumericValue, type NumericValueProps, PAGE_SIZES, Pagination, type PaginationProps, Pill, type PillProps, RowIdentity, type RowIdentityProps, SearchField, type SearchFieldProps, type SegmentOption, Segmented, type SegmentedProps, SelectField, type SelectFieldProps, SidebarNav, type SidebarNavBadge, type SidebarNavItem, type SidebarNavLeaf, type SidebarNavProps, type SidebarNavSection, Skeleton, type SkeletonProps, SkeletonTable, type SkeletonTableProps, Snippet, type SnippetProps, StatBand, type StatBandProps, type StatDelta, type StatFigure, type StatTone, type StatVariant, StatusDot, type StatusDotProps, Success, SuccessCheck, type SuccessCheckProps, type SuccessCountdown, SuccessPanel, type SuccessPanelProps, type SuccessProps, Switch, type SwitchProps, THEME_INIT_SCRIPT, type TabItem, type TableSort, Tabs, type TabsProps, TextArea, type TextAreaProps, TextField, type TextFieldProps, ThemeToggle, type ThemeToggleProps, Timeline, type TimelineEvent, type TimelineEventKind, type TimelineProps, Toast, type ToastNotice, type ToastProps, Tooltip, type TooltipProps, sortTableRows, useToast };
|
|
871
|
+
export { AppShell, type AppShellProps, type AppShellSection, BackLink, type BackLinkOwnProps, type BackLinkProps, Badge, BusyRegion, type BusyRegionProps, Button, type ButtonLinkProps, type ButtonProps, Callout, type CalloutProps, type CalloutVariant, Card, Checkbox, type CheckboxProps, type Column, type CommandGroup, type CommandItem, CommandPalette, type CommandPaletteProps, Confirm, type ConfirmProps, DEFAULT_PAGE_SIZE, DataTable, type DataTableProps, DeltaValue, type DeltaValueProps, Denied, type DeniedProps, Drawer, type DrawerProps, DrawerSection, type DrawerSectionProps, Dropdown, type DropdownBadge, type DropdownEntry, type DropdownItem, type DropdownProps, type DropdownRowProps, type DropdownSearch, type DropdownSection, type DropdownSeparator, EmptyState, type EmptyStateProps, type EmptyStateVariant, Field, type FieldControlProps, type FieldProps, FileDrop, type FileDropFile, type FileDropProps, type FileDropStatus, FileField, type FileFieldProps, type Filter, FilterBar, type FilterBarProps, Icon, KeyValueList, type KeyValueListProps, type KeyValueRow, Modal, type ModalProps, type NativeButtonProps, NumericValue, type NumericValueProps, PAGE_SIZES, Pagination, type PaginationProps, Pill, type PillProps, RowIdentity, type RowIdentityProps, SearchField, type SearchFieldProps, type SegmentOption, Segmented, type SegmentedProps, SelectField, type SelectFieldProps, SidebarNav, type SidebarNavBadge, type SidebarNavItem, type SidebarNavLeaf, type SidebarNavProps, type SidebarNavSection, Skeleton, type SkeletonProps, SkeletonTable, type SkeletonTableProps, Snippet, type SnippetProps, StatBand, type StatBandProps, type StatDelta, type StatFigure, type StatTone, type StatVariant, StatusDot, type StatusDotProps, Success, SuccessCheck, type SuccessCheckProps, type SuccessCountdown, SuccessPanel, type SuccessPanelProps, type SuccessProps, Switch, type SwitchProps, THEME_INIT_SCRIPT, type TabItem, type TableSort, Tabs, type TabsProps, TextArea, type TextAreaProps, TextField, type TextFieldProps, ThemeToggle, type ThemeToggleProps, Timeline, type TimelineEvent, type TimelineEventKind, type TimelineProps, Toast, type ToastNotice, type ToastProps, Tooltip, type TooltipProps, sortTableRows, useToast };
|
package/react/dist/index.js
CHANGED
|
@@ -2372,14 +2372,165 @@ function FileField({ label, hint, error, required, disabled, onFileChange, class
|
|
|
2372
2372
|
) });
|
|
2373
2373
|
}
|
|
2374
2374
|
|
|
2375
|
+
// src/FileDrop.tsx
|
|
2376
|
+
import { useRef as useRef16, useState as useState10 } from "react";
|
|
2377
|
+
import { Fragment as Fragment8, jsx as jsx29, jsxs as jsxs24 } from "react/jsx-runtime";
|
|
2378
|
+
var MARKS = {
|
|
2379
|
+
uploading: "clock",
|
|
2380
|
+
done: "circleCheck",
|
|
2381
|
+
error: "circleAlert"
|
|
2382
|
+
};
|
|
2383
|
+
var WORDS = {
|
|
2384
|
+
uploading: "Uploading",
|
|
2385
|
+
done: "Uploaded",
|
|
2386
|
+
error: "Upload failed"
|
|
2387
|
+
};
|
|
2388
|
+
var splitName = (name) => {
|
|
2389
|
+
const cut = name.lastIndexOf(".");
|
|
2390
|
+
return cut > 0 ? [name.slice(0, cut), name.slice(cut)] : [name, ""];
|
|
2391
|
+
};
|
|
2392
|
+
var carriesFiles = (event) => Array.from(event.dataTransfer.types).includes("Files");
|
|
2393
|
+
var cx6 = (...a) => a.filter(Boolean).join(" ");
|
|
2394
|
+
function FileDrop({
|
|
2395
|
+
label = "Upload",
|
|
2396
|
+
note,
|
|
2397
|
+
accept,
|
|
2398
|
+
dropLabel = "Drop to upload",
|
|
2399
|
+
removeLabel = "Remove file",
|
|
2400
|
+
retryLabel = "Retry",
|
|
2401
|
+
file,
|
|
2402
|
+
dragging,
|
|
2403
|
+
disabled,
|
|
2404
|
+
onFile,
|
|
2405
|
+
onRemove,
|
|
2406
|
+
onRetry,
|
|
2407
|
+
children,
|
|
2408
|
+
className
|
|
2409
|
+
}) {
|
|
2410
|
+
const input = useRef16(null);
|
|
2411
|
+
const [over, setOver] = useState10(false);
|
|
2412
|
+
const status = file?.status ?? "uploading";
|
|
2413
|
+
const word = status === "error" ? file?.error ?? WORDS.error : file?.state ?? WORDS[status];
|
|
2414
|
+
const [stem, ext] = splitName(file?.name ?? "");
|
|
2415
|
+
const hasBar = status === "uploading" && file?.progress !== void 0;
|
|
2416
|
+
const showWord = !hasBar;
|
|
2417
|
+
const showSize = Boolean(file?.size) && status !== "error";
|
|
2418
|
+
const showTarget = (dragging ?? over) && !disabled;
|
|
2419
|
+
const take = (chosen) => {
|
|
2420
|
+
if (chosen && !disabled) onFile?.(chosen);
|
|
2421
|
+
};
|
|
2422
|
+
return /* @__PURE__ */ jsxs24(
|
|
2423
|
+
"div",
|
|
2424
|
+
{
|
|
2425
|
+
className: cx6("ui-drop", disabled && "is-disabled", className),
|
|
2426
|
+
onDragOver: (event) => {
|
|
2427
|
+
if (disabled || !carriesFiles(event)) return;
|
|
2428
|
+
event.preventDefault();
|
|
2429
|
+
setOver(true);
|
|
2430
|
+
},
|
|
2431
|
+
onDragLeave: (event) => {
|
|
2432
|
+
if (!event.currentTarget.contains(event.relatedTarget)) setOver(false);
|
|
2433
|
+
},
|
|
2434
|
+
onDrop: (event) => {
|
|
2435
|
+
if (disabled || !carriesFiles(event)) return;
|
|
2436
|
+
event.preventDefault();
|
|
2437
|
+
setOver(false);
|
|
2438
|
+
take(event.dataTransfer.files[0]);
|
|
2439
|
+
},
|
|
2440
|
+
children: [
|
|
2441
|
+
children,
|
|
2442
|
+
/* @__PURE__ */ jsxs24("div", { className: "ui-drop__row", children: [
|
|
2443
|
+
file ? /* @__PURE__ */ jsxs24("div", { className: cx6("ui-drop__file", status === "error" && "ui-drop__file--failed"), children: [
|
|
2444
|
+
/* @__PURE__ */ jsxs24("div", { className: "ui-drop__line", children: [
|
|
2445
|
+
/* @__PURE__ */ jsxs24("span", { className: "ui-drop__name", title: file.name, children: [
|
|
2446
|
+
/* @__PURE__ */ jsx29("span", { className: "ui-drop__stem", children: stem }),
|
|
2447
|
+
/* @__PURE__ */ jsx29("span", { className: "ui-drop__ext", children: ext })
|
|
2448
|
+
] }),
|
|
2449
|
+
(onRemove || status === "error" && onRetry) && /* @__PURE__ */ jsxs24("span", { className: "ui-drop__actions", children: [
|
|
2450
|
+
status === "error" && onRetry && /* @__PURE__ */ jsx29(Button, { size: "sm", icon: "refresh", onClick: onRetry, disabled, children: retryLabel }),
|
|
2451
|
+
onRemove && /* @__PURE__ */ jsx29(
|
|
2452
|
+
Button,
|
|
2453
|
+
{
|
|
2454
|
+
variant: "ghost",
|
|
2455
|
+
size: "sm",
|
|
2456
|
+
icon: "x",
|
|
2457
|
+
iconOnly: true,
|
|
2458
|
+
"aria-label": removeLabel,
|
|
2459
|
+
onClick: onRemove,
|
|
2460
|
+
disabled
|
|
2461
|
+
}
|
|
2462
|
+
)
|
|
2463
|
+
] })
|
|
2464
|
+
] }),
|
|
2465
|
+
(showSize || showWord) && /* @__PURE__ */ jsxs24(
|
|
2466
|
+
"div",
|
|
2467
|
+
{
|
|
2468
|
+
className: cx6("ui-drop__facts", status === "error" && "ui-drop__error"),
|
|
2469
|
+
role: status === "error" ? "alert" : void 0,
|
|
2470
|
+
children: [
|
|
2471
|
+
showSize && /* @__PURE__ */ jsx29("span", { className: "ui-drop__size", children: file.size }),
|
|
2472
|
+
showWord && /* @__PURE__ */ jsxs24("span", { className: "ui-drop__state", children: [
|
|
2473
|
+
/* @__PURE__ */ jsx29(Icon, { name: MARKS[status] }),
|
|
2474
|
+
/* @__PURE__ */ jsx29("span", { className: "ui-drop__word", title: word, children: word })
|
|
2475
|
+
] })
|
|
2476
|
+
]
|
|
2477
|
+
}
|
|
2478
|
+
),
|
|
2479
|
+
hasBar && /* @__PURE__ */ jsx29(
|
|
2480
|
+
"span",
|
|
2481
|
+
{
|
|
2482
|
+
className: "ui-drop__bar",
|
|
2483
|
+
role: "progressbar",
|
|
2484
|
+
"aria-label": `${word} ${file.name}`,
|
|
2485
|
+
"aria-valuenow": file.progress,
|
|
2486
|
+
"aria-valuemin": 0,
|
|
2487
|
+
"aria-valuemax": 100,
|
|
2488
|
+
children: /* @__PURE__ */ jsx29("span", { style: { width: `${file.progress}%` } })
|
|
2489
|
+
}
|
|
2490
|
+
)
|
|
2491
|
+
] }) : /* @__PURE__ */ jsxs24(Fragment8, { children: [
|
|
2492
|
+
/* @__PURE__ */ jsx29(
|
|
2493
|
+
Button,
|
|
2494
|
+
{
|
|
2495
|
+
size: "sm",
|
|
2496
|
+
icon: "upload",
|
|
2497
|
+
disabled,
|
|
2498
|
+
onClick: () => input.current?.click(),
|
|
2499
|
+
children: label
|
|
2500
|
+
}
|
|
2501
|
+
),
|
|
2502
|
+
note && /* @__PURE__ */ jsx29("p", { className: "ui-drop__note", children: note })
|
|
2503
|
+
] }),
|
|
2504
|
+
/* @__PURE__ */ jsx29(
|
|
2505
|
+
"input",
|
|
2506
|
+
{
|
|
2507
|
+
ref: input,
|
|
2508
|
+
type: "file",
|
|
2509
|
+
accept,
|
|
2510
|
+
disabled,
|
|
2511
|
+
hidden: true,
|
|
2512
|
+
tabIndex: -1,
|
|
2513
|
+
onChange: (event) => {
|
|
2514
|
+
take(event.currentTarget.files?.[0]);
|
|
2515
|
+
event.currentTarget.value = "";
|
|
2516
|
+
}
|
|
2517
|
+
}
|
|
2518
|
+
)
|
|
2519
|
+
] }),
|
|
2520
|
+
showTarget && /* @__PURE__ */ jsx29("div", { className: "ui-drop__target", "aria-hidden": "true", children: dropLabel })
|
|
2521
|
+
]
|
|
2522
|
+
}
|
|
2523
|
+
);
|
|
2524
|
+
}
|
|
2525
|
+
|
|
2375
2526
|
// src/SearchField.tsx
|
|
2376
2527
|
import { forwardRef as forwardRef7 } from "react";
|
|
2377
2528
|
import { icon as icon5 } from "@apliteni/apliteni-ui";
|
|
2378
|
-
import { jsx as
|
|
2529
|
+
import { jsx as jsx30, jsxs as jsxs25 } from "react/jsx-runtime";
|
|
2379
2530
|
var SearchField = forwardRef7(function SearchField2({ ariaLabel, className, ...props }, ref) {
|
|
2380
|
-
return /* @__PURE__ */
|
|
2381
|
-
/* @__PURE__ */
|
|
2382
|
-
/* @__PURE__ */
|
|
2531
|
+
return /* @__PURE__ */ jsxs25("div", { className: "ui-input-group", children: [
|
|
2532
|
+
/* @__PURE__ */ jsx30("span", { className: "ui-input-group__icon", dangerouslySetInnerHTML: { __html: icon5("search") } }),
|
|
2533
|
+
/* @__PURE__ */ jsx30(
|
|
2383
2534
|
"input",
|
|
2384
2535
|
{
|
|
2385
2536
|
ref,
|
|
@@ -2393,9 +2544,9 @@ var SearchField = forwardRef7(function SearchField2({ ariaLabel, className, ...p
|
|
|
2393
2544
|
});
|
|
2394
2545
|
|
|
2395
2546
|
// src/Toast.tsx
|
|
2396
|
-
import { createContext as createContext2, useCallback as useCallback3, useContext as useContext2, useEffect as useEffect11, useLayoutEffect as useLayoutEffect4, useRef as
|
|
2547
|
+
import { createContext as createContext2, useCallback as useCallback3, useContext as useContext2, useEffect as useEffect11, useLayoutEffect as useLayoutEffect4, useRef as useRef17, useState as useState11 } from "react";
|
|
2397
2548
|
import { createPortal as createPortal4 } from "react-dom";
|
|
2398
|
-
import { jsx as
|
|
2549
|
+
import { jsx as jsx31, jsxs as jsxs26 } from "react/jsx-runtime";
|
|
2399
2550
|
var Context = createContext2(null);
|
|
2400
2551
|
var glyphs = { success: "circleCheck", danger: "circleX", warn: "circleAlert", info: "info", neutral: "bolt" };
|
|
2401
2552
|
function useToast() {
|
|
@@ -2404,12 +2555,12 @@ function useToast() {
|
|
|
2404
2555
|
return push;
|
|
2405
2556
|
}
|
|
2406
2557
|
function Notice({ notice, remove }) {
|
|
2407
|
-
const root =
|
|
2408
|
-
const [leaving2, setLeaving] =
|
|
2409
|
-
const dismissed =
|
|
2410
|
-
const remaining =
|
|
2411
|
-
const [hovered, setHovered] =
|
|
2412
|
-
const [focused, setFocused] =
|
|
2558
|
+
const root = useRef17(null);
|
|
2559
|
+
const [leaving2, setLeaving] = useState11(false);
|
|
2560
|
+
const dismissed = useRef17(false);
|
|
2561
|
+
const remaining = useRef17(5e3);
|
|
2562
|
+
const [hovered, setHovered] = useState11(false);
|
|
2563
|
+
const [focused, setFocused] = useState11(false);
|
|
2413
2564
|
const paused = hovered || focused;
|
|
2414
2565
|
const tone = notice.tone ?? "success";
|
|
2415
2566
|
const dismiss = useCallback3(() => {
|
|
@@ -2441,7 +2592,7 @@ function Notice({ notice, remove }) {
|
|
|
2441
2592
|
remaining.current = Math.max(0, remaining.current - (Date.now() - started));
|
|
2442
2593
|
};
|
|
2443
2594
|
}, [dismiss, leaving2, notice.action, paused]);
|
|
2444
|
-
return /* @__PURE__ */
|
|
2595
|
+
return /* @__PURE__ */ jsxs26(
|
|
2445
2596
|
"div",
|
|
2446
2597
|
{
|
|
2447
2598
|
ref: root,
|
|
@@ -2455,12 +2606,12 @@ function Notice({ notice, remove }) {
|
|
|
2455
2606
|
if (!event.currentTarget.contains(event.relatedTarget)) setFocused(false);
|
|
2456
2607
|
},
|
|
2457
2608
|
children: [
|
|
2458
|
-
/* @__PURE__ */
|
|
2459
|
-
/* @__PURE__ */
|
|
2460
|
-
/* @__PURE__ */
|
|
2461
|
-
notice.text && /* @__PURE__ */
|
|
2609
|
+
/* @__PURE__ */ jsx31("span", { className: "ui-toast__icon", children: /* @__PURE__ */ jsx31(Icon, { name: glyphs[tone] }) }),
|
|
2610
|
+
/* @__PURE__ */ jsxs26("div", { className: "ui-toast__body", children: [
|
|
2611
|
+
/* @__PURE__ */ jsx31("div", { className: "ui-toast__title", children: notice.title }),
|
|
2612
|
+
notice.text && /* @__PURE__ */ jsx31("div", { className: "ui-toast__text", children: notice.text })
|
|
2462
2613
|
] }),
|
|
2463
|
-
notice.action && /* @__PURE__ */
|
|
2614
|
+
notice.action && /* @__PURE__ */ jsx31(
|
|
2464
2615
|
"button",
|
|
2465
2616
|
{
|
|
2466
2617
|
type: "button",
|
|
@@ -2475,8 +2626,8 @@ function Notice({ notice, remove }) {
|
|
|
2475
2626
|
children: notice.action.label
|
|
2476
2627
|
}
|
|
2477
2628
|
),
|
|
2478
|
-
/* @__PURE__ */
|
|
2479
|
-
!notice.action && /* @__PURE__ */
|
|
2629
|
+
/* @__PURE__ */ jsx31("button", { type: "button", className: "ui-toast__close", "aria-label": "Dismiss", disabled: leaving2, onClick: dismiss, children: /* @__PURE__ */ jsx31(Icon, { name: "x" }) }),
|
|
2630
|
+
!notice.action && /* @__PURE__ */ jsx31(
|
|
2480
2631
|
"span",
|
|
2481
2632
|
{
|
|
2482
2633
|
className: "ui-toast__timer is-running",
|
|
@@ -2509,40 +2660,40 @@ function usePublishedReach(stack2, count) {
|
|
|
2509
2660
|
}, [stack2, count]);
|
|
2510
2661
|
}
|
|
2511
2662
|
function Toast({ children }) {
|
|
2512
|
-
const [entries, setEntries] =
|
|
2513
|
-
const nextId =
|
|
2514
|
-
const stack2 =
|
|
2663
|
+
const [entries, setEntries] = useState11([]);
|
|
2664
|
+
const nextId = useRef17(0);
|
|
2665
|
+
const stack2 = useRef17(null);
|
|
2515
2666
|
const push = useCallback3((notice) => {
|
|
2516
2667
|
const id = nextId.current++;
|
|
2517
2668
|
const remove = () => setEntries((current) => current.filter((entry) => entry.id !== id));
|
|
2518
2669
|
setEntries((current) => [...current, { id, notice, remove }]);
|
|
2519
2670
|
}, []);
|
|
2520
2671
|
usePublishedReach(stack2, entries.length);
|
|
2521
|
-
return /* @__PURE__ */
|
|
2672
|
+
return /* @__PURE__ */ jsxs26(Context.Provider, { value: push, children: [
|
|
2522
2673
|
children,
|
|
2523
2674
|
typeof document !== "undefined" && createPortal4(
|
|
2524
|
-
/* @__PURE__ */
|
|
2675
|
+
/* @__PURE__ */ jsx31("div", { className: "ui-toast-stack rx-toast-stack", ref: stack2, children: entries.map((entry) => /* @__PURE__ */ jsx31(Notice, { notice: entry.notice, remove: entry.remove }, entry.id)) }),
|
|
2525
2676
|
document.body
|
|
2526
2677
|
)
|
|
2527
2678
|
] });
|
|
2528
2679
|
}
|
|
2529
2680
|
|
|
2530
2681
|
// src/AppShell.tsx
|
|
2531
|
-
import { useEffect as useEffect13, useId as useId12, useState as
|
|
2682
|
+
import { useEffect as useEffect13, useId as useId12, useState as useState13 } from "react";
|
|
2532
2683
|
import { applyTheme, paletteHotkey, prism, railCollapsed, themeIcon, themeName } from "@apliteni/apliteni-ui";
|
|
2533
2684
|
|
|
2534
2685
|
// src/SidebarNav.tsx
|
|
2535
|
-
import { forwardRef as forwardRef8, useEffect as useEffect12, useId as useId11, useState as
|
|
2686
|
+
import { forwardRef as forwardRef8, useEffect as useEffect12, useId as useId11, useState as useState12 } from "react";
|
|
2536
2687
|
import { ENTRANCE_FALLBACK_MS } from "@apliteni/apliteni-ui/motion";
|
|
2537
|
-
import { Fragment as
|
|
2688
|
+
import { Fragment as Fragment9, jsx as jsx32, jsxs as jsxs27 } from "react/jsx-runtime";
|
|
2538
2689
|
var badgeText = (badge) => typeof badge === "object" ? badge.text : badge;
|
|
2539
2690
|
function Row({ item, active, activeIs, collapsed, renderLink, sub }) {
|
|
2540
2691
|
const uid = useId11();
|
|
2541
2692
|
const group = !sub && !!item.items?.length;
|
|
2542
2693
|
const childActive = group && item.items.some((child) => child.id === active);
|
|
2543
|
-
const [expanded, setExpanded] =
|
|
2694
|
+
const [expanded, setExpanded] = useState12();
|
|
2544
2695
|
const open = expanded ?? item.defaultOpen ?? childActive;
|
|
2545
|
-
const [entering, setEntering] =
|
|
2696
|
+
const [entering, setEntering] = useState12(false);
|
|
2546
2697
|
useEffect12(() => {
|
|
2547
2698
|
if (!entering) return;
|
|
2548
2699
|
const timer = setTimeout(() => setEntering(false), ENTRANCE_FALLBACK_MS);
|
|
@@ -2560,15 +2711,15 @@ function Row({ item, active, activeIs, collapsed, renderLink, sub }) {
|
|
|
2560
2711
|
item.disabled && "is-disabled",
|
|
2561
2712
|
item.danger && "is-danger"
|
|
2562
2713
|
].filter(Boolean).join(" ");
|
|
2563
|
-
const children = /* @__PURE__ */
|
|
2564
|
-
(item.leading ?? item.icon) && /* @__PURE__ */
|
|
2565
|
-
/* @__PURE__ */
|
|
2566
|
-
count != null && count !== "" && /* @__PURE__ */
|
|
2567
|
-
group && /* @__PURE__ */
|
|
2714
|
+
const children = /* @__PURE__ */ jsxs27(Fragment9, { children: [
|
|
2715
|
+
(item.leading ?? item.icon) && /* @__PURE__ */ jsx32("span", { className: "ui-nav__ic", children: item.leading ?? /* @__PURE__ */ jsx32(Icon, { name: item.icon }) }),
|
|
2716
|
+
/* @__PURE__ */ jsx32("span", { className: "ui-nav__label", children: item.label }),
|
|
2717
|
+
count != null && count !== "" && /* @__PURE__ */ jsx32("span", { className: `ui-nav__badge is-${typeof item.badge === "object" ? item.badge.tone ?? "neutral" : "neutral"}`, children: count }),
|
|
2718
|
+
group && /* @__PURE__ */ jsx32("span", { className: "ui-nav__caret", "aria-hidden": "true" })
|
|
2568
2719
|
] });
|
|
2569
2720
|
const common = { className, "aria-label": name, title: collapsed ? name : void 0, children };
|
|
2570
|
-
if (group) return /* @__PURE__ */
|
|
2571
|
-
/* @__PURE__ */
|
|
2721
|
+
if (group) return /* @__PURE__ */ jsxs27("li", { className: `ui-nav__group${open ? " is-open" : ""}`, children: [
|
|
2722
|
+
/* @__PURE__ */ jsx32(
|
|
2572
2723
|
"button",
|
|
2573
2724
|
{
|
|
2574
2725
|
...common,
|
|
@@ -2582,22 +2733,22 @@ function Row({ item, active, activeIs, collapsed, renderLink, sub }) {
|
|
|
2582
2733
|
}
|
|
2583
2734
|
}
|
|
2584
2735
|
),
|
|
2585
|
-
/* @__PURE__ */
|
|
2736
|
+
/* @__PURE__ */ jsx32("ul", { id: uid, className: `ui-nav__sub${entering ? " is-entering" : ""}`, hidden: !open, onAnimationEnd: () => setEntering(false), children: item.items.map((child) => /* @__PURE__ */ jsx32(Row, { item: child, active, activeIs, collapsed, renderLink, sub: true }, child.id)) })
|
|
2586
2737
|
] });
|
|
2587
|
-
if (item.disabled) return /* @__PURE__ */
|
|
2738
|
+
if (item.disabled) return /* @__PURE__ */ jsx32("li", { children: /* @__PURE__ */ jsx32("span", { ...common, role: "link", "aria-disabled": "true" }) });
|
|
2588
2739
|
const props = {
|
|
2589
2740
|
...common,
|
|
2590
2741
|
href: item.href ?? `#${item.id}`,
|
|
2591
2742
|
target: item.target,
|
|
2592
2743
|
"aria-current": isActive ? activeIs === "section" ? "true" : "page" : void 0
|
|
2593
2744
|
};
|
|
2594
|
-
return /* @__PURE__ */
|
|
2745
|
+
return /* @__PURE__ */ jsx32("li", { children: renderLink ? renderLink(item, props) : /* @__PURE__ */ jsx32("a", { ...props }) });
|
|
2595
2746
|
}
|
|
2596
2747
|
function Section({ section, ...props }) {
|
|
2597
2748
|
const uid = useId11();
|
|
2598
|
-
return /* @__PURE__ */
|
|
2599
|
-
section.label && /* @__PURE__ */
|
|
2600
|
-
/* @__PURE__ */
|
|
2749
|
+
return /* @__PURE__ */ jsxs27("div", { className: "ui-nav__section", children: [
|
|
2750
|
+
section.label && /* @__PURE__ */ jsx32("div", { className: "ui-nav__cap", id: uid, "aria-hidden": props.collapsed || void 0, children: section.label }),
|
|
2751
|
+
/* @__PURE__ */ jsx32("ul", { className: "ui-nav__list", "aria-labelledby": section.label ? uid : void 0, children: section.items.map((item) => /* @__PURE__ */ jsx32(Row, { item, ...props }, item.id)) })
|
|
2601
2752
|
] });
|
|
2602
2753
|
}
|
|
2603
2754
|
var SidebarNav = forwardRef8(function SidebarNav2({
|
|
@@ -2612,8 +2763,8 @@ var SidebarNav = forwardRef8(function SidebarNav2({
|
|
|
2612
2763
|
"aria-label": label = "Sidebar",
|
|
2613
2764
|
...rest
|
|
2614
2765
|
}, ref) {
|
|
2615
|
-
return /* @__PURE__ */
|
|
2616
|
-
(sections?.length ? sections : [{ items }]).map((section, index) => /* @__PURE__ */
|
|
2766
|
+
return /* @__PURE__ */ jsxs27("nav", { ...rest, ref, "aria-label": label, className: ["ui-nav", "ui-nav--side", collapsed && "is-collapsed", className].filter(Boolean).join(" "), children: [
|
|
2767
|
+
(sections?.length ? sections : [{ items }]).map((section, index) => /* @__PURE__ */ jsx32(
|
|
2617
2768
|
Section,
|
|
2618
2769
|
{
|
|
2619
2770
|
section,
|
|
@@ -2624,12 +2775,12 @@ var SidebarNav = forwardRef8(function SidebarNav2({
|
|
|
2624
2775
|
},
|
|
2625
2776
|
index
|
|
2626
2777
|
)),
|
|
2627
|
-
footer && /* @__PURE__ */
|
|
2778
|
+
footer && /* @__PURE__ */ jsx32("div", { className: "ui-nav__foot", children: footer })
|
|
2628
2779
|
] });
|
|
2629
2780
|
});
|
|
2630
2781
|
|
|
2631
2782
|
// src/AppShell.tsx
|
|
2632
|
-
import { Fragment as
|
|
2783
|
+
import { Fragment as Fragment10, jsx as jsx33, jsxs as jsxs28 } from "react/jsx-runtime";
|
|
2633
2784
|
var path = (value) => value.split(/[?#]/)[0].replace(/\/+$/, "") || "/";
|
|
2634
2785
|
function AppShell({
|
|
2635
2786
|
sections,
|
|
@@ -2651,10 +2802,10 @@ function AppShell({
|
|
|
2651
2802
|
renderLink
|
|
2652
2803
|
}) {
|
|
2653
2804
|
const uid = useId12();
|
|
2654
|
-
const [collapsed, setCollapsed] =
|
|
2655
|
-
const [search, setSearch] =
|
|
2656
|
-
const [more, setMore] =
|
|
2657
|
-
const [theme, setTheme] =
|
|
2805
|
+
const [collapsed, setCollapsed] = useState13(defaultCollapsed);
|
|
2806
|
+
const [search, setSearch] = useState13(false);
|
|
2807
|
+
const [more, setMore] = useState13(false);
|
|
2808
|
+
const [theme, setTheme] = useState13("dark");
|
|
2658
2809
|
const current = path(pathname);
|
|
2659
2810
|
const active = sections.filter((section) => current === path(section.href) || path(section.href) !== "/" && current.startsWith(`${path(section.href)}/`)).sort((a, b) => path(b.href).length - path(a.href).length)[0];
|
|
2660
2811
|
useEffect13(() => {
|
|
@@ -2702,15 +2853,15 @@ function AppShell({
|
|
|
2702
2853
|
onClick: (event) => {
|
|
2703
2854
|
if (!event.metaKey && !event.ctrlKey && !event.shiftKey && !event.altKey) setMore(false);
|
|
2704
2855
|
},
|
|
2705
|
-
children: /* @__PURE__ */
|
|
2706
|
-
/* @__PURE__ */
|
|
2707
|
-
/* @__PURE__ */
|
|
2708
|
-
section.count != null && /* @__PURE__ */
|
|
2856
|
+
children: /* @__PURE__ */ jsxs28(Fragment10, { children: [
|
|
2857
|
+
/* @__PURE__ */ jsx33("span", { className: "ui-nav__ic", children: /* @__PURE__ */ jsx33(Icon, { name: section.icon }) }),
|
|
2858
|
+
/* @__PURE__ */ jsx33("span", { className: "ui-nav__label", children: section.label }),
|
|
2859
|
+
section.count != null && /* @__PURE__ */ jsx33("span", { className: "ui-nav__badge is-neutral", children: section.count })
|
|
2709
2860
|
] })
|
|
2710
2861
|
};
|
|
2711
|
-
return /* @__PURE__ */
|
|
2862
|
+
return /* @__PURE__ */ jsx33("li", { children: renderLink ? renderLink(section, linkProps) : /* @__PURE__ */ jsx33("a", { ...linkProps }) }, section.href);
|
|
2712
2863
|
});
|
|
2713
|
-
const sidebar = (items, label) => /* @__PURE__ */
|
|
2864
|
+
const sidebar = (items, label) => /* @__PURE__ */ jsx33(
|
|
2714
2865
|
SidebarNav,
|
|
2715
2866
|
{
|
|
2716
2867
|
"aria-label": label,
|
|
@@ -2722,7 +2873,7 @@ function AppShell({
|
|
|
2722
2873
|
const linkProps = { ...props, onClick: (event) => {
|
|
2723
2874
|
if (!event.metaKey && !event.ctrlKey && !event.shiftKey && !event.altKey) setMore(false);
|
|
2724
2875
|
} };
|
|
2725
|
-
return renderLink ? renderLink(section, linkProps) : /* @__PURE__ */
|
|
2876
|
+
return renderLink ? renderLink(section, linkProps) : /* @__PURE__ */ jsx33("a", { ...linkProps });
|
|
2726
2877
|
}
|
|
2727
2878
|
}
|
|
2728
2879
|
);
|
|
@@ -2730,33 +2881,33 @@ function AppShell({
|
|
|
2730
2881
|
const visible = sections.length > 4 ? sections.slice(0, 3) : sections;
|
|
2731
2882
|
const remaining = sections.slice(visible.length);
|
|
2732
2883
|
const initials = account.name.trim().split(/\s+/).map((part) => part[0]).slice(0, 2).join("");
|
|
2733
|
-
return /* @__PURE__ */
|
|
2734
|
-
/* @__PURE__ */
|
|
2735
|
-
/* @__PURE__ */
|
|
2736
|
-
brand ?? /* @__PURE__ */
|
|
2737
|
-
/* @__PURE__ */
|
|
2884
|
+
return /* @__PURE__ */ jsxs28("div", { className: `ui-app ui-app--topbar ui-react-app${collapsed ? " is-collapsed" : ""}`, children: [
|
|
2885
|
+
/* @__PURE__ */ jsxs28("div", { className: "ui-app__rail", children: [
|
|
2886
|
+
/* @__PURE__ */ jsx33("div", { className: "ui-app__head", children: /* @__PURE__ */ jsxs28("a", { className: "ui-app__brand", href: brandHref, "aria-label": word, children: [
|
|
2887
|
+
brand ?? /* @__PURE__ */ jsx33("i", { "aria-hidden": "true", className: "ui-react-app__mark", dangerouslySetInnerHTML: { __html: prism(uid, 24) } }),
|
|
2888
|
+
/* @__PURE__ */ jsx33("span", { children: word })
|
|
2738
2889
|
] }) }),
|
|
2739
2890
|
sidebar(sections, "Sections"),
|
|
2740
|
-
/* @__PURE__ */
|
|
2741
|
-
/* @__PURE__ */
|
|
2742
|
-
/* @__PURE__ */
|
|
2743
|
-
/* @__PURE__ */
|
|
2744
|
-
/* @__PURE__ */
|
|
2891
|
+
/* @__PURE__ */ jsx33("div", { className: "ui-app__foot", children: /* @__PURE__ */ jsx33("div", { className: "ui-app__fold-row", children: /* @__PURE__ */ jsxs28("button", { type: "button", className: "ui-nav__item ui-app__fold", "aria-expanded": !collapsed, "aria-label": foldLabel, onClick: fold, children: [
|
|
2892
|
+
/* @__PURE__ */ jsx33("span", { className: "ui-nav__ic", children: /* @__PURE__ */ jsxs28("svg", { className: "ui-icon", viewBox: "0 0 24 24", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
|
|
2893
|
+
/* @__PURE__ */ jsx33("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2" }),
|
|
2894
|
+
/* @__PURE__ */ jsx33("path", { className: "ui-app__fold-seam", d: "M9 3v18" }),
|
|
2895
|
+
/* @__PURE__ */ jsx33("path", { className: "ui-app__fold-arrow", d: "m16 15-3-3 3-3" })
|
|
2745
2896
|
] }) }),
|
|
2746
|
-
/* @__PURE__ */
|
|
2897
|
+
/* @__PURE__ */ jsx33("span", { className: "ui-nav__label", children: foldLabel })
|
|
2747
2898
|
] }) }) })
|
|
2748
2899
|
] }),
|
|
2749
|
-
/* @__PURE__ */
|
|
2750
|
-
/* @__PURE__ */
|
|
2751
|
-
/* @__PURE__ */
|
|
2752
|
-
/* @__PURE__ */
|
|
2753
|
-
/* @__PURE__ */
|
|
2754
|
-
/* @__PURE__ */
|
|
2900
|
+
/* @__PURE__ */ jsxs28("div", { className: "ui-app__well", children: [
|
|
2901
|
+
/* @__PURE__ */ jsxs28("header", { className: "ui-app__bar", children: [
|
|
2902
|
+
/* @__PURE__ */ jsxs28("button", { type: "button", className: "ui-app__search", "aria-haspopup": "dialog", onClick: () => setSearch(true), children: [
|
|
2903
|
+
/* @__PURE__ */ jsx33("span", { className: "ui-app__search-ic", children: /* @__PURE__ */ jsx33(Icon, { name: "search" }) }),
|
|
2904
|
+
/* @__PURE__ */ jsx33("span", { className: "ui-app__search-txt", children: "Search or run a command\u2026" }),
|
|
2905
|
+
/* @__PURE__ */ jsx33("kbd", { className: "ui-cmdk__key", children: paletteHotkey() })
|
|
2755
2906
|
] }),
|
|
2756
|
-
/* @__PURE__ */
|
|
2907
|
+
/* @__PURE__ */ jsx33("span", { className: "ui-app__bar-gap" }),
|
|
2757
2908
|
bandControl,
|
|
2758
|
-
/* @__PURE__ */
|
|
2759
|
-
/* @__PURE__ */
|
|
2909
|
+
/* @__PURE__ */ jsx33("button", { type: "button", className: "toggle", "aria-label": themeName(theme), title: themeName(theme), onClick: () => applyTheme(theme === "light" ? "dark" : "light"), children: /* @__PURE__ */ jsx33("span", { className: "ic", "aria-hidden": "true", dangerouslySetInnerHTML: { __html: themeIcon(theme) } }) }),
|
|
2910
|
+
/* @__PURE__ */ jsx33("div", { className: "ui-app__user ui-app__user--bar", children: /* @__PURE__ */ jsx33(
|
|
2760
2911
|
Dropdown,
|
|
2761
2912
|
{
|
|
2762
2913
|
variant: "menu",
|
|
@@ -2765,27 +2916,27 @@ function AppShell({
|
|
|
2765
2916
|
ariaLabel: `Signed in as ${account.name}, ${account.email}`,
|
|
2766
2917
|
triggerClass: "ui-app__user-trigger",
|
|
2767
2918
|
panelClass: "ui-app__user-panel",
|
|
2768
|
-
triggerContent: /* @__PURE__ */
|
|
2769
|
-
header: /* @__PURE__ */
|
|
2770
|
-
/* @__PURE__ */
|
|
2771
|
-
/* @__PURE__ */
|
|
2919
|
+
triggerContent: /* @__PURE__ */ jsx33("span", { className: "ui-app__av", "aria-hidden": "true", children: initials }),
|
|
2920
|
+
header: /* @__PURE__ */ jsxs28("div", { className: "ui-dropdown__head", children: [
|
|
2921
|
+
/* @__PURE__ */ jsx33("b", { children: account.name }),
|
|
2922
|
+
/* @__PURE__ */ jsx33("span", { children: account.email })
|
|
2772
2923
|
] }),
|
|
2773
2924
|
items: [{ label: "Sign out", icon: "logout", danger: true }],
|
|
2774
2925
|
onSelect: onSignOut
|
|
2775
2926
|
}
|
|
2776
2927
|
) })
|
|
2777
2928
|
] }),
|
|
2778
|
-
/* @__PURE__ */
|
|
2779
|
-
back && /* @__PURE__ */
|
|
2780
|
-
/* @__PURE__ */
|
|
2781
|
-
lede && /* @__PURE__ */
|
|
2782
|
-
actions && /* @__PURE__ */
|
|
2783
|
-
/* @__PURE__ */
|
|
2929
|
+
/* @__PURE__ */ jsxs28("main", { className: `ui-app__main${width === "wide" ? " ui-app__main--wide" : ""}`, children: [
|
|
2930
|
+
back && /* @__PURE__ */ jsx33(BackLink, { ...back }),
|
|
2931
|
+
/* @__PURE__ */ jsx33("h1", { children: title }),
|
|
2932
|
+
lede && /* @__PURE__ */ jsx33("p", { className: "ui-app__sub", children: lede }),
|
|
2933
|
+
actions && /* @__PURE__ */ jsx33("div", { className: "ui-react-app__actions", children: actions }),
|
|
2934
|
+
/* @__PURE__ */ jsx33("div", { className: "ui-app__body", children })
|
|
2784
2935
|
] })
|
|
2785
2936
|
] }),
|
|
2786
|
-
/* @__PURE__ */
|
|
2937
|
+
/* @__PURE__ */ jsx33("nav", { className: "ui-nav ui-react-app__bottom", "aria-label": "Sections on mobile", children: /* @__PURE__ */ jsxs28("ul", { className: "ui-nav__list", children: [
|
|
2787
2938
|
links(visible),
|
|
2788
|
-
remaining.length > 0 && /* @__PURE__ */
|
|
2939
|
+
remaining.length > 0 && /* @__PURE__ */ jsx33("li", { children: /* @__PURE__ */ jsxs28(
|
|
2789
2940
|
"button",
|
|
2790
2941
|
{
|
|
2791
2942
|
type: "button",
|
|
@@ -2794,20 +2945,20 @@ function AppShell({
|
|
|
2794
2945
|
"aria-expanded": more,
|
|
2795
2946
|
onClick: () => setMore(true),
|
|
2796
2947
|
children: [
|
|
2797
|
-
/* @__PURE__ */
|
|
2798
|
-
/* @__PURE__ */
|
|
2948
|
+
/* @__PURE__ */ jsx33("span", { className: "ui-nav__ic", children: /* @__PURE__ */ jsx33(Icon, { name: "moreHorizontal" }) }),
|
|
2949
|
+
/* @__PURE__ */ jsx33("span", { className: "ui-nav__label", children: "More" })
|
|
2799
2950
|
]
|
|
2800
2951
|
}
|
|
2801
2952
|
) })
|
|
2802
2953
|
] }) }),
|
|
2803
|
-
/* @__PURE__ */
|
|
2804
|
-
/* @__PURE__ */
|
|
2954
|
+
/* @__PURE__ */ jsx33(Drawer, { open: more, onClose: () => setMore(false), title: "More sections", side: "bottom", children: sidebar(remaining, "More sections") }),
|
|
2955
|
+
/* @__PURE__ */ jsx33(CommandPalette, { ...palette ?? { groups: [{ items: sections.map((section) => ({ id: section.href, ...section })) }] }, open: search, onClose: () => setSearch(false) })
|
|
2805
2956
|
] });
|
|
2806
2957
|
}
|
|
2807
2958
|
|
|
2808
2959
|
// src/EmptyState.tsx
|
|
2809
2960
|
import { icon as kitIcon2, illo } from "@apliteni/apliteni-ui";
|
|
2810
|
-
import { jsx as
|
|
2961
|
+
import { jsx as jsx34, jsxs as jsxs29 } from "react/jsx-runtime";
|
|
2811
2962
|
var cases = {
|
|
2812
2963
|
"first-run": { title: "Nothing needs you yet", sub: "New items will appear here when they need your attention.", icon: "check" },
|
|
2813
2964
|
"no-matches": { title: "No results in this view", sub: "Try another search or clear your filters.", icon: "search" },
|
|
@@ -2819,18 +2970,18 @@ function EmptyState({ variant = "first-run", title, sub, icon: icon7, art, actio
|
|
|
2819
2970
|
const heading = title ?? preset.title;
|
|
2820
2971
|
const guidance = sub ?? preset.sub;
|
|
2821
2972
|
const glyph = icon7 ?? preset.icon;
|
|
2822
|
-
return /* @__PURE__ */
|
|
2823
|
-
art ? typeof art === "string" ? /* @__PURE__ */
|
|
2824
|
-
heading && /* @__PURE__ */
|
|
2825
|
-
guidance && /* @__PURE__ */
|
|
2826
|
-
actions && /* @__PURE__ */
|
|
2973
|
+
return /* @__PURE__ */ jsxs29("div", { className: "ui-empty", children: [
|
|
2974
|
+
art ? typeof art === "string" ? /* @__PURE__ */ jsx34("div", { className: "ui-empty__art", dangerouslySetInnerHTML: { __html: /<svg/.test(art) ? art : illo(art) } }) : /* @__PURE__ */ jsx34("div", { className: "ui-empty__art", "aria-hidden": "true", children: art }) : glyph ? /* @__PURE__ */ jsx34("div", { className: "ui-empty__icon", dangerouslySetInnerHTML: { __html: kitIcon2(glyph) } }) : null,
|
|
2975
|
+
heading && /* @__PURE__ */ jsx34("div", { className: "ui-empty__title", children: heading }),
|
|
2976
|
+
guidance && /* @__PURE__ */ jsx34("div", { className: "ui-empty__sub", children: guidance }),
|
|
2977
|
+
actions && /* @__PURE__ */ jsx34("div", { className: "ui-empty__actions", children: actions })
|
|
2827
2978
|
] });
|
|
2828
2979
|
}
|
|
2829
2980
|
|
|
2830
2981
|
// src/ThemeToggle.tsx
|
|
2831
2982
|
import { useSyncExternalStore } from "react";
|
|
2832
2983
|
import { icon as icon6 } from "@apliteni/apliteni-ui";
|
|
2833
|
-
import { jsx as
|
|
2984
|
+
import { jsx as jsx35, jsxs as jsxs30 } from "react/jsx-runtime";
|
|
2834
2985
|
var STORAGE_KEY = "apliteni-strategy-theme";
|
|
2835
2986
|
var CHOICE_EVENT = "apliteni-theme-choice";
|
|
2836
2987
|
var THEME_INIT_SCRIPT = `(()=>{let t;try{t=localStorage.getItem('apliteni-strategy-theme')}catch{}document.documentElement.setAttribute('data-theme',t==='dark'||t==='light'?t:window.matchMedia?.('(prefers-color-scheme: light)').matches?'light':'dark')})()`;
|
|
@@ -2888,10 +3039,10 @@ function ThemeToggle({ labelled = false }) {
|
|
|
2888
3039
|
}
|
|
2889
3040
|
window.dispatchEvent(new Event(CHOICE_EVENT));
|
|
2890
3041
|
};
|
|
2891
|
-
const button = /* @__PURE__ */
|
|
2892
|
-
return labelled ? /* @__PURE__ */
|
|
3042
|
+
const button = /* @__PURE__ */ jsx35("button", { type: "button", className: "toggle", "aria-label": name, title: name, onClick: toggle, children: /* @__PURE__ */ jsx35("span", { className: "ic", "aria-hidden": "true", dangerouslySetInnerHTML: { __html: icon6(theme === "auto" ? "monitor" : theme === "light" ? "sun" : "moon") } }) });
|
|
3043
|
+
return labelled ? /* @__PURE__ */ jsxs30("span", { className: "ui-theme-toggle", children: [
|
|
2893
3044
|
button,
|
|
2894
|
-
/* @__PURE__ */
|
|
3045
|
+
/* @__PURE__ */ jsxs30("span", { children: [
|
|
2895
3046
|
label,
|
|
2896
3047
|
" theme"
|
|
2897
3048
|
] })
|
|
@@ -2899,11 +3050,11 @@ function ThemeToggle({ labelled = false }) {
|
|
|
2899
3050
|
}
|
|
2900
3051
|
|
|
2901
3052
|
// src/Success.tsx
|
|
2902
|
-
import { forwardRef as forwardRef9, useEffect as useEffect14, useRef as
|
|
2903
|
-
import { jsx as
|
|
3053
|
+
import { forwardRef as forwardRef9, useEffect as useEffect14, useRef as useRef18, useState as useState14 } from "react";
|
|
3054
|
+
import { jsx as jsx36, jsxs as jsxs31 } from "react/jsx-runtime";
|
|
2904
3055
|
var SuccessCheck = forwardRef9(function SuccessCheck2({ variant = "line", className, ...rest }, ref) {
|
|
2905
3056
|
const circled = variant === "circled";
|
|
2906
|
-
return /* @__PURE__ */
|
|
3057
|
+
return /* @__PURE__ */ jsxs31(
|
|
2907
3058
|
"svg",
|
|
2908
3059
|
{
|
|
2909
3060
|
...rest,
|
|
@@ -2915,18 +3066,18 @@ var SuccessCheck = forwardRef9(function SuccessCheck2({ variant = "line", classN
|
|
|
2915
3066
|
"aria-hidden": "true",
|
|
2916
3067
|
focusable: "false",
|
|
2917
3068
|
children: [
|
|
2918
|
-
circled && /* @__PURE__ */
|
|
2919
|
-
/* @__PURE__ */
|
|
3069
|
+
circled && /* @__PURE__ */ jsx36("path", { className: "ui-sx__circle", d: "M22 11.08V12a10 10 0 1 1-5.93-9.14" }),
|
|
3070
|
+
/* @__PURE__ */ jsx36("path", { className: "ui-sx__tick", d: circled ? "M22 4L12 14.01l-3-3" : "M20 6L9 17l-5-5" })
|
|
2920
3071
|
]
|
|
2921
3072
|
}
|
|
2922
3073
|
);
|
|
2923
3074
|
});
|
|
2924
3075
|
var SuccessPanel = forwardRef9(function SuccessPanel2({ title = "Done", sub, check = "line", className, ...rest }, ref) {
|
|
2925
3076
|
const circled = check === "circled";
|
|
2926
|
-
return /* @__PURE__ */
|
|
2927
|
-
/* @__PURE__ */
|
|
2928
|
-
/* @__PURE__ */
|
|
2929
|
-
sub && /* @__PURE__ */
|
|
3077
|
+
return /* @__PURE__ */ jsxs31("div", { ...rest, ref, className: ["ui-success", className].filter(Boolean).join(" "), children: [
|
|
3078
|
+
/* @__PURE__ */ jsx36("div", { className: ["ui-success__check", circled && "ui-success__check--circled"].filter(Boolean).join(" "), children: /* @__PURE__ */ jsx36(SuccessCheck, { variant: circled ? "circled" : "line" }) }),
|
|
3079
|
+
/* @__PURE__ */ jsx36("div", { className: "ui-success__title", children: title }),
|
|
3080
|
+
sub && /* @__PURE__ */ jsx36("div", { className: "ui-success__sub", children: sub })
|
|
2930
3081
|
] });
|
|
2931
3082
|
});
|
|
2932
3083
|
var pieces = [
|
|
@@ -2946,8 +3097,8 @@ var pieces = [
|
|
|
2946
3097
|
[80, 0.36, -20, "c", 1.05]
|
|
2947
3098
|
];
|
|
2948
3099
|
function Countdown({ seconds, label = "Redirecting", onDone }) {
|
|
2949
|
-
const [remaining, setRemaining] =
|
|
2950
|
-
const callback =
|
|
3100
|
+
const [remaining, setRemaining] = useState14(seconds);
|
|
3101
|
+
const callback = useRef18(onDone);
|
|
2951
3102
|
useEffect14(() => {
|
|
2952
3103
|
callback.current = onDone;
|
|
2953
3104
|
}, [onDone]);
|
|
@@ -2963,12 +3114,12 @@ function Countdown({ seconds, label = "Redirecting", onDone }) {
|
|
|
2963
3114
|
}, 1e3);
|
|
2964
3115
|
return () => clearInterval(timer);
|
|
2965
3116
|
}, [seconds]);
|
|
2966
|
-
return /* @__PURE__ */
|
|
2967
|
-
/* @__PURE__ */
|
|
2968
|
-
/* @__PURE__ */
|
|
3117
|
+
return /* @__PURE__ */ jsxs31("div", { className: "ui-sx__count", "data-sx-count": "", style: { "--sx-secs": `${seconds}s` }, children: [
|
|
3118
|
+
/* @__PURE__ */ jsx36("span", { className: "ui-sx__count-ring", "aria-hidden": "true" }),
|
|
3119
|
+
/* @__PURE__ */ jsxs31("span", { className: "ui-sx__count-text", children: [
|
|
2969
3120
|
label,
|
|
2970
3121
|
" in ",
|
|
2971
|
-
/* @__PURE__ */
|
|
3122
|
+
/* @__PURE__ */ jsx36("b", { "data-sx-num": "", children: remaining }),
|
|
2972
3123
|
"s"
|
|
2973
3124
|
] })
|
|
2974
3125
|
] });
|
|
@@ -2995,7 +3146,7 @@ var Success = forwardRef9(function Success2({
|
|
|
2995
3146
|
const seconds = countdown?.seconds;
|
|
2996
3147
|
const duration = seconds != null && Number.isFinite(seconds) && seconds >= 1 ? Math.floor(seconds) : 5;
|
|
2997
3148
|
const mark = check === "circled" ? "circled" : "line";
|
|
2998
|
-
return /* @__PURE__ */
|
|
3149
|
+
return /* @__PURE__ */ jsxs31(
|
|
2999
3150
|
"div",
|
|
3000
3151
|
{
|
|
3001
3152
|
role: "status",
|
|
@@ -3004,16 +3155,16 @@ var Success = forwardRef9(function Success2({
|
|
|
3004
3155
|
ref,
|
|
3005
3156
|
className: ["ui-sx", `ui-sx--${layout}`, `ui-sx--check-${mark}`, confetti && "ui-sx--confetti", className].filter(Boolean).join(" "),
|
|
3006
3157
|
children: [
|
|
3007
|
-
confetti && /* @__PURE__ */
|
|
3008
|
-
([x, d, r, t, s], index) => /* @__PURE__ */
|
|
3158
|
+
confetti && /* @__PURE__ */ jsx36("div", { className: "ui-sx__confetti", "aria-hidden": "true", children: pieces.map(
|
|
3159
|
+
([x, d, r, t, s], index) => /* @__PURE__ */ jsx36("i", { className: `ui-sx__piece ui-sx__piece--${t}`, style: { left: `${x}%`, "--sx-d": `${d}s`, "--sx-r": `${r}deg`, "--sx-s": s } }, index)
|
|
3009
3160
|
) }),
|
|
3010
|
-
/* @__PURE__ */
|
|
3011
|
-
/* @__PURE__ */
|
|
3012
|
-
/* @__PURE__ */
|
|
3013
|
-
/* @__PURE__ */
|
|
3014
|
-
body && /* @__PURE__ */
|
|
3015
|
-
hasActions && /* @__PURE__ */
|
|
3016
|
-
countdown && /* @__PURE__ */
|
|
3161
|
+
/* @__PURE__ */ jsxs31("div", { className: "ui-sx__inner", children: [
|
|
3162
|
+
/* @__PURE__ */ jsx36("div", { className: "ui-sx__visual", children: /* @__PURE__ */ jsx36(SuccessCheck, { variant: mark }) }),
|
|
3163
|
+
/* @__PURE__ */ jsxs31("div", { className: "ui-sx__content", children: [
|
|
3164
|
+
/* @__PURE__ */ jsx36(Heading, { className: "ui-sx__title", children: title }),
|
|
3165
|
+
body && /* @__PURE__ */ jsx36("p", { className: "ui-sx__body", children: body }),
|
|
3166
|
+
hasActions && /* @__PURE__ */ jsx36("div", { className: "ui-sx__actions", children: actions }),
|
|
3167
|
+
countdown && /* @__PURE__ */ jsx36(Countdown, { seconds: duration, label: countdown.label, onDone: onCountdownEnd }, duration)
|
|
3017
3168
|
] })
|
|
3018
3169
|
] })
|
|
3019
3170
|
]
|
|
@@ -3044,6 +3195,7 @@ export {
|
|
|
3044
3195
|
Dropdown,
|
|
3045
3196
|
EmptyState,
|
|
3046
3197
|
Field,
|
|
3198
|
+
FileDrop,
|
|
3047
3199
|
FileField,
|
|
3048
3200
|
FilterBar,
|
|
3049
3201
|
Icon,
|
package/src/index.css
CHANGED
package/src/inline.js
CHANGED
|
@@ -38,6 +38,7 @@ export const styles = {
|
|
|
38
38
|
segmented: read('styles/segmented.css'),
|
|
39
39
|
tabs: read('styles/tabs.css'),
|
|
40
40
|
input: read('styles/input.css'),
|
|
41
|
+
fileDrop: read('styles/file-drop.css'),
|
|
41
42
|
dropdown: read('styles/dropdown.css'),
|
|
42
43
|
tooltip: read('styles/tooltip.css'),
|
|
43
44
|
nav: read('styles/nav.css'),
|
|
@@ -73,6 +74,7 @@ export const cssText = [
|
|
|
73
74
|
styles.segmented,
|
|
74
75
|
styles.tabs,
|
|
75
76
|
styles.input,
|
|
77
|
+
styles.fileDrop,
|
|
76
78
|
styles.dropdown,
|
|
77
79
|
styles.tooltip,
|
|
78
80
|
styles.nav,
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/* File drop — .ui-drop. One row at rest; the field-sized box stays with .ui-file.
|
|
2
|
+
* why: docs/specification.md#react-file-drop */
|
|
3
|
+
.ui-drop { position: relative; }
|
|
4
|
+
|
|
5
|
+
/* What the region accepts the file for, above the control. */
|
|
6
|
+
.ui-drop > * + .ui-drop__row { margin-top: var(--space-3); }
|
|
7
|
+
|
|
8
|
+
.ui-drop__row {
|
|
9
|
+
display: flex;
|
|
10
|
+
flex-wrap: wrap;
|
|
11
|
+
align-items: center;
|
|
12
|
+
/* One height at rest, whichever control is in it: the picker button carries a
|
|
13
|
+
glyph and a note beside it does not. */
|
|
14
|
+
min-height: var(--space-8);
|
|
15
|
+
gap: var(--space-2) var(--space-3);
|
|
16
|
+
font-size: var(--text-sm);
|
|
17
|
+
color: var(--text);
|
|
18
|
+
overflow-wrap: anywhere;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.ui-drop__note { margin: 0; }
|
|
22
|
+
|
|
23
|
+
/* The file in hand is a stack, not a line: the name owns the top tier, what the
|
|
24
|
+
page has a rule about sits under it, and the track runs the width below both.
|
|
25
|
+
One line asked to carry all three truncates the name to nothing at 320. */
|
|
26
|
+
.ui-drop__file { display: grid; flex: 1 1 auto; gap: var(--space-2); min-width: 0; }
|
|
27
|
+
|
|
28
|
+
/* The name tier. Never wraps: the actions stay on the name's line at every
|
|
29
|
+
width, and the name gives way to them by truncating. */
|
|
30
|
+
.ui-drop__line { display: flex; flex-wrap: nowrap; align-items: center; gap: var(--space-3); min-width: 0; }
|
|
31
|
+
|
|
32
|
+
/* The extension is the half that says what kind of file this is, so the stem
|
|
33
|
+
gives way and the tail never does — `frankfurt-settlement-state….pdf`, the
|
|
34
|
+
way Finder, Drive and Dropbox cut a name. */
|
|
35
|
+
.ui-drop__name { display: flex; flex: 1 1 auto; min-width: var(--space-10); color: var(--strong); font-weight: var(--weight-medium); }
|
|
36
|
+
.ui-drop__stem { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
37
|
+
.ui-drop__ext { flex: none; }
|
|
38
|
+
|
|
39
|
+
/* The facts tier: the size while it uploads, the message when it fails. Never
|
|
40
|
+
wraps either — a second tier of text here would be a third tier on the row. */
|
|
41
|
+
.ui-drop__facts { display: flex; flex-wrap: nowrap; align-items: center; gap: var(--space-3); min-width: 0; }
|
|
42
|
+
.ui-drop__size { flex: none; white-space: nowrap; }
|
|
43
|
+
.ui-drop__word { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 0 1 auto; min-width: 0; }
|
|
44
|
+
|
|
45
|
+
/* A status the track does not already draw is a circled mark and a word,
|
|
46
|
+
because colour alone is not a status. */
|
|
47
|
+
.ui-drop__state { display: inline-flex; flex: 0 1 auto; align-items: center; gap: var(--space-2); min-width: 0; }
|
|
48
|
+
.ui-drop__state svg { width: 16px; height: 16px; flex: none; stroke-width: 2.4; }
|
|
49
|
+
.ui-drop__error { color: var(--pink); }
|
|
50
|
+
|
|
51
|
+
/* Closes the name's line on the edge the content ends on, in every state. */
|
|
52
|
+
.ui-drop__actions { display: flex; flex: none; align-items: center; gap: var(--space-2); margin-left: auto; }
|
|
53
|
+
|
|
54
|
+
/* The width of the row it reports on, so 40% reads as 40% rather than as a dash.
|
|
55
|
+
The ground is the kit's token for sunken tracks; --surface-3 was invisible. */
|
|
56
|
+
.ui-drop__bar {
|
|
57
|
+
display: block;
|
|
58
|
+
width: 100%;
|
|
59
|
+
height: 6px;
|
|
60
|
+
border-radius: var(--radius-pill);
|
|
61
|
+
background: var(--surface-2); /* ring-gap: inherit — a progress track holds no focus target. */
|
|
62
|
+
overflow: hidden;
|
|
63
|
+
}
|
|
64
|
+
.ui-drop__bar span { display: block; height: 100%; background: var(--accent); }
|
|
65
|
+
|
|
66
|
+
/* Covers the region rather than joining it, so the row keeps its place. */
|
|
67
|
+
.ui-drop__target {
|
|
68
|
+
position: absolute;
|
|
69
|
+
inset: 0;
|
|
70
|
+
display: flex;
|
|
71
|
+
align-items: center;
|
|
72
|
+
justify-content: center;
|
|
73
|
+
padding: var(--space-3);
|
|
74
|
+
border: 1px dashed var(--accent);
|
|
75
|
+
border-radius: var(--radius-md);
|
|
76
|
+
background: var(--surface); /* ring-gap: inherit — the target is painted while a file is over the region and holds no focus target. */
|
|
77
|
+
color: var(--strong);
|
|
78
|
+
font-size: var(--text-sm);
|
|
79
|
+
text-align: center;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.ui-drop.is-disabled .ui-drop__row { /* muted-ink: state — the drop accepts nothing */ color: var(--disabled-ink); }
|
|
83
|
+
.ui-drop.is-disabled .ui-drop__name { /* muted-ink: state — the drop accepts nothing */ color: var(--disabled-ink); }
|
package/src/styles/tap-zone.css
CHANGED
|
@@ -128,6 +128,15 @@
|
|
|
128
128
|
mark's zone 9px inside `Revoke`. Nothing down, because a dense table's
|
|
129
129
|
rows share an edge and its padding is not clearance to spend. */
|
|
130
130
|
.ui-table__act .ui-btn + .ui-btn { margin-inline-start: var(--tap-gap) !important; }
|
|
131
|
+
|
|
132
|
+
/* A file drop's pair opens both ways: the row's own floor leaves clearance
|
|
133
|
+
under a button shorter than the target, and the panel's padding is below. */
|
|
134
|
+
.ui-drop__row { --tap-clear-y: var(--tap-gap); }
|
|
135
|
+
.ui-drop__actions {
|
|
136
|
+
gap: var(--tap-gap) !important;
|
|
137
|
+
--tap-clear-x: var(--tap-gap);
|
|
138
|
+
--tap-clear-y: var(--tap-gap);
|
|
139
|
+
}
|
|
131
140
|
.ui-table__act { --tap-clear-x: var(--tap-gap); --tap-clear-y: 0px; }
|
|
132
141
|
|
|
133
142
|
/* Down only, like the nine rows below: a toast is a full row at this width,
|