@apliteni/apliteni-ui 0.47.0 → 0.48.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/package.json +1 -1
- package/react/README.md +17 -0
- package/react/dist/index.css +13 -0
- package/react/dist/index.d.ts +16 -1
- package/react/dist/index.js +116 -1
package/package.json
CHANGED
package/react/README.md
CHANGED
|
@@ -407,3 +407,20 @@ native control props. `SelectField` accepts option children.
|
|
|
407
407
|
`FileField` accepts one file from a picker or a drop and reports it through
|
|
408
408
|
`onFileChange`. Use `accept` and `hint` to describe allowed file types and size.
|
|
409
409
|
Your application must validate files before upload.
|
|
410
|
+
|
|
411
|
+
## Toast
|
|
412
|
+
|
|
413
|
+
Wrap your app in `Toast`, then call `useToast()` inside it to add a notice.
|
|
414
|
+
Pass `title`, optional `text`, and a `tone`: success (default), danger, warn, info,
|
|
415
|
+
or neutral. Notices disappear after five seconds, pausing while hovered or focused.
|
|
416
|
+
Danger notices are announced assertively; other tones are polite. An `action` with a `label` and
|
|
417
|
+
`onClick` stays until selected or dismissed. New notices appear at the bottom.
|
|
418
|
+
|
|
419
|
+
```tsx
|
|
420
|
+
function SaveButton() {
|
|
421
|
+
const push = useToast();
|
|
422
|
+
return <Button onClick={() => push({ title: 'Saved', text: 'Your changes were saved.' })}>Save</Button>;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
<Toast><SaveButton /></Toast>
|
|
426
|
+
```
|
package/react/dist/index.css
CHANGED
|
@@ -512,3 +512,16 @@
|
|
|
512
512
|
.ui-file.is-disabled .ui-file__input {
|
|
513
513
|
cursor: not-allowed;
|
|
514
514
|
}
|
|
515
|
+
|
|
516
|
+
/* src/Toast.css */
|
|
517
|
+
.rx-toast-stack {
|
|
518
|
+
position: fixed;
|
|
519
|
+
right: var(--space-4);
|
|
520
|
+
bottom: var(--space-4);
|
|
521
|
+
max-width: calc(100% - 2 * var(--space-4));
|
|
522
|
+
z-index: var(--z-toast);
|
|
523
|
+
pointer-events: none;
|
|
524
|
+
}
|
|
525
|
+
.rx-toast-stack > .ui-toast {
|
|
526
|
+
pointer-events: auto;
|
|
527
|
+
}
|
package/react/dist/index.d.ts
CHANGED
|
@@ -526,7 +526,22 @@ type FileFieldProps = FieldMessage & Omit<InputHTMLAttributes<HTMLInputElement>,
|
|
|
526
526
|
};
|
|
527
527
|
declare function FileField({ label, hint, error, required, disabled, onFileChange, className, ...props }: FileFieldProps): react.JSX.Element;
|
|
528
528
|
|
|
529
|
+
type ToastNotice = {
|
|
530
|
+
tone?: 'success' | 'danger' | 'warn' | 'info' | 'neutral';
|
|
531
|
+
title: string;
|
|
532
|
+
text?: string;
|
|
533
|
+
action?: {
|
|
534
|
+
label: string;
|
|
535
|
+
onClick: () => void;
|
|
536
|
+
};
|
|
537
|
+
};
|
|
538
|
+
type ToastProps = {
|
|
539
|
+
children: ReactNode;
|
|
540
|
+
};
|
|
541
|
+
declare function useToast(): (notice: ToastNotice) => void;
|
|
542
|
+
declare function Toast({ children }: ToastProps): react.JSX.Element;
|
|
543
|
+
|
|
529
544
|
declare const PAGE_SIZES: readonly number[];
|
|
530
545
|
declare const DEFAULT_PAGE_SIZE: number;
|
|
531
546
|
|
|
532
|
-
export { BackLink, type BackLinkOwnProps, type BackLinkProps, Badge, BusyRegion, type BusyRegionProps, Button, type ButtonProps, Card, 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, FileField, type FileFieldProps, type Filter, FilterBar, type FilterBarProps, Icon, KeyValueList, type KeyValueListProps, type KeyValueRow, Modal, type ModalProps, NumericValue, type NumericValueProps, PAGE_SIZES, Pagination, type PaginationProps, RowIdentity, type RowIdentityProps, type SegmentOption, Segmented, type SegmentedProps, SelectField, type SelectFieldProps, Skeleton, type SkeletonProps, SkeletonTable, type SkeletonTableProps, Snippet, type SnippetProps, StatBand, type StatBandProps, type StatDelta, type StatFigure, type StatTone, type StatVariant, type TabItem, type TableSort, Tabs, type TabsProps, TextArea, type TextAreaProps, TextField, type TextFieldProps, Timeline, type TimelineEvent, type TimelineProps, sortTableRows };
|
|
547
|
+
export { BackLink, type BackLinkOwnProps, type BackLinkProps, Badge, BusyRegion, type BusyRegionProps, Button, type ButtonProps, Card, 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, FileField, type FileFieldProps, type Filter, FilterBar, type FilterBarProps, Icon, KeyValueList, type KeyValueListProps, type KeyValueRow, Modal, type ModalProps, NumericValue, type NumericValueProps, PAGE_SIZES, Pagination, type PaginationProps, RowIdentity, type RowIdentityProps, type SegmentOption, Segmented, type SegmentedProps, SelectField, type SelectFieldProps, Skeleton, type SkeletonProps, SkeletonTable, type SkeletonTableProps, Snippet, type SnippetProps, StatBand, type StatBandProps, type StatDelta, type StatFigure, type StatTone, type StatVariant, type TabItem, type TableSort, Tabs, type TabsProps, TextArea, type TextAreaProps, TextField, type TextFieldProps, Timeline, type TimelineEvent, type TimelineProps, Toast, type ToastNotice, type ToastProps, sortTableRows, useToast };
|
package/react/dist/index.js
CHANGED
|
@@ -2026,6 +2026,119 @@ function FileField({ label, hint, error, required, disabled, onFileChange, class
|
|
|
2026
2026
|
) });
|
|
2027
2027
|
}
|
|
2028
2028
|
|
|
2029
|
+
// src/Toast.tsx
|
|
2030
|
+
import { createContext as createContext2, useCallback as useCallback3, useContext as useContext2, useEffect as useEffect8, useRef as useRef13, useState as useState8 } from "react";
|
|
2031
|
+
import { createPortal as createPortal4 } from "react-dom";
|
|
2032
|
+
import { jsx as jsx23, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
2033
|
+
var Context = createContext2(null);
|
|
2034
|
+
var glyphs = { success: "circleCheck", danger: "circleX", warn: "circleAlert", info: "info", neutral: "bolt" };
|
|
2035
|
+
function useToast() {
|
|
2036
|
+
const push = useContext2(Context);
|
|
2037
|
+
if (!push) throw new Error("useToast requires a Toast provider");
|
|
2038
|
+
return push;
|
|
2039
|
+
}
|
|
2040
|
+
function Notice({ notice, remove }) {
|
|
2041
|
+
const root = useRef13(null);
|
|
2042
|
+
const [leaving2, setLeaving] = useState8(false);
|
|
2043
|
+
const dismissed = useRef13(false);
|
|
2044
|
+
const remaining = useRef13(5e3);
|
|
2045
|
+
const [hovered, setHovered] = useState8(false);
|
|
2046
|
+
const [focused, setFocused] = useState8(false);
|
|
2047
|
+
const paused = hovered || focused;
|
|
2048
|
+
const tone = notice.tone ?? "success";
|
|
2049
|
+
const dismiss = useCallback3(() => {
|
|
2050
|
+
if (dismissed.current) return;
|
|
2051
|
+
dismissed.current = true;
|
|
2052
|
+
if (typeof matchMedia === "function" && matchMedia("(prefers-reduced-motion: reduce)").matches) remove();
|
|
2053
|
+
else setLeaving(true);
|
|
2054
|
+
}, [remove]);
|
|
2055
|
+
useEffect8(() => {
|
|
2056
|
+
if (leaving2) {
|
|
2057
|
+
const element = root.current;
|
|
2058
|
+
const finish = (event) => {
|
|
2059
|
+
if (event.target === element) remove();
|
|
2060
|
+
};
|
|
2061
|
+
element?.addEventListener("animationend", finish);
|
|
2062
|
+
const timer = setTimeout(remove, 260);
|
|
2063
|
+
return () => {
|
|
2064
|
+
clearTimeout(timer);
|
|
2065
|
+
element?.removeEventListener("animationend", finish);
|
|
2066
|
+
};
|
|
2067
|
+
}
|
|
2068
|
+
}, [leaving2, remove]);
|
|
2069
|
+
useEffect8(() => {
|
|
2070
|
+
if (leaving2 || notice.action || paused) return;
|
|
2071
|
+
const started = Date.now();
|
|
2072
|
+
const timer = setTimeout(dismiss, remaining.current);
|
|
2073
|
+
return () => {
|
|
2074
|
+
clearTimeout(timer);
|
|
2075
|
+
remaining.current = Math.max(0, remaining.current - (Date.now() - started));
|
|
2076
|
+
};
|
|
2077
|
+
}, [dismiss, leaving2, notice.action, paused]);
|
|
2078
|
+
return /* @__PURE__ */ jsxs19(
|
|
2079
|
+
"div",
|
|
2080
|
+
{
|
|
2081
|
+
ref: root,
|
|
2082
|
+
className: `ui-toast ui-toast--${tone} ui-toast--soft${leaving2 ? " is-leaving" : ""}`,
|
|
2083
|
+
role: tone === "danger" ? "alert" : "status",
|
|
2084
|
+
"aria-live": tone === "danger" ? "assertive" : "polite",
|
|
2085
|
+
onMouseEnter: () => setHovered(true),
|
|
2086
|
+
onMouseLeave: () => setHovered(false),
|
|
2087
|
+
onFocus: () => setFocused(true),
|
|
2088
|
+
onBlur: (event) => {
|
|
2089
|
+
if (!event.currentTarget.contains(event.relatedTarget)) setFocused(false);
|
|
2090
|
+
},
|
|
2091
|
+
children: [
|
|
2092
|
+
/* @__PURE__ */ jsx23("span", { className: "ui-toast__icon", children: /* @__PURE__ */ jsx23(Icon, { name: glyphs[tone] }) }),
|
|
2093
|
+
/* @__PURE__ */ jsxs19("div", { className: "ui-toast__body", children: [
|
|
2094
|
+
/* @__PURE__ */ jsx23("div", { className: "ui-toast__title", children: notice.title }),
|
|
2095
|
+
notice.text && /* @__PURE__ */ jsx23("div", { className: "ui-toast__text", children: notice.text })
|
|
2096
|
+
] }),
|
|
2097
|
+
notice.action && /* @__PURE__ */ jsx23(
|
|
2098
|
+
"button",
|
|
2099
|
+
{
|
|
2100
|
+
type: "button",
|
|
2101
|
+
className: "ui-toast__action",
|
|
2102
|
+
disabled: leaving2,
|
|
2103
|
+
onClick: () => {
|
|
2104
|
+
if (!dismissed.current) {
|
|
2105
|
+
dismiss();
|
|
2106
|
+
notice.action.onClick();
|
|
2107
|
+
}
|
|
2108
|
+
},
|
|
2109
|
+
children: notice.action.label
|
|
2110
|
+
}
|
|
2111
|
+
),
|
|
2112
|
+
/* @__PURE__ */ jsx23("button", { type: "button", className: "ui-toast__close", "aria-label": "Dismiss", disabled: leaving2, onClick: dismiss, children: /* @__PURE__ */ jsx23(Icon, { name: "x" }) }),
|
|
2113
|
+
!notice.action && /* @__PURE__ */ jsx23(
|
|
2114
|
+
"span",
|
|
2115
|
+
{
|
|
2116
|
+
className: "ui-toast__timer is-running",
|
|
2117
|
+
"aria-hidden": "true",
|
|
2118
|
+
style: { animationPlayState: paused ? "paused" : "running" }
|
|
2119
|
+
}
|
|
2120
|
+
)
|
|
2121
|
+
]
|
|
2122
|
+
}
|
|
2123
|
+
);
|
|
2124
|
+
}
|
|
2125
|
+
function Toast({ children }) {
|
|
2126
|
+
const [entries, setEntries] = useState8([]);
|
|
2127
|
+
const nextId = useRef13(0);
|
|
2128
|
+
const push = useCallback3((notice) => {
|
|
2129
|
+
const id = nextId.current++;
|
|
2130
|
+
const remove = () => setEntries((current) => current.filter((entry) => entry.id !== id));
|
|
2131
|
+
setEntries((current) => [...current, { id, notice, remove }]);
|
|
2132
|
+
}, []);
|
|
2133
|
+
return /* @__PURE__ */ jsxs19(Context.Provider, { value: push, children: [
|
|
2134
|
+
children,
|
|
2135
|
+
typeof document !== "undefined" && createPortal4(
|
|
2136
|
+
/* @__PURE__ */ jsx23("div", { className: "ui-toast-stack rx-toast-stack", children: entries.map((entry) => /* @__PURE__ */ jsx23(Notice, { notice: entry.notice, remove: entry.remove }, entry.id)) }),
|
|
2137
|
+
document.body
|
|
2138
|
+
)
|
|
2139
|
+
] });
|
|
2140
|
+
}
|
|
2141
|
+
|
|
2029
2142
|
// src/index.ts
|
|
2030
2143
|
var PAGE_SIZES = KIT_PAGE_SIZES;
|
|
2031
2144
|
var DEFAULT_PAGE_SIZE3 = KIT_DEFAULT_PAGE_SIZE;
|
|
@@ -2063,5 +2176,7 @@ export {
|
|
|
2063
2176
|
TextArea,
|
|
2064
2177
|
TextField,
|
|
2065
2178
|
Timeline,
|
|
2066
|
-
|
|
2179
|
+
Toast,
|
|
2180
|
+
sortTableRows,
|
|
2181
|
+
useToast
|
|
2067
2182
|
};
|