@egen-civitas/esm-extensions 2.2.0 → 2.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @egen-civitas/esm-extensions
2
2
 
3
+ ## 2.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 0afcb05: `StaggeredMenuPanel` accepte des composants libres posés par les apps, comme les rails. Nouveau slot `staggered-menu-slot` (`STAGGERED_MENU_SLOT`) : une extension y déclare dans sa `meta` sa zone (`top`, `content`, `bottom`), son `order`, sa `priority`, `global` (visible quelle que soit l'app active ; sinon seulement quand son app est montée), `showTitle`, `titleKey`. Nouveaux exports : `registerStaggeredMenuEntry` (déclaration par le code), `resolveStaggeredMenuEntries`, `isStaggeredMenuEntryMeta`, `getStaggeredMenuEntryProblems`, `useStaggeredMenuEntries` et les types associés. Nouvelles props du panneau : `extensionSlot` (slot dédié ou `false`) et `maxExtensions` (limite par priorité). Le contenu est chargé à la première ouverture ; chaque composant reçoit `closeMenu`, `position` et `isOpen` en props ; un clic dans un menu/dialogue ouvert par un composant ne ferme plus le panneau (`closeOnClickAway`) ; le panneau fermé est `inert`. Aucun changement pour les usages existants.
8
+
3
9
  ## 2.2.0
4
10
 
5
11
  ### Minor Changes
package/dist/index.d.ts CHANGED
@@ -3,6 +3,8 @@ export * from './helpers.js';
3
3
  export * from './nav-entry.js';
4
4
  export * from './tool-window.js';
5
5
  export * from './tool-window-registration.js';
6
+ export * from './staggered-menu-slot.js';
7
+ export * from './staggered-menu-registration.js';
6
8
  export * from './nav-behavior.js';
7
9
  export * from './modals.js';
8
10
  export * from './workspaces.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,cAAc,CAAC;AAC7B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC;AACjC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,mBAAmB,CAAC;AAClC,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC;AACjC,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,cAAc,CAAC;AAC7B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC;AACjC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,0BAA0B,CAAC;AACzC,cAAc,kCAAkC,CAAC;AACjD,cAAc,mBAAmB,CAAC;AAClC,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC;AACjC,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC"}
package/dist/index.js CHANGED
@@ -3,6 +3,8 @@ export * from "./helpers.js";
3
3
  export * from "./nav-entry.js";
4
4
  export * from "./tool-window.js";
5
5
  export * from "./tool-window-registration.js";
6
+ export * from "./staggered-menu-slot.js";
7
+ export * from "./staggered-menu-registration.js";
6
8
  export * from "./nav-behavior.js";
7
9
  export * from "./modals.js";
8
10
  export * from "./workspaces.js";
package/dist/public.d.ts CHANGED
@@ -6,5 +6,7 @@ export { type ExtensionData, type ComponentConfig } from './types.js';
6
6
  export { TOPBAR_LEVEL2_NAV_SLOT, NAV_ALL_WORKSPACES, type NavEntryMeta, isNavEntryMeta, isNavEntryInWorkspace, } from './nav-entry.js';
7
7
  export { SHELL_RAIL_LEFT_SLOT, SHELL_RAIL_BOTTOM_SLOT, SHELL_RAIL_RIGHT_SLOT, TOOL_WINDOW_SLOTS, type ToolWindowAnchor, type ToolWindowMeta, isToolWindowMeta, isToolWindowSlot, isSafeToolWindowIconUrl, getToolWindowMetaProblems, getToolWindowCompartment, DEFAULT_COMPARTMENT_HEIGHTS, type ToolWindowCompartment, type ResolvedToolWindowCompartment, } from './tool-window.js';
8
8
  export { registerToolWindow, type ToolWindowRegistration } from './tool-window-registration.js';
9
+ export { STAGGERED_MENU_SLOT, STAGGERED_MENU_AREAS, DEFAULT_STAGGERED_MENU_AREA, type StaggeredMenuArea, type StaggeredMenuEntryMeta, type StaggeredMenuAssignedExtension, type ResolvedStaggeredMenuEntry, type ResolveStaggeredMenuEntriesOptions, isStaggeredMenuArea, isStaggeredMenuEntryMeta, getStaggeredMenuEntryProblems, resolveStaggeredMenuEntries, } from './staggered-menu-slot.js';
10
+ export { registerStaggeredMenuEntry, type StaggeredMenuEntryRegistration } from './staggered-menu-registration.js';
9
11
  export { type NavBehavior, type NavBehaviorTarget, type NavSelectContext, registerNavBehavior, getNavBehavior, } from './nav-behavior.js';
10
12
  //# sourceMappingURL=public.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../src/public.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EACtB,iBAAiB,EACjB,MAAM,EACN,MAAM,EACN,SAAS,EACT,qBAAqB,EACrB,qBAAqB,GACtB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,KAAK,aAAa,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAClE,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,qBAAqB,EAC1B,KAAK,cAAc,EACnB,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACvB,iBAAiB,GAClB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,KAAK,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAC7D,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;AACtE,OAAO,EACL,sBAAsB,EACtB,kBAAkB,EAClB,KAAK,YAAY,EACjB,cAAc,EACd,qBAAqB,GACtB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,oBAAoB,EACpB,sBAAsB,EACtB,qBAAqB,EACrB,iBAAiB,EACjB,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,gBAAgB,EAChB,gBAAgB,EAChB,uBAAuB,EACvB,yBAAyB,EACzB,wBAAwB,EACxB,2BAA2B,EAC3B,KAAK,qBAAqB,EAC1B,KAAK,6BAA6B,GACnC,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,kBAAkB,EAAE,KAAK,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AAChG,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,mBAAmB,EACnB,cAAc,GACf,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../src/public.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EACtB,iBAAiB,EACjB,MAAM,EACN,MAAM,EACN,SAAS,EACT,qBAAqB,EACrB,qBAAqB,GACtB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,KAAK,aAAa,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAClE,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,qBAAqB,EAC1B,KAAK,cAAc,EACnB,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACvB,iBAAiB,GAClB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,KAAK,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAC7D,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;AACtE,OAAO,EACL,sBAAsB,EACtB,kBAAkB,EAClB,KAAK,YAAY,EACjB,cAAc,EACd,qBAAqB,GACtB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,oBAAoB,EACpB,sBAAsB,EACtB,qBAAqB,EACrB,iBAAiB,EACjB,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,gBAAgB,EAChB,gBAAgB,EAChB,uBAAuB,EACvB,yBAAyB,EACzB,wBAAwB,EACxB,2BAA2B,EAC3B,KAAK,qBAAqB,EAC1B,KAAK,6BAA6B,GACnC,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,kBAAkB,EAAE,KAAK,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AAChG,OAAO,EACL,mBAAmB,EACnB,oBAAoB,EACpB,2BAA2B,EAC3B,KAAK,iBAAiB,EACtB,KAAK,sBAAsB,EAC3B,KAAK,8BAA8B,EACnC,KAAK,0BAA0B,EAC/B,KAAK,kCAAkC,EACvC,mBAAmB,EACnB,wBAAwB,EACxB,6BAA6B,EAC7B,2BAA2B,GAC5B,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,0BAA0B,EAAE,KAAK,8BAA8B,EAAE,MAAM,kCAAkC,CAAC;AACnH,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,mBAAmB,EACnB,cAAc,GACf,MAAM,mBAAmB,CAAC"}
package/dist/public.js CHANGED
@@ -4,4 +4,6 @@ export { getExtensionStore } from "./store.js";
4
4
  export { TOPBAR_LEVEL2_NAV_SLOT, NAV_ALL_WORKSPACES, isNavEntryMeta, isNavEntryInWorkspace } from "./nav-entry.js";
5
5
  export { SHELL_RAIL_LEFT_SLOT, SHELL_RAIL_BOTTOM_SLOT, SHELL_RAIL_RIGHT_SLOT, TOOL_WINDOW_SLOTS, isToolWindowMeta, isToolWindowSlot, isSafeToolWindowIconUrl, getToolWindowMetaProblems, getToolWindowCompartment, DEFAULT_COMPARTMENT_HEIGHTS } from "./tool-window.js";
6
6
  export { registerToolWindow } from "./tool-window-registration.js";
7
+ export { STAGGERED_MENU_SLOT, STAGGERED_MENU_AREAS, DEFAULT_STAGGERED_MENU_AREA, isStaggeredMenuArea, isStaggeredMenuEntryMeta, getStaggeredMenuEntryProblems, resolveStaggeredMenuEntries } from "./staggered-menu-slot.js";
8
+ export { registerStaggeredMenuEntry } from "./staggered-menu-registration.js";
7
9
  export { registerNavBehavior, getNavBehavior } from "./nav-behavior.js";
@@ -0,0 +1,27 @@
1
+ import type { ExtensionRegistration } from './store.js';
2
+ import { type StaggeredMenuEntryMeta } from './staggered-menu-slot.js';
3
+ export interface StaggeredMenuEntryRegistration {
4
+ /** Nom du module (de l'app) qui porte le composant : celui de l'importmap / de la fédération. */
5
+ moduleName: string;
6
+ /** Identifiant unique de l'entrée (nom de l'extension). */
7
+ name: string;
8
+ /** Zone, ordre, priorité et portée de l'entrée. */
9
+ meta: StaggeredMenuEntryMeta;
10
+ /** Chargement du composant (cycles de vie single-spa), appelé quand le panneau s'ouvre pour la première fois. */
11
+ load: ExtensionRegistration['load'];
12
+ /**
13
+ * Slot visé. Défaut : `STAGGERED_MENU_SLOT`. À renseigner pour viser un panneau qui a son propre slot
14
+ * (prop `extensionSlot` de `StaggeredMenuPanel`).
15
+ */
16
+ slot?: string;
17
+ privileges?: ExtensionRegistration['privileges'];
18
+ featureFlag?: string;
19
+ displayExpression?: string;
20
+ }
21
+ /**
22
+ * Ajoute un composant libre au `StaggeredMenuPanel` par le code, sans `routes.json` : pour une app fédérée
23
+ * ou open source qui s'intègre depuis son point d'entrée. Équivalent exact d'une extension `routes.json`
24
+ * posée sur le slot du panneau. Retourne `false` (et avertit) si la `meta` est invalide.
25
+ */
26
+ export declare function registerStaggeredMenuEntry(registration: StaggeredMenuEntryRegistration): boolean;
27
+ //# sourceMappingURL=staggered-menu-registration.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"staggered-menu-registration.d.ts","sourceRoot":"","sources":["../src/staggered-menu-registration.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AACxD,OAAO,EAGL,KAAK,sBAAsB,EAC5B,MAAM,0BAA0B,CAAC;AAElC,MAAM,WAAW,8BAA8B;IAC7C,iGAAiG;IACjG,UAAU,EAAE,MAAM,CAAC;IACnB,2DAA2D;IAC3D,IAAI,EAAE,MAAM,CAAC;IACb,mDAAmD;IACnD,IAAI,EAAE,sBAAsB,CAAC;IAC7B,iHAAiH;IACjH,IAAI,EAAE,qBAAqB,CAAC,MAAM,CAAC,CAAC;IACpC;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,qBAAqB,CAAC,YAAY,CAAC,CAAC;IACjD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CAAC,YAAY,EAAE,8BAA8B,GAAG,OAAO,CAiChG"}
@@ -0,0 +1,27 @@
1
+ /** @module @category Extension */ import { attach, registerExtension } from "./extensions.js";
2
+ import { getStaggeredMenuEntryProblems, STAGGERED_MENU_SLOT } from "./staggered-menu-slot.js";
3
+ /**
4
+ * Ajoute un composant libre au `StaggeredMenuPanel` par le code, sans `routes.json` : pour une app fédérée
5
+ * ou open source qui s'intègre depuis son point d'entrée. Équivalent exact d'une extension `routes.json`
6
+ * posée sur le slot du panneau. Retourne `false` (et avertit) si la `meta` est invalide.
7
+ */ export function registerStaggeredMenuEntry(registration) {
8
+ const { moduleName, name, meta, load, slot = STAGGERED_MENU_SLOT, privileges, featureFlag, displayExpression } = registration;
9
+ const problems = getStaggeredMenuEntryProblems(meta);
10
+ if (problems.length > 0 || !slot) {
11
+ console.warn(`[staggered-menu] L'entrée « ${name} » de ${moduleName} n'est pas enregistrée : ${problems.join(' ; ') || 'slot vide'}.`);
12
+ return false;
13
+ }
14
+ registerExtension({
15
+ name,
16
+ moduleName,
17
+ load,
18
+ meta,
19
+ privileges,
20
+ featureFlag,
21
+ displayExpression,
22
+ online: true,
23
+ offline: false
24
+ });
25
+ attach(slot, name);
26
+ return true;
27
+ }
@@ -0,0 +1,99 @@
1
+ /** @module @category Extension */
2
+ /**
3
+ * Extensions du `StaggeredMenuPanel`.
4
+ *
5
+ * Le panneau latéral animé du styleguide accepte des composants libres posés par les apps, de la même
6
+ * façon que les rails du shell (voir `tool-window.ts`) : une app déclare une **extension** sur le slot
7
+ * `staggered-menu-slot` et indique, dans sa `meta`, où, dans quel ordre et avec quelle priorité elle
8
+ * s'affiche.
9
+ *
10
+ * Déclaration dans le `routes.json` d'une app :
11
+ * `{ "slot": "staggered-menu-slot", "component": "monWidget", "meta": StaggeredMenuEntryMeta }`.
12
+ *
13
+ * Comme pour les rails, une entrée n'apparaît que lorsque son app est montée (une de ses pages est
14
+ * active), sauf si elle déclare `meta.global: true`.
15
+ */
16
+ export declare const STAGGERED_MENU_SLOT = "staggered-menu-slot";
17
+ /**
18
+ * Zones du panneau qui accueillent des extensions, dans l'ordre d'affichage :
19
+ *
20
+ * `top` avant la liste des liens de navigation
21
+ * `content` juste après la liste des liens (zone par défaut)
22
+ * `bottom` en pied de panneau, après le bloc de liens sociaux
23
+ */
24
+ export declare const STAGGERED_MENU_AREAS: readonly ["top", "content", "bottom"];
25
+ export type StaggeredMenuArea = (typeof STAGGERED_MENU_AREAS)[number];
26
+ /** Zone utilisée quand `meta.area` est absent. */
27
+ export declare const DEFAULT_STAGGERED_MENU_AREA: StaggeredMenuArea;
28
+ /** Métadonnées d'une extension du panneau (titre accessible, zone, ordre, priorité, portée). */
29
+ export interface StaggeredMenuEntryMeta {
30
+ /** Titre : nom accessible de la section et, avec `showTitle`, intitulé visible au-dessus du composant. */
31
+ title: string;
32
+ /** Clé de traduction du titre (optionnelle), cherchée dans le module de l'app. */
33
+ titleKey?: string;
34
+ /** Zone du panneau où s'affiche le composant (défaut `content`). */
35
+ area?: StaggeredMenuArea;
36
+ /**
37
+ * Position dans la zone (ordre croissant). Absent : ordre d'enregistrement du slot. Comme pour les rails,
38
+ * deux entrées de même `order` sont départagées par `priority` puis par l'ordre d'enregistrement.
39
+ */
40
+ order?: number;
41
+ /**
42
+ * Importance de l'entrée (défaut 0, plus grand = plus important). Sert (1) à départager deux entrées de
43
+ * même `order` : la plus prioritaire passe devant, et (2) à choisir les entrées conservées quand le panneau
44
+ * limite leur nombre (`maxExtensions`) : les moins prioritaires sont écartées.
45
+ */
46
+ priority?: number;
47
+ /**
48
+ * L'entrée reste affichée quelle que soit l'app active (comme `global` des rails). Absent : elle n'apparaît
49
+ * que lorsqu'une page de son app est montée.
50
+ */
51
+ global?: boolean;
52
+ /** Affiche `title` comme intitulé visible au-dessus du composant (défaut : non, le composant est libre). */
53
+ showTitle?: boolean;
54
+ }
55
+ /** Vrai si `area` est une zone connue du panneau. */
56
+ export declare function isStaggeredMenuArea(area: unknown): area is StaggeredMenuArea;
57
+ /**
58
+ * Liste précise des problèmes d'une `meta` d'extension du panneau (vide = valide). Une entrée invalide
59
+ * n'apparaît pas : le dire clairement à l'enregistrement évite de chercher pourquoi à l'intégration d'une app.
60
+ */
61
+ export declare function getStaggeredMenuEntryProblems(meta: unknown): string[];
62
+ /** Valide une `meta` d'extension : vrai si c'est une `StaggeredMenuEntryMeta` exploitable. */
63
+ export declare function isStaggeredMenuEntryMeta(meta: unknown): meta is StaggeredMenuEntryMeta;
64
+ /** Forme minimale d'une extension assignée au slot (compatible avec `AssignedExtension`). */
65
+ export interface StaggeredMenuAssignedExtension {
66
+ name: string;
67
+ moduleName: string;
68
+ meta?: unknown;
69
+ }
70
+ /** Entrée résolue : une extension valide, visible, avec sa zone, son ordre et sa priorité calculés. */
71
+ export interface ResolvedStaggeredMenuEntry<T extends StaggeredMenuAssignedExtension = StaggeredMenuAssignedExtension> {
72
+ /** Nom de l'extension : identifiant de l'entrée dans le panneau. */
73
+ id: string;
74
+ moduleName: string;
75
+ meta: StaggeredMenuEntryMeta;
76
+ area: StaggeredMenuArea;
77
+ order: number;
78
+ priority: number;
79
+ /** Extension d'origine (pour la retrouver dans le slot). */
80
+ extension: T;
81
+ }
82
+ export interface ResolveStaggeredMenuEntriesOptions {
83
+ /**
84
+ * Nombre maximal d'entrées affichées. Au-delà, les moins prioritaires sont écartées (à priorité égale,
85
+ * les premières dans l'ordre d'affichage sont conservées). Absent ou invalide : aucune limite.
86
+ */
87
+ maxEntries?: number;
88
+ }
89
+ /**
90
+ * Entrées affichables du panneau, dans l'ordre d'affichage.
91
+ *
92
+ * 1. écarte les entrées dont la `meta` est invalide ;
93
+ * 2. écarte celles dont l'app n'est pas montée, sauf `meta.global` ;
94
+ * 3. si `maxEntries` est fixé, ne garde que les plus prioritaires ;
95
+ * 4. trie par zone (`top`, `content`, `bottom`), puis `order` croissant, puis `priority` décroissante,
96
+ * puis ordre d'enregistrement.
97
+ */
98
+ export declare function resolveStaggeredMenuEntries<T extends StaggeredMenuAssignedExtension>(assigned: readonly T[], activeModules: ReadonlySet<string>, options?: ResolveStaggeredMenuEntriesOptions): ResolvedStaggeredMenuEntry<T>[];
99
+ //# sourceMappingURL=staggered-menu-slot.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"staggered-menu-slot.d.ts","sourceRoot":"","sources":["../src/staggered-menu-slot.ts"],"names":[],"mappings":"AAAA,kCAAkC;AAElC;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,mBAAmB,wBAAwB,CAAC;AAEzD;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,uCAAwC,CAAC;AAE1E,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEtE,kDAAkD;AAClD,eAAO,MAAM,2BAA2B,EAAE,iBAA6B,CAAC;AAExE,gGAAgG;AAChG,MAAM,WAAW,sBAAsB;IACrC,0GAA0G;IAC1G,KAAK,EAAE,MAAM,CAAC;IACd,kFAAkF;IAClF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oEAAoE;IACpE,IAAI,CAAC,EAAE,iBAAiB,CAAC;IACzB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,4GAA4G;IAC5G,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,qDAAqD;AACrD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,IAAI,iBAAiB,CAE5E;AAED;;;GAGG;AACH,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,EAAE,CAerE;AAED,8FAA8F;AAC9F,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,IAAI,sBAAsB,CAEtF;AAED,6FAA6F;AAC7F,MAAM,WAAW,8BAA8B;IAC7C,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,uGAAuG;AACvG,MAAM,WAAW,0BAA0B,CAAC,CAAC,SAAS,8BAA8B,GAAG,8BAA8B;IACnH,oEAAoE;IACpE,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,sBAAsB,CAAC;IAC7B,IAAI,EAAE,iBAAiB,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,4DAA4D;IAC5D,SAAS,EAAE,CAAC,CAAC;CACd;AAED,MAAM,WAAW,kCAAkC;IACjD;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;GAQG;AACH,wBAAgB,2BAA2B,CAAC,CAAC,SAAS,8BAA8B,EAClF,QAAQ,EAAE,SAAS,CAAC,EAAE,EACtB,aAAa,EAAE,WAAW,CAAC,MAAM,CAAC,EAClC,OAAO,GAAE,kCAAuC,GAC/C,0BAA0B,CAAC,CAAC,CAAC,EAAE,CAgDjC"}
@@ -0,0 +1,93 @@
1
+ /** @module @category Extension */ /**
2
+ * Extensions du `StaggeredMenuPanel`.
3
+ *
4
+ * Le panneau latéral animé du styleguide accepte des composants libres posés par les apps, de la même
5
+ * façon que les rails du shell (voir `tool-window.ts`) : une app déclare une **extension** sur le slot
6
+ * `staggered-menu-slot` et indique, dans sa `meta`, où, dans quel ordre et avec quelle priorité elle
7
+ * s'affiche.
8
+ *
9
+ * Déclaration dans le `routes.json` d'une app :
10
+ * `{ "slot": "staggered-menu-slot", "component": "monWidget", "meta": StaggeredMenuEntryMeta }`.
11
+ *
12
+ * Comme pour les rails, une entrée n'apparaît que lorsque son app est montée (une de ses pages est
13
+ * active), sauf si elle déclare `meta.global: true`.
14
+ */ export const STAGGERED_MENU_SLOT = 'staggered-menu-slot';
15
+ /**
16
+ * Zones du panneau qui accueillent des extensions, dans l'ordre d'affichage :
17
+ *
18
+ * `top` avant la liste des liens de navigation
19
+ * `content` juste après la liste des liens (zone par défaut)
20
+ * `bottom` en pied de panneau, après le bloc de liens sociaux
21
+ */ export const STAGGERED_MENU_AREAS = [
22
+ 'top',
23
+ 'content',
24
+ 'bottom'
25
+ ];
26
+ /** Zone utilisée quand `meta.area` est absent. */ export const DEFAULT_STAGGERED_MENU_AREA = 'content';
27
+ /** Vrai si `area` est une zone connue du panneau. */ export function isStaggeredMenuArea(area) {
28
+ return typeof area === 'string' && STAGGERED_MENU_AREAS.includes(area);
29
+ }
30
+ /**
31
+ * Liste précise des problèmes d'une `meta` d'extension du panneau (vide = valide). Une entrée invalide
32
+ * n'apparaît pas : le dire clairement à l'enregistrement évite de chercher pourquoi à l'intégration d'une app.
33
+ */ export function getStaggeredMenuEntryProblems(meta) {
34
+ if (!meta || typeof meta !== 'object') return [
35
+ '`meta` est absente : ajouter au moins { "title": "…" }'
36
+ ];
37
+ const m = meta;
38
+ const problems = [];
39
+ if (typeof m.title !== 'string' || m.title.length === 0) problems.push('`title` doit être une chaîne non vide');
40
+ if (m.titleKey !== undefined && typeof m.titleKey !== 'string') problems.push('`titleKey` doit être une chaîne');
41
+ if (m.area !== undefined && !isStaggeredMenuArea(m.area)) problems.push(`\`area\` vaut ${STAGGERED_MENU_AREAS.map((a)=>`"${a}"`).join(', ')}`);
42
+ if (m.order !== undefined && (typeof m.order !== 'number' || !Number.isFinite(m.order))) problems.push('`order` doit être un nombre fini');
43
+ if (m.priority !== undefined && (typeof m.priority !== 'number' || !Number.isFinite(m.priority))) problems.push('`priority` doit être un nombre fini');
44
+ if (m.global !== undefined && typeof m.global !== 'boolean') problems.push('`global` doit être un booléen');
45
+ if (m.showTitle !== undefined && typeof m.showTitle !== 'boolean') problems.push('`showTitle` doit être un booléen');
46
+ return problems;
47
+ }
48
+ /** Valide une `meta` d'extension : vrai si c'est une `StaggeredMenuEntryMeta` exploitable. */ export function isStaggeredMenuEntryMeta(meta) {
49
+ return getStaggeredMenuEntryProblems(meta).length === 0;
50
+ }
51
+ /**
52
+ * Entrées affichables du panneau, dans l'ordre d'affichage.
53
+ *
54
+ * 1. écarte les entrées dont la `meta` est invalide ;
55
+ * 2. écarte celles dont l'app n'est pas montée, sauf `meta.global` ;
56
+ * 3. si `maxEntries` est fixé, ne garde que les plus prioritaires ;
57
+ * 4. trie par zone (`top`, `content`, `bottom`), puis `order` croissant, puis `priority` décroissante,
58
+ * puis ordre d'enregistrement.
59
+ */ export function resolveStaggeredMenuEntries(assigned, activeModules, options = {}) {
60
+ const areaRank = (area)=>STAGGERED_MENU_AREAS.indexOf(area);
61
+ const visible = assigned.map((extension, index)=>({
62
+ extension,
63
+ index
64
+ })).filter(({ extension })=>isStaggeredMenuEntryMeta(extension.meta)).filter(({ extension })=>extension.meta.global === true || activeModules.has(extension.moduleName)).map(({ extension, index })=>{
65
+ const meta = extension.meta;
66
+ return {
67
+ index,
68
+ entry: {
69
+ id: extension.name,
70
+ moduleName: extension.moduleName,
71
+ meta,
72
+ area: meta.area ?? DEFAULT_STAGGERED_MENU_AREA,
73
+ order: meta.order ?? index,
74
+ priority: meta.priority ?? 0,
75
+ extension
76
+ }
77
+ };
78
+ });
79
+ const byDisplay = (a, b)=>areaRank(a.entry.area) - areaRank(b.entry.area) || a.entry.order - b.entry.order || b.entry.priority - a.entry.priority || a.index - b.index;
80
+ const sorted = [
81
+ ...visible
82
+ ].sort(byDisplay);
83
+ const { maxEntries } = options;
84
+ if (typeof maxEntries !== 'number' || !Number.isFinite(maxEntries) || maxEntries < 0 || maxEntries >= sorted.length) {
85
+ return sorted.map(({ entry })=>entry);
86
+ }
87
+ // Les plus prioritaires d'abord ; à priorité égale, celles qui passent en premier à l'affichage.
88
+ const kept = new Set(sorted.map((item, displayIndex)=>({
89
+ item,
90
+ displayIndex
91
+ })).sort((a, b)=>b.item.entry.priority - a.item.entry.priority || a.displayIndex - b.displayIndex).slice(0, Math.floor(maxEntries)).map(({ item })=>item));
92
+ return sorted.filter((item)=>kept.has(item)).map(({ entry })=>entry);
93
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@egen-civitas/esm-extensions",
3
- "version": "2.2.0",
3
+ "version": "2.3.0",
4
4
  "license": "MPL-2.0",
5
5
  "description": "Coordinates extensions and extension points in the Egen Frontend",
6
6
  "type": "module",