@abyss-project/commons-front-core 8.1.0 → 8.3.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.
Files changed (130) hide show
  1. package/README.md +78 -0
  2. package/catalog/captures-plan.ts +5 -1
  3. package/catalog/catalog-entries.ts +21 -1
  4. package/catalog/cloud-picker-fixture.tsx +97 -0
  5. package/catalog/pages/AccountMenu.tsx +62 -0
  6. package/catalog/pages/Avatar.tsx +94 -0
  7. package/catalog/pages/Callout.tsx +67 -0
  8. package/catalog/pages/CloudImport.tsx +35 -0
  9. package/catalog/pages/ContextSelector.tsx +103 -0
  10. package/catalog/pages/CopyField.tsx +29 -0
  11. package/catalog/pages/DropZone.tsx +37 -0
  12. package/catalog/pages/FormField.tsx +54 -0
  13. package/catalog/pages/ImportButton.tsx +45 -0
  14. package/catalog/pages/ImportFromCloudModal.tsx +33 -0
  15. package/catalog/pages/Menu.tsx +99 -98
  16. package/catalog/pages/NotificationsMenu.tsx +116 -0
  17. package/catalog/pages/NumberField.tsx +46 -0
  18. package/catalog/pages/QRCodeModal.tsx +26 -0
  19. package/catalog/pages/SettingsRow.tsx +67 -0
  20. package/catalog/pages/Tabs.tsx +72 -0
  21. package/catalog/pages/UsageMeter.tsx +58 -0
  22. package/catalog/pages/index.ts +34 -2
  23. package/catalog/specs/captures.spec.ts +7 -0
  24. package/catalog/specs/write-capture-index.ts +1 -0
  25. package/dist/{AbyssCookieConsentProvider-BhSzVu8P.js → AbyssCookieConsentProvider-B-PlGbFf.js} +815 -625
  26. package/dist/{CookieConsentSettings.component-DMEAuCVS.js → CookieConsentSettings.component-DEGCDEJW.js} +1 -1
  27. package/dist/components/business/CloudImport/CloudImport.d.ts +24 -0
  28. package/dist/components/business/CloudImport/cloud-import-tree.d.ts +22 -0
  29. package/dist/components/business/ImportFromCloudModal/ImportFromCloudModal.d.ts +21 -0
  30. package/dist/components/business/QRCodeModal/QRCodeModal.d.ts +18 -0
  31. package/dist/components/business/QRCodeModal/QRCodeModal.spec.d.ts +1 -0
  32. package/dist/components/business/index.d.ts +3 -0
  33. package/dist/components/data/DataTable/data-table.types.d.ts +10 -0
  34. package/dist/components/data/UsageMeter/UsageMeter.d.ts +31 -0
  35. package/dist/components/data/UsageMeter/UsageMeter.spec.d.ts +1 -0
  36. package/dist/components/data/index.d.ts +1 -0
  37. package/dist/components/inputs/ContextSelector/ContextSelector.d.ts +60 -0
  38. package/dist/components/inputs/ContextSelector/ContextSelector.spec.d.ts +1 -0
  39. package/dist/components/inputs/CopyField/CopyField.d.ts +19 -0
  40. package/dist/components/inputs/CopyField/CopyField.spec.d.ts +1 -0
  41. package/dist/components/inputs/DropZone/DropZone.d.ts +30 -0
  42. package/dist/components/inputs/DropZone/DropZone.spec.d.ts +1 -0
  43. package/dist/components/inputs/FormField/FormField.d.ts +29 -0
  44. package/dist/components/inputs/FormField/FormField.spec.d.ts +1 -0
  45. package/dist/components/inputs/ImportButton/ImportButton.d.ts +33 -0
  46. package/dist/components/inputs/ImportButton/ImportButton.spec.d.ts +1 -0
  47. package/dist/components/inputs/NumberField/NumberField.d.ts +21 -0
  48. package/dist/components/inputs/NumberField/NumberField.spec.d.ts +1 -0
  49. package/dist/components/inputs/NumberField/number-input.d.ts +13 -0
  50. package/dist/components/inputs/SettingsRow/SettingsRow.d.ts +16 -0
  51. package/dist/components/inputs/SettingsRow/SettingsRow.spec.d.ts +1 -0
  52. package/dist/components/inputs/index.d.ts +7 -0
  53. package/dist/components/layers/CommandPalette/CommandPalette.d.ts +27 -6
  54. package/dist/components/layers/CommandPalette/command-registry.d.ts +39 -9
  55. package/dist/components/layers/Menu/Menu.d.ts +2 -0
  56. package/dist/components/layers/Modal/Modal.d.ts +2 -1
  57. package/dist/components/meaning/Avatar/Avatar.d.ts +41 -0
  58. package/dist/components/meaning/Avatar/Avatar.spec.d.ts +1 -0
  59. package/dist/components/meaning/index.d.ts +1 -0
  60. package/dist/components/states/Callout/Callout.d.ts +25 -0
  61. package/dist/components/states/Callout/Callout.spec.d.ts +1 -0
  62. package/dist/components/states/PageState/PageState.d.ts +6 -1
  63. package/dist/components/states/index.d.ts +1 -0
  64. package/dist/components/structure/AccountMenu/AccountMenu.d.ts +27 -0
  65. package/dist/components/structure/AccountMenu/AccountMenu.spec.d.ts +1 -0
  66. package/dist/components/structure/AppShell/AppShell.d.ts +9 -3
  67. package/dist/components/structure/AppShell/ShellNavigation.d.ts +4 -2
  68. package/dist/components/structure/AppShell/app-shell.types.d.ts +38 -2
  69. package/dist/components/structure/NotificationsMenu/NotificationsMenu.d.ts +36 -0
  70. package/dist/components/structure/NotificationsMenu/NotificationsMenu.spec.d.ts +1 -0
  71. package/dist/components/structure/PageHeader/PageHeader.d.ts +2 -2
  72. package/dist/components/structure/Tabs/Tabs.d.ts +34 -0
  73. package/dist/components/structure/Tabs/Tabs.spec.d.ts +1 -0
  74. package/dist/components/structure/index.d.ts +3 -0
  75. package/dist/components/structure/test-support/structure-test-harness.d.ts +6 -0
  76. package/dist/cookie-consent.js +2 -2
  77. package/dist/design-theme-CKTz_cOa.js +15490 -0
  78. package/dist/hooks/index.d.ts +1 -0
  79. package/dist/hooks/use-rich-intl.hook.d.ts +190 -0
  80. package/dist/hooks/use-scroll-overflow-cue.hook.d.ts +9 -0
  81. package/dist/hooks/use-scroll-overflow-cue.hook.spec.d.ts +1 -0
  82. package/dist/{index-D5e4uD4k.js → index-7N3J68EI.js} +2 -2
  83. package/dist/{index-BqIg55B3.js → index-B3rK_-T4.js} +4 -4
  84. package/dist/{index-6KdfKdSJ.js → index-B6qHo25Z.js} +2 -2
  85. package/dist/{index-BTjzj7mm.js → index-BMaAO_4e.js} +2 -2
  86. package/dist/{index-CHGRiRCe.js → index-B_jSh70G.js} +3 -3
  87. package/dist/{index-BkfBpYKR.js → index-B_yywet5.js} +3 -3
  88. package/dist/{index-DixNft1u.js → index-Bdtvzv-J.js} +2 -2
  89. package/dist/{index-ClLoBnCN.js → index-Bh2_8FCK.js} +2 -2
  90. package/dist/{index-D_GWBYM2.js → index-Bpg9TBMX.js} +1 -1
  91. package/dist/{index-GoHiFeaw.js → index-Bpnf8xKI.js} +2 -2
  92. package/dist/{index-bR-mnNUA.js → index-Bw2swxVq.js} +2 -2
  93. package/dist/{index-LdiTmDFg.js → index-BxxZgBFn.js} +2 -2
  94. package/dist/{index-ASLTmD1Y.js → index-Ca9tzEsn.js} +2 -2
  95. package/dist/{index-21vSFz6u.js → index-Csvh-AG6.js} +2 -2
  96. package/dist/{index-l7FAjaVB.js → index-CwuklsUK.js} +3 -3
  97. package/dist/{index-Ni4BmFs4.js → index-DFC9xBY9.js} +2 -2
  98. package/dist/{index-Btnmn1iK.js → index-DfUcsAJv.js} +4 -4
  99. package/dist/{index-D4Qh2Iww.js → index-DqedXp41.js} +3 -3
  100. package/dist/{index-BVS73U9E.js → index-DwXNHQIp.js} +4 -4
  101. package/dist/{index-BtsnuEJC.js → index-_UY_fqvv.js} +28415 -34554
  102. package/dist/{index-7HAvRXgu.js → index-dVVflNge.js} +2 -2
  103. package/dist/{index-C92C_oG5.js → index-sEWp0xIC.js} +2 -2
  104. package/dist/index.js +8273 -5009
  105. package/dist/legacy/components/QuotaOffer/AbyssQuotaOffer.component.d.ts +1 -1
  106. package/dist/legacy/components/QuotaOffer/index.d.ts +2 -2
  107. package/dist/legacy/theme/core-breakpoints-under-legacy-theme.spec.d.ts +1 -0
  108. package/dist/legacy.js +144 -144
  109. package/dist/theme/DesignCssVariables.d.ts +11 -0
  110. package/dist/theme/DesignCssVariables.spec.d.ts +1 -0
  111. package/dist/theme/components/shared.d.ts +4 -1
  112. package/dist/theme/css-variables.d.ts +7 -0
  113. package/dist/theme/design-media.d.ts +26 -0
  114. package/dist/theme/design-media.spec.d.ts +1 -0
  115. package/dist/theme/focus-ring.spec.d.ts +1 -0
  116. package/dist/theme/index.d.ts +4 -0
  117. package/dist/theme/tokens/semantic.d.ts +1 -1
  118. package/dist/theme/use-design-breakpoint.d.ts +7 -0
  119. package/dist/translations/en.d.ts +95 -0
  120. package/dist/translations/fr.d.ts +95 -0
  121. package/dist/utils/cloud-accept.utils.spec.d.ts +1 -0
  122. package/dist/utils/index.d.ts +1 -0
  123. package/dist/utils/quota-offer-state.utils.spec.d.ts +1 -0
  124. package/package.json +1 -1
  125. package/test-results/catalog/.last-run.json +4 -0
  126. package/dist/design-theme-CvLzOz3p.js +0 -9305
  127. /package/dist/{legacy/components/Inputs/CloudImport/cloud-accept.spec.d.ts → components/business/CloudImport/CloudImport.spec.d.ts} +0 -0
  128. /package/dist/{legacy/components/QuotaOffer/quota-offer-state.spec.d.ts → components/business/ImportFromCloudModal/ImportFromCloudModal.spec.d.ts} +0 -0
  129. /package/dist/{legacy/components/Inputs/CloudImport/cloud-accept.d.ts → utils/cloud-accept.utils.d.ts} +0 -0
  130. /package/dist/{legacy/components/QuotaOffer/quota-offer-state.d.ts → utils/quota-offer-state.utils.d.ts} +0 -0
@@ -1,7 +1,7 @@
1
1
  import { jsx as e, jsxs as a } from "react/jsx-runtime";
2
2
  import { useState as m, useEffect as C } from "react";
3
3
  import { Card as v, CardContent as b, Stack as u, Typography as o, FormControlLabel as l, Box as r, Checkbox as d, Button as M } from "@mui/material";
4
- import { g as R, u as x } from "./AbyssCookieConsentProvider-BhSzVu8P.js";
4
+ import { g as R, u as x } from "./AbyssCookieConsentProvider-B-PlGbFf.js";
5
5
  const B = () => {
6
6
  const t = R(), { consent: i, save: y } = x(), [h, g] = m((i == null ? void 0 : i.analytics) ?? !1), [k, f] = m((i == null ? void 0 : i.marketing) ?? !1), c = i == null ? void 0 : i.analytics, n = i == null ? void 0 : i.marketing;
7
7
  return C(() => {
@@ -0,0 +1,24 @@
1
+ import { IFile, ISpace } from '@abyss-project/cloud';
2
+ import { CloudPicker } from '../../../hooks/cloud-picker.types';
3
+ export interface CloudImportProps {
4
+ /** Picker state and actions, as returned by `useCloudPicker`. */
5
+ cloudPicker: CloudPicker;
6
+ /** Receives the Space the files were picked from, and the picked files. */
7
+ onImport(space: ISpace, files: IFile[]): void;
8
+ /** Several files can be picked; one otherwise. */
9
+ multiple?: boolean;
10
+ /** Files larger than this, in bytes, are not listed. */
11
+ maxSize?: number;
12
+ /**
13
+ * The files offered, as the HTML `accept` attribute lists them: MIME types (`application/pdf`),
14
+ * wildcards (`image/*`) and extensions (`.pdf`). Absent or empty: every file.
15
+ */
16
+ accept?: string[];
17
+ }
18
+ /**
19
+ * The AbyssCloud picker: the user picks a Space (personal, one group per organization, shared
20
+ * with them), opens its folders and picks files, then imports them. Mounting it provisions the
21
+ * Spaces (`cloudPicker.refresh`); a failed provisioning with nothing listed offers one « Créer mon
22
+ * cloud » retry. Files larger than `maxSize` or of a type `accept` refuses are not listed.
23
+ */
24
+ export declare function CloudImport({ cloudPicker, onImport, multiple, maxSize, accept, }: CloudImportProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,22 @@
1
+ import { IFile, IFolder } from '@abyss-project/cloud';
2
+ /** What decides whether a cloud file is offered, and how picked files are tracked. */
3
+ export interface CloudTreeContext {
4
+ multiple: boolean;
5
+ maxSize?: number;
6
+ accept?: string[];
7
+ selectedIds: ReadonlySet<string>;
8
+ getFileThumbnailUrl(fileId: string): string;
9
+ /** Adds the files to the selection (replaces it when not `multiple`). */
10
+ select(files: IFile[]): void;
11
+ unselect(files: IFile[]): void;
12
+ }
13
+ /** The key of a file in the selection. */
14
+ export declare const fileKey: (file: IFile) => string;
15
+ /** Whether a file is listed: within the size limit and of an accepted type. */
16
+ export declare const isOffered: (file: IFile, maxSize: number | undefined, accept: string[] | undefined) => boolean;
17
+ /** The list of a folder level: sub-folders first, then the files the picker offers. */
18
+ export declare function CloudTreeLevel({ folders, files, context, }: {
19
+ folders: IFolder[];
20
+ files: IFile[];
21
+ context: CloudTreeContext;
22
+ }): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,21 @@
1
+ import { IFile, ISpace } from '@abyss-project/cloud';
2
+ import { CloudPicker } from '../../../hooks/cloud-picker.types';
3
+ export interface ImportFromCloudModalProps {
4
+ open: boolean;
5
+ onClose(): void;
6
+ /** Receives the Space the files were picked from, and the files; the modal then closes. */
7
+ onImport(space: ISpace, files: IFile[]): void | Promise<void>;
8
+ /** Picker state and actions, as returned by `useCloudPicker`. */
9
+ cloudPicker: CloudPicker;
10
+ multiple?: boolean;
11
+ /** Files larger than this, in bytes, are not listed. */
12
+ maxSize?: number;
13
+ /** The files offered, as the HTML `accept` attribute lists them. Absent or empty: every file. */
14
+ accept?: string[];
15
+ }
16
+ /**
17
+ * The AbyssCloud picker (`CloudImport`) in a `Modal`: a bottom sheet below 768 px. Importing hands
18
+ * the files to `onImport` and closes the modal; the host runs the import and reports its outcome.
19
+ * The picker mounts on opening, which is what provisions the user's Spaces.
20
+ */
21
+ export declare function ImportFromCloudModal({ open, onClose, onImport, cloudPicker, multiple, maxSize, accept, }: ImportFromCloudModalProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,18 @@
1
+ export interface QRCodeModalProps {
2
+ open: boolean;
3
+ onClose(): void;
4
+ /** The link the QR code encodes. */
5
+ url: string;
6
+ /** Hides the « Partager » button. */
7
+ maskShareButton?: boolean;
8
+ /** Hides the link and its copy button. */
9
+ maskCopyButton?: boolean;
10
+ }
11
+ /** Side of the QR code image, in px: large enough for a phone camera across a desk. */
12
+ export declare const QR_CODE_SIZE = 200;
13
+ /**
14
+ * A link as a QR code, in a `Modal` (a bottom sheet below 768 px): the code on a light page, the
15
+ * link with its copy button (`CopyField`), and « Partager », which opens the device's share sheet
16
+ * or, without one, copies the link.
17
+ */
18
+ export declare function QRCodeModal({ open, onClose, url, maskShareButton, maskCopyButton, }: QRCodeModalProps): import("react/jsx-runtime").JSX.Element;
@@ -1 +1,4 @@
1
1
  export * from './InfectedDownloadModal/InfectedDownloadModal';
2
+ export * from './CloudImport/CloudImport';
3
+ export * from './ImportFromCloudModal/ImportFromCloudModal';
4
+ export * from './QRCodeModal/QRCodeModal';
@@ -29,6 +29,16 @@ export interface DataColumn<T> {
29
29
  mono?: boolean;
30
30
  /** `Default` when absent; an `Optional` column is hidden from 768 to 1199 px. */
31
31
  priority?: ColumnPriority;
32
+ /**
33
+ * The cells run over several lines instead of being cut by an ellipsis (words stay whole): for a
34
+ * label whose whole text matters. The other columns keep their single line.
35
+ */
36
+ wrap?: boolean;
37
+ /**
38
+ * The header is read by assistive technologies but not drawn: a narrow column of icon actions
39
+ * whose label would only show cut.
40
+ */
41
+ hideHeader?: boolean;
32
42
  }
33
43
  export interface DataTableSelection {
34
44
  selected: string[];
@@ -0,0 +1,31 @@
1
+ /** Shares of `max` (0.8 = 80 %) from which the meter turns to the warning, then danger color. */
2
+ export interface UsageMeterThresholds {
3
+ warning: number;
4
+ danger: number;
5
+ }
6
+ export interface UsageMeterProps {
7
+ /** The consumption, in the unit of `max`. */
8
+ value: number;
9
+ /** The ceiling. Without it, only the value is shown, no gauge. */
10
+ max?: number;
11
+ thresholds?: UsageMeterThresholds;
12
+ label: string;
13
+ /** Formats `value` and `max` for display (`intl.formatNumber`, bytes, credits). */
14
+ formatValue(value: number): string;
15
+ }
16
+ /** How full the meter is, against its thresholds. */
17
+ export declare enum UsageLevel {
18
+ Normal = "normal",
19
+ Warning = "warning",
20
+ Danger = "danger"
21
+ }
22
+ /** The level of a consumption share, against the thresholds (none: always normal). */
23
+ export declare const usageLevel: (share: number, thresholds?: UsageMeterThresholds) => UsageLevel;
24
+ /**
25
+ * A consumption against its ceiling (storage, credits, quota): the label, the value and the
26
+ * ceiling in mono, and a gauge filled with the accent, then the warning and danger colors once
27
+ * the share reaches `thresholds`. The gauge is a `meter` named by the label, its value text the
28
+ * formatted "value of max"; a consumption above the ceiling fills it whole. Without `max`, the
29
+ * value alone.
30
+ */
31
+ export declare function UsageMeter({ value, max, thresholds, label, formatValue, }: UsageMeterProps): import("react/jsx-runtime").JSX.Element;
@@ -3,3 +3,4 @@ export * from './KeyValueList/KeyValueList';
3
3
  export * from './StatTile/StatTile';
4
4
  export * from './Sparkline/Sparkline';
5
5
  export * from './chart-theme';
6
+ export * from './UsageMeter/UsageMeter';
@@ -0,0 +1,60 @@
1
+ import { ReactNode } from 'react';
2
+ /** The three scopes a context can take. */
3
+ export declare enum ContextKind {
4
+ Personal = "personal",
5
+ Organization = "organization",
6
+ Project = "project"
7
+ }
8
+ /** The selected context: the personal space, a whole organization, or one of its projects. */
9
+ export type ContextValue = {
10
+ kind: ContextKind.Personal;
11
+ } | {
12
+ kind: ContextKind.Organization;
13
+ organizationId: string;
14
+ } | {
15
+ kind: ContextKind.Project;
16
+ organizationId: string;
17
+ projectId: string;
18
+ };
19
+ /** A project of an organization. */
20
+ export interface ContextSelectorProject {
21
+ id: string;
22
+ name: string;
23
+ /** A `ProjectAvatar`, for instance; a folder icon without it. */
24
+ avatar?: ReactNode;
25
+ }
26
+ /** An organization the user belongs to, with its projects. */
27
+ export interface ContextSelectorOrganization {
28
+ id: string;
29
+ name: string;
30
+ /** An `OrganizationAvatar`, for instance; a building icon without it. */
31
+ avatar?: ReactNode;
32
+ projects?: ContextSelectorProject[];
33
+ }
34
+ /** The personal space. */
35
+ export interface ContextSelectorPersonal {
36
+ /** The user's alias, for instance; « Mon espace » without it. */
37
+ label?: string;
38
+ /** A `UserAvatar`, for instance; a person icon without it. */
39
+ avatar?: ReactNode;
40
+ }
41
+ export interface ContextSelectorData {
42
+ personal: ContextSelectorPersonal;
43
+ organizations: ContextSelectorOrganization[];
44
+ }
45
+ export interface ContextSelectorProps {
46
+ data: ContextSelectorData;
47
+ value: ContextValue;
48
+ onChange(next: ContextValue): void;
49
+ /** Label above the field, its accessible name. */
50
+ label: string;
51
+ disabled?: boolean;
52
+ }
53
+ /**
54
+ * The scope picker of a page: the personal space first, then one group per organization (headed
55
+ * by its name) holding an entry for the whole organization and one per project. The field shows
56
+ * the avatar of the selected context; typing filters the entries. Avatars are passed by the host
57
+ * (they need its storage URLs) and are hidden from assistive technology: the label names each
58
+ * entry.
59
+ */
60
+ export declare function ContextSelector({ data, value, onChange, label, disabled, }: ContextSelectorProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,19 @@
1
+ export interface CopyFieldProps {
2
+ value: string;
3
+ /** Hides the value behind dots until the user reveals it; the copy button still copies it. */
4
+ masked?: boolean;
5
+ /** Called each time the user reveals a masked value. */
6
+ onReveal?(): void;
7
+ /** Label of the copy button, named after what it copies ("Copier la clé"). */
8
+ copyLabel: string;
9
+ }
10
+ /** How long the copy button shows its check mark after a copy, in ms. */
11
+ export declare const COPY_FEEDBACK_MS = 2000;
12
+ /**
13
+ * A value to copy (API key, connection identifier): shown in mono in a read-only box, with a
14
+ * button that copies it to the clipboard. Masked, the value is replaced by dots and a toggle
15
+ * reveals it (calling `onReveal`). The outcome of a copy is announced in a polite live region; a
16
+ * success turns the copy icon into a check mark for a moment, a failure is also written under the
17
+ * field.
18
+ */
19
+ export declare function CopyField({ value, masked, onReveal, copyLabel, }: CopyFieldProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,30 @@
1
+ import { ReactNode } from 'react';
2
+ import { Accept } from 'react-dropzone';
3
+ import { CloudImportWiring } from '../../../hooks/cloud-picker.types';
4
+ export interface DropZoneProps {
5
+ /** The files dropped or picked from the device, once the size and type checks passed. */
6
+ onDrop(files: File[]): void;
7
+ /** Several files at once. Defaults to `true`. */
8
+ multiple?: boolean;
9
+ /** Accepted types, as `react-dropzone` reads them: `{ 'image/*': ['.png', '.jpg'] }`. */
10
+ accept?: Accept;
11
+ /** Largest accepted file, in bytes; shown under the zone. */
12
+ maxSize?: number;
13
+ /** Title above the zone, which names it. */
14
+ title?: string;
15
+ /**
16
+ * AbyssCloud picker (from `useCloudPicker`) plus the import action, which receives the Space
17
+ * the files were picked from. The AbyssCloud entry exists only when this is passed.
18
+ */
19
+ cloudImport?: CloudImportWiring;
20
+ /** Replaces the icon and the prompt inside the zone (a preview of the current file, say). */
21
+ children?: ReactNode;
22
+ }
23
+ /**
24
+ * A zone to drop files on, with a button that opens the device's file picker and, when
25
+ * `cloudImport` is wired, an « Importer depuis AbyssCloud » entry. Files larger than `maxSize` and
26
+ * types `accept` refuses are left out, and the reason is written under the zone (`role="alert"`);
27
+ * a single file that is too large is not handed over at all. Cloud files arrive through
28
+ * `cloudImport.onImport`, never through `onDrop`.
29
+ */
30
+ export declare function DropZone({ onDrop, multiple, accept, maxSize, title, cloudImport, children, }: DropZoneProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,29 @@
1
+ import { ReactElement, ReactNode } from 'react';
2
+ export interface FormFieldProps {
3
+ label: string;
4
+ /** A hint under the field, always shown. */
5
+ help?: ReactNode;
6
+ /** The error message: the field turns to the danger color and is marked invalid. */
7
+ error?: ReactNode;
8
+ required?: boolean;
9
+ /**
10
+ * One MUI control of the input family, without a label of its own: `OutlinedInput`,
11
+ * `InputBase`, or `Select`.
12
+ */
13
+ children: ReactElement<FieldControlProps>;
14
+ }
15
+ /** What the field gives its control so that the label, the hint and the error reach it. */
16
+ interface FieldControlProps {
17
+ id?: string;
18
+ 'aria-describedby'?: string;
19
+ labelId?: string;
20
+ }
21
+ /**
22
+ * A labelled form field (mock-up `.field`): the label above the control, a hint and an error
23
+ * under it. The control gets the `id` the label points to (its own `id` when it has one) and an
24
+ * `aria-describedby` listing the hint and the error; a `Select` also gets the `labelId` that
25
+ * names its listbox button. The error and `required` state reach the control through the MUI form
26
+ * control: red border, `aria-invalid`, the `required` attribute and the asterisk of the label.
27
+ */
28
+ export declare function FormField({ label, help, error, required, children, }: FormFieldProps): import("react/jsx-runtime").JSX.Element;
29
+ export {};
@@ -0,0 +1,33 @@
1
+ import { ChangeEvent, MouseEvent, ReactElement } from 'react';
2
+ import { CloudImportWiring } from '../../../hooks/cloud-picker.types';
3
+ /** What the trigger receives: it opens the menu of sources. */
4
+ interface TriggerProps {
5
+ onClick?(event: MouseEvent<HTMLElement>): void;
6
+ 'aria-haspopup'?: 'menu';
7
+ 'aria-expanded'?: boolean;
8
+ }
9
+ export interface ImportButtonProps {
10
+ /** The trigger, a button: it opens the menu of sources. */
11
+ children: ReactElement<TriggerProps>;
12
+ /** An import is running: the entries are disabled. */
13
+ isLoading: boolean;
14
+ /** « Mon ordinateur »: receives the change event of the file input. */
15
+ handleFileUpload(event: ChangeEvent<HTMLInputElement>): Promise<void>;
16
+ /** « Lien URL »: the entry exists only when this is passed. */
17
+ handleUploadFromUrl?(url: string): Promise<void>;
18
+ multiple?: boolean;
19
+ /** Largest file the AbyssCloud picker lists, in bytes. */
20
+ maxSize?: number;
21
+ /** Accepted types, as the HTML `accept` attribute lists them. */
22
+ accept?: string[];
23
+ /** AbyssCloud picker plus the import action: the « AbyssCloud » entry exists only when passed. */
24
+ cloudImport?: CloudImportWiring;
25
+ }
26
+ /**
27
+ * Wraps a trigger and opens a menu of upload sources: the device's files, a link (with
28
+ * `handleUploadFromUrl`) and AbyssCloud (with `cloudImport`). The link opens a small modal whose
29
+ * address is checked before it can be confirmed; it closes once the import succeeded and says so
30
+ * when it failed. AbyssCloud opens `ImportFromCloudModal`.
31
+ */
32
+ export declare function ImportButton({ children, isLoading, handleFileUpload, handleUploadFromUrl, multiple, maxSize, accept, cloudImport, }: ImportButtonProps): import("react/jsx-runtime").JSX.Element;
33
+ export {};
@@ -0,0 +1,21 @@
1
+ export interface NumberFieldProps {
2
+ label: string;
3
+ /** The number shown; `null` for an empty field. */
4
+ value: number | null;
5
+ /** Called with each number typed, `null` once emptied, and the clamped, rounded number on leave. */
6
+ onChange(value: number | null): void;
7
+ /** Digits kept after the decimal mark: 0 for a quantity (default), 2 for an amount. */
8
+ decimals?: number;
9
+ /** Lowest number kept when the field is left; 0 by default. */
10
+ min?: number;
11
+ disabled?: boolean;
12
+ /** Unit or currency shown at the end of the field (`€`, `Go`). */
13
+ adornment?: string;
14
+ }
15
+ /**
16
+ * A labelled number input that follows the language of the viewer: « 1 234,50 » in French,
17
+ * « 1,234.50 » in English. While focused it shows what is typed (either decimal mark is read);
18
+ * on leaving it clamps to `min`, rounds to `decimals` and formats again. A value changed from
19
+ * outside (a linked field) is shown at once unless the field is being edited.
20
+ */
21
+ export declare function NumberField({ label, value, onChange, decimals, min, disabled, adornment, }: NumberFieldProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,13 @@
1
+ /** The grouping and decimal separators of a locale. */
2
+ export interface LocaleSeparators {
3
+ group: string;
4
+ decimal: string;
5
+ }
6
+ /** The grouping and decimal separators of a locale, read from `Intl` (never hardcoded). */
7
+ export declare const getLocaleSeparators: (locale: string) => LocaleSeparators;
8
+ /**
9
+ * Reads a typed number: the grouping separators of the locale (and the usual whitespace) are
10
+ * dropped, then « , » or « . » is the decimal mark, so « 50,5 » and « 50.5 » give the same number.
11
+ * Empty or non-numeric text gives `null`.
12
+ */
13
+ export declare const parseNumberInput: (raw: string, separators: LocaleSeparators) => number | null;
@@ -0,0 +1,16 @@
1
+ import { ReactNode } from 'react';
2
+ export interface SettingsRowProps {
3
+ label: string;
4
+ description?: ReactNode;
5
+ /** The control of the setting: a switch, a select, a button. */
6
+ control: ReactNode;
7
+ /** `id` of the form element inside `control`: the label then labels it (`<label for>`). */
8
+ htmlFor?: string;
9
+ }
10
+ /**
11
+ * One setting of a settings list: its label and description on the left, its
12
+ * control on the right, rows split by a 1 px rule. Below 768 px the control goes under the text.
13
+ * The row is a `group` named by the label and described by the description; with `htmlFor` the
14
+ * label also labels the form element of the control.
15
+ */
16
+ export declare function SettingsRow({ label, description, control, htmlFor, }: SettingsRowProps): import("react/jsx-runtime").JSX.Element;
@@ -3,3 +3,10 @@ export * from './DateField/DateField';
3
3
  export * from './DateRangeField/DateRangeField';
4
4
  export * from './ColorField/ColorField';
5
5
  export * from './SegmentedControl/SegmentedControl';
6
+ export * from './FormField/FormField';
7
+ export * from './NumberField/NumberField';
8
+ export * from './SettingsRow/SettingsRow';
9
+ export * from './CopyField/CopyField';
10
+ export * from './ContextSelector/ContextSelector';
11
+ export * from './DropZone/DropZone';
12
+ export * from './ImportButton/ImportButton';
@@ -1,28 +1,49 @@
1
1
  import { ReactNode } from 'react';
2
- import { Command } from './command-registry';
3
- export type { Command } from './command-registry';
2
+ import { Command, CommandSource } from './command-registry';
3
+ export type { Command, CommandSource } from './command-registry';
4
+ /** Delay between the last keystroke and the call of the command sources. */
5
+ export declare const COMMAND_SOURCE_DEBOUNCE_MS = 200;
6
+ export interface CommandPaletteOpenOptions {
7
+ /** Lists only the commands of this group; the filter shows as a chip the user can remove. */
8
+ group?: string;
9
+ }
4
10
  export interface CommandPaletteApi {
5
- /** Opens the palette, as `Ctrl+K` / `Cmd+K` does. */
6
- open(): void;
11
+ /** Whether the palette is open: a host fetches the lists it registers only while it is. */
12
+ isOpen: boolean;
13
+ /** Opens the palette, as `Ctrl+K` / `Cmd+K` does, filtered on `options.group` when given. */
14
+ open(options?: CommandPaletteOpenOptions): void;
7
15
  /**
8
16
  * Runs the registered command `id` (closing the palette first if it is open) and returns `true`;
9
17
  * returns `false` when no mounted component registers that command.
10
18
  */
11
19
  execute(id: string): boolean;
12
20
  }
21
+ export interface CommandPaletteRecents {
22
+ /** `localStorage` key the identifiers of the last executed commands are kept under. */
23
+ storageKey: string;
24
+ /** Number of recent commands kept and listed; 5 by default. */
25
+ max?: number;
26
+ }
13
27
  export interface CommandPaletteProviderProps {
14
28
  children: ReactNode;
29
+ /** Lists the last executed commands first, under « Récents », while the search is empty. */
30
+ recents?: CommandPaletteRecents;
15
31
  }
16
32
  /**
17
33
  * Holds the commands the mounted screens register and the palette that lists them. `Ctrl+K` /
18
34
  * `Cmd+K` opens it anywhere in the page; `useCommandPalette().execute(id)` runs a command by its
19
35
  * identifier, for the voice and MCP clients.
20
36
  */
21
- export declare function CommandPaletteProvider({ children, }: CommandPaletteProviderProps): import("react/jsx-runtime").JSX.Element;
37
+ export declare function CommandPaletteProvider({ children, recents, }: CommandPaletteProviderProps): import("react/jsx-runtime").JSX.Element;
22
38
  /**
23
39
  * Registers `commands` while the calling component is mounted. Pass a memoized array: a new array
24
40
  * replaces the previous one at each render.
25
41
  */
26
42
  export declare const useRegisterCommands: (commands: Command[]) => void;
27
- /** Opens the palette and runs commands by identifier. */
43
+ /**
44
+ * Registers `source` while the calling component is mounted. Memoize `search`: a new function is a
45
+ * new source, which drops its answers and searches again.
46
+ */
47
+ export declare const useRegisterCommandSource: (source: CommandSource) => void;
48
+ /** Opens the palette, tells whether it is open and runs commands by identifier. */
28
49
  export declare const useCommandPalette: () => CommandPaletteApi;
@@ -1,3 +1,4 @@
1
+ import { ReactNode } from 'react';
1
2
  /** An action of the application, reachable from the palette and by its identifier. */
2
3
  export interface Command {
3
4
  /** Stable identifier: the address the voice and MCP clients use to run the action. */
@@ -5,6 +6,10 @@ export interface Command {
5
6
  label: string;
6
7
  /** Heading the command is listed under in the palette. */
7
8
  group: string;
9
+ /** Context shown muted under the label, on one line; searched like the label. */
10
+ subtitle?: string;
11
+ /** Decorative mark shown before the label. */
12
+ icon?: ReactNode;
8
13
  /** Extra words the search matches, such as synonyms. */
9
14
  keywords?: string[];
10
15
  /** Shortcut hint shown next to the command; the palette binds no key for it. */
@@ -12,19 +17,36 @@ export interface Command {
12
17
  run(): void;
13
18
  }
14
19
  /**
15
- * The commands every mounted `useRegisterCommands` call contributes, as an external store. A
16
- * registration is replaced in place when its commands change, so the listing order stays the
17
- * mount order; when two registrations share an identifier, the most recent command wins.
20
+ * Commands found by a search the palette cannot run itself, such as a server-side search. The
21
+ * palette calls `search` while it is open, once the query is long enough, and lists the results
22
+ * under `group`.
18
23
  */
19
- export declare class CommandRegistry {
24
+ export interface CommandSource {
25
+ /** Stable identifier of the source. */
26
+ id: string;
27
+ /** Heading the results are listed under; a group filter matches it. */
28
+ group: string;
29
+ /** Resolves the commands matching `query`; `signal` aborts when the query changes or the palette closes. */
30
+ search(query: string, signal: AbortSignal): Promise<Command[]>;
31
+ /** Number of characters of the trimmed query below which the source is not called; 1 by default. */
32
+ minQueryLength?: number;
33
+ }
34
+ /**
35
+ * The items every mounted registration contributes, as an external store. A registration is
36
+ * replaced in place when its items change, so the listing order stays the mount order; when two
37
+ * registrations share an identifier, the most recent item wins.
38
+ */
39
+ export declare class KeyedRegistry<Item extends {
40
+ id: string;
41
+ }> {
20
42
  private readonly registrations;
21
43
  private readonly listeners;
22
44
  private snapshot;
23
- register(key: symbol, commands: Command[]): void;
45
+ register(key: symbol, items: Item[]): void;
24
46
  unregister(key: symbol): void;
25
- find(id: string): Command | undefined;
47
+ find(id: string): Item | undefined;
26
48
  readonly subscribe: (listener: () => void) => (() => void);
27
- readonly getSnapshot: () => Command[];
49
+ readonly getSnapshot: () => Item[];
28
50
  private refresh;
29
51
  }
30
52
  export interface CommandGroup {
@@ -32,7 +54,15 @@ export interface CommandGroup {
32
54
  commands: Command[];
33
55
  }
34
56
  /**
35
- * The commands whose label, group or keywords contain every word of `query` (case and accents
36
- * ignored), grouped in the order their group first appears.
57
+ * The commands whose label, subtitle, group or keywords contain every word of `query` (case and
58
+ * accents ignored), grouped in the order their group first appears.
37
59
  */
38
60
  export declare const searchCommands: (commands: Command[], query: string) => CommandGroup[];
61
+ /** Number of recent commands kept when the host sets no maximum. */
62
+ export declare const DEFAULT_RECENTS_MAX = 5;
63
+ /** The identifiers stored under `storageKey`, most recent first; none when storage is unreadable. */
64
+ export declare const readRecents: (storageKey: string) => string[];
65
+ /** Stores `ids` under `storageKey`; when storage refuses, the recents live for the page only. */
66
+ export declare const writeRecents: (storageKey: string, ids: string[]) => void;
67
+ /** `ids` with `id` moved to the front, at most `max` long. */
68
+ export declare const pushRecent: (ids: string[], id: string, max: number) => string[];
@@ -10,6 +10,8 @@ export declare enum MenuSeparator {
10
10
  export interface MenuItem {
11
11
  id: string;
12
12
  label: string;
13
+ /** Secondary line under the label; it describes the item (`aria-describedby`) without renaming it. */
14
+ description?: string;
13
15
  icon?: ReactNode;
14
16
  /** Shortcut hint shown at the end of the row, such as `⌘D`; the host binds the key itself. */
15
17
  shortcut?: string;
@@ -18,6 +18,7 @@ export interface ModalProps {
18
18
  /**
19
19
  * A modal dialog (`.modal`): header with title, description and close button, scrolling body,
20
20
  * footer of actions. Focus is trapped inside and returns to the opener on close; Escape and the
21
- * scrim close it. Below the `md` breakpoint it is presented as a `BottomSheet`.
21
+ * scrim close it. Below 768 px (the design system's tablet tier, whatever the theme) it is presented
22
+ * as a `BottomSheet`.
22
23
  */
23
24
  export declare function Modal({ open, onClose, title, description, size, actions, children, }: ModalProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,41 @@
1
+ /** Side of an avatar when the host gives none, in px. */
2
+ export declare const AVATAR_DEFAULT_SIZE = 32;
3
+ export interface UserAvatarProps {
4
+ userId: string;
5
+ /** The user's alias: accessible name, and its initial while there is no picture. */
6
+ alias: string;
7
+ /** Whether the user has uploaded a picture; without one, no request is made. */
8
+ hasThumbnail: boolean;
9
+ /** Changes when the picture does (a timestamp after an upload): appended to the URL. */
10
+ cache?: string;
11
+ /** Side in px. Defaults to `AVATAR_DEFAULT_SIZE`. */
12
+ size?: number;
13
+ /** URL of a user's picture, from the host's storage helpers. */
14
+ getProfilePictureUrl(userId: string): string;
15
+ }
16
+ /** A person: their picture in a disc, or the initial of their alias. */
17
+ export declare function UserAvatar({ userId, alias, hasThumbnail, cache, size, getProfilePictureUrl, }: UserAvatarProps): import("react/jsx-runtime").JSX.Element;
18
+ export interface OrganizationAvatarProps {
19
+ orgId: string;
20
+ /** The organization's name: accessible name, and its initial while there is no logo. */
21
+ name: string;
22
+ hasThumbnail: boolean;
23
+ cache?: string;
24
+ size?: number;
25
+ /** URL of an organization's logo, from the host's storage helpers. */
26
+ getLogoUrl(orgId: string): string;
27
+ }
28
+ /** An organization: its logo in a rounded square, or the initial of its name. */
29
+ export declare function OrganizationAvatar({ orgId, name, hasThumbnail, cache, size, getLogoUrl, }: OrganizationAvatarProps): import("react/jsx-runtime").JSX.Element;
30
+ export interface ProjectAvatarProps {
31
+ projectId: string;
32
+ /** The project's title: accessible name, and its initial while there is no picture. */
33
+ title: string;
34
+ hasThumbnail: boolean;
35
+ cache?: string;
36
+ size?: number;
37
+ /** URL of a project's picture, from the host's storage helpers. */
38
+ getProjectPictureUrl(projectId: string): string;
39
+ }
40
+ /** A project: its picture in a rounded square, or the initial of its title. */
41
+ export declare function ProjectAvatar({ projectId, title, hasThumbnail, cache, size, getProjectPictureUrl, }: ProjectAvatarProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1 @@
1
+ export {};