@consenti/ui 0.4.0 → 0.5.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/README.md CHANGED
@@ -272,6 +272,47 @@ profileOverride: {
272
272
  no-op (same as omitting the key), the latter merges an empty object onto the existing category,
273
273
  changing nothing. Only an explicit `null` removes it.
274
274
 
275
+ #### Replacing a whole map: `'*': null`
276
+
277
+ Removing entries one by one doesn't scale when you want to *replace* a map: every built-in
278
+ profile uses different button ids (`reject-optional`, `save-choices`, `manage-preferences`, …),
279
+ and there are three `buttons` maps (`mainBanner`, `gpcBanner`, `preferenceModal`). Use the
280
+ wildcard key `'*': null` to delete **every key of the base map that your override doesn't name**:
281
+
282
+ ```ts
283
+ profileOverride: {
284
+ mainBanner: {
285
+ buttons: {
286
+ '*': null, // drop all other built-in buttons
287
+ 'accept-all': { text: 'Allow all' }, // kept — only its text changes
288
+ 'my-button': { text: 'Custom', style: 'secondary', action: 'custom', cookies: '!' },
289
+ },
290
+ },
291
+ preferenceModal: {
292
+ buttons: { '*': null, 'my-save': { text: 'Save', style: 'primary', action: 'submit' } },
293
+ categories: { '*': null, necessary: {}, analytics: {} }, // keep only these two categories
294
+ },
295
+ },
296
+ ```
297
+
298
+ - Keys you **name** are kept and merged onto their base value (so `'accept-all': { text }` keeps
299
+ its `style`/`action`/`cookies`); keys you **don't name** are deleted; new keys are added.
300
+ - It works per map, so you choose which surfaces to replace — leave `gpcBanner` alone and it keeps
301
+ its built-in buttons.
302
+ - `'*': null` alone empties the map. It works on any keyed map (`cookies` too), and under
303
+ `complianceGroupsOverride` and `widget.setProfile()`.
304
+ - `'*'` is reserved in these maps and can't be used as a button/category id.
305
+ - `widget.setProfile()` patches are replayed in order on the resolved profile (rather than being
306
+ folded together first), so `null` and `'*': null` deletes in a later `setProfile()` call are
307
+ honoured even after an earlier override.
308
+
309
+ > **Compliance:** refusing must be as easy as accepting (GDPR Art. 7(3); CNIL/EDPB cookie-banner
310
+ > guidance). If, after overrides, a `mainBanner`/`gpcBanner` has an "accept all" button
311
+ > (`cookies: '*'`) but no reject (`cookies: '!'`) or `manage` button, the widget logs a
312
+ > `console.warn`. Warnings are hidden by default — enable them with `verbose: true` or
313
+ > `core: { console: ['warning'] }`. Keep a reject or manage path when
314
+ > you replace the built-in buttons.
315
+
275
316
  ---
276
317
 
277
318
  ## Compliance Groups
@@ -622,6 +622,7 @@ declare class ConsentiSetup implements ConsentiWidgetAPI {
622
622
  private initializing;
623
623
  private profile;
624
624
  private resolvedBase;
625
+ private profilePatches;
625
626
  /** cookieId → Category lookup, rebuilt whenever `profile` changes. Avoids rescanning categories on every consent action. */
626
627
  private categoryIndex;
627
628
  /** Watchers created by the declarative `data-consenti-*` DOM scan — destroyed alongside the widget since they have no other owner. */
@@ -677,6 +678,14 @@ declare class ConsentiSetup implements ConsentiWidgetAPI {
677
678
  * cookies per compliance group (built-in or custom) without authoring a separate profile for
678
679
  * each one. Applied before `profileOverride`, which still always wins as the final catch-all. */
679
680
  private applyComplianceGroupOverride;
681
+ /**
682
+ * Applied in order: compliance-group override → `config.profileOverride` → each `setProfile()`
683
+ * patch. The `setProfile()` patches are kept as a list and replayed, not folded into one
684
+ * object, because folding runs `deepMerge` patch-onto-patch, which consumes `null`/`'*': null`
685
+ * delete markers before they ever reach the profile they were meant to delete from.
686
+ */
687
+ private applyProfileOverrides;
688
+ private warnIfAcceptOnly;
680
689
  /**
681
690
  * Whether `window.__tcfapi`/`window.__gpp` should stay uninstalled because the corresponding
682
691
  * server-side registration (`compliance.tcf`/`compliance.gpp`) is unconfirmed (cmpId/cmpVersion
package/dist/angular.d.ts CHANGED
@@ -622,6 +622,7 @@ declare class ConsentiSetup implements ConsentiWidgetAPI {
622
622
  private initializing;
623
623
  private profile;
624
624
  private resolvedBase;
625
+ private profilePatches;
625
626
  /** cookieId → Category lookup, rebuilt whenever `profile` changes. Avoids rescanning categories on every consent action. */
626
627
  private categoryIndex;
627
628
  /** Watchers created by the declarative `data-consenti-*` DOM scan — destroyed alongside the widget since they have no other owner. */
@@ -677,6 +678,14 @@ declare class ConsentiSetup implements ConsentiWidgetAPI {
677
678
  * cookies per compliance group (built-in or custom) without authoring a separate profile for
678
679
  * each one. Applied before `profileOverride`, which still always wins as the final catch-all. */
679
680
  private applyComplianceGroupOverride;
681
+ /**
682
+ * Applied in order: compliance-group override → `config.profileOverride` → each `setProfile()`
683
+ * patch. The `setProfile()` patches are kept as a list and replayed, not folded into one
684
+ * object, because folding runs `deepMerge` patch-onto-patch, which consumes `null`/`'*': null`
685
+ * delete markers before they ever reach the profile they were meant to delete from.
686
+ */
687
+ private applyProfileOverrides;
688
+ private warnIfAcceptOnly;
680
689
  /**
681
690
  * Whether `window.__tcfapi`/`window.__gpp` should stay uninstalled because the corresponding
682
691
  * server-side registration (`compliance.tcf`/`compliance.gpp`) is unconfirmed (cmpId/cmpVersion
package/dist/index.d.ts CHANGED
@@ -789,6 +789,7 @@ declare class ConsentiSetup implements ConsentiWidgetAPI {
789
789
  private initializing;
790
790
  private profile;
791
791
  private resolvedBase;
792
+ private profilePatches;
792
793
  /** cookieId → Category lookup, rebuilt whenever `profile` changes. Avoids rescanning categories on every consent action. */
793
794
  private categoryIndex;
794
795
  /** Watchers created by the declarative `data-consenti-*` DOM scan — destroyed alongside the widget since they have no other owner. */
@@ -844,6 +845,14 @@ declare class ConsentiSetup implements ConsentiWidgetAPI {
844
845
  * cookies per compliance group (built-in or custom) without authoring a separate profile for
845
846
  * each one. Applied before `profileOverride`, which still always wins as the final catch-all. */
846
847
  private applyComplianceGroupOverride;
848
+ /**
849
+ * Applied in order: compliance-group override → `config.profileOverride` → each `setProfile()`
850
+ * patch. The `setProfile()` patches are kept as a list and replayed, not folded into one
851
+ * object, because folding runs `deepMerge` patch-onto-patch, which consumes `null`/`'*': null`
852
+ * delete markers before they ever reach the profile they were meant to delete from.
853
+ */
854
+ private applyProfileOverrides;
855
+ private warnIfAcceptOnly;
847
856
  /**
848
857
  * Whether `window.__tcfapi`/`window.__gpp` should stay uninstalled because the corresponding
849
858
  * server-side registration (`compliance.tcf`/`compliance.gpp`) is unconfirmed (cmpId/cmpVersion