@readium/decorator 1.1.1 → 1.2.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
@@ -87,12 +87,22 @@ class DecorationController {
87
87
  registerDecorationObserver(group: string, observer: DecorationObserver): void
88
88
  unregisterDecorationObserver(observer: DecorationObserver): void
89
89
 
90
+ // Watches an additional element for resize, so decorations relay out when it resizes
91
+ // even if the document's own size doesn't change (e.g. a sidebar collapsing and the
92
+ // reading pane reflowing inside it). Selector is resolved against the Decorator's document.
93
+ addDecorationResizeTarget(selector: string): void
94
+ removeDecorationResizeTarget(selector: string): void
95
+
90
96
  destroy(): void
91
97
  }
92
98
 
93
99
  interface DecorationControllerConfig {
94
100
  // Named style templates. Register a template here and reference it by ID in decoration styles.
95
101
  decorationTemplates?: Record<string, HTMLDecorationTemplate>;
102
+
103
+ // CSS selectors to watch for resize from construction, equivalent to calling
104
+ // addDecorationResizeTarget() for each one right after construction.
105
+ resizeWatchSelectors?: string[];
96
106
  }
97
107
  ```
98
108
 
@@ -165,7 +175,7 @@ interface IComms {
165
175
  | `BuiltinDecorationStyle` | `{ type?, tint?, layout?, width?, enforceContrast?, expand? }` |
166
176
  | `HTMLDecorationTemplate` | `{ type: "template", layout, width, element, stylesheet? }` — `element` is a function `(decoration) => string`, resolved to HTML per decoration before rendering |
167
177
  | `NamedDecorationStyle` | `{ type: string }` — reference to a style registered in `DecoratorConfig.decorationTemplates` |
168
- | `DecoratorConfig` | `{ decorationTemplates? }` — same shape as `DecorationControllerConfig` (the latter is a type alias of this) |
178
+ | `DecoratorConfig` | `{ decorationTemplates?, resizeWatchSelectors? }` — same shape as `DecorationControllerConfig` (the latter is a type alias of this) |
169
179
  | `DecorationStyleType` | `"highlight" \| "highlightUnderline" \| "underline" \| "strikethrough" \| "outline" \| "textColor" \| "mask" \| "template"` |
170
180
  | `DecorationLayout` | `"boxes" \| "bounds"` |
171
181
  | `DecorationWidth` | `"wrap" \| "viewport" \| "bounds" \| "page"` |
@@ -1 +1 @@
1
- import{supportsDecorationStyle as v,decorationsEqual as _,resolveDecorationForWire as d}from"../styles.js";class p{constructor(o,e={}){this.host=o,this._decorations=new Map,this._activationState=new Map,this._hoverState=new Map,this._observers=new Map,this._hoveredDecorations=new Map,this._config=e,o.on("decoration_activated",a=>{const t=a,r=this._decorations.get(t.group)?.find(i=>i.id===t.decorationId);r&&this._observers.get(t.group)?.forEach(i=>i.onDecorationActivated?.({group:t.group,decoration:r,rect:t.rect,point:t.point}))}),o.on("decoration_pointer_enter",a=>{const t=a,r=this._decorations.get(t.group)?.find(i=>i.id===t.decorationId);r&&(this._hoveredDecorations.set(t.group,r),this._observers.get(t.group)?.forEach(i=>i.onDecorationPointerEnter?.({group:t.group,decoration:r,rect:t.rect,point:t.point})))}),o.on("decoration_pointer_leave",a=>{const t=a,r=this._decorations.get(t.group)?.find(i=>i.id===t.decorationId)??this._hoveredDecorations.get(t.group);this._hoveredDecorations.delete(t.group),r&&this._observers.get(t.group)?.forEach(i=>i.onDecorationPointerLeave?.({group:t.group,decoration:r,rect:t.rect,point:t.point}))})}supportsDecorationStyle(o){return v(o,this._config.decorationTemplates)}applyDecorations(o,e){const a=this._decorations.get(e)??[],t=new Map(a.map(n=>[n.id,n])),r=new Map(o.map(n=>[n.id,n]));for(const[n,s]of t){const c=r.get(n);c?_(s,c)||this.host.send("decorate",{group:e,action:"update",decoration:d(c,this._config.decorationTemplates)}):this.host.send("decorate",{group:e,action:"remove",decoration:{id:n}})}for(const[n,s]of r)t.has(n)||this.host.send("decorate",{group:e,action:"add",decoration:d(s,this._config.decorationTemplates)});this._decorations.set(e,o);const i=this._activationState.get(e);i!==void 0&&this.host.send("decoration_activatable",{group:e,activatable:i});const h=this._hoverState.get(e);h!==void 0&&this.host.send("decoration_hoverable",{group:e,hoverable:h})}registerDecorationObserver(o,e){this._observers.has(o)||this._observers.set(o,new Set),this._observers.get(o).add(e),e.onDecorationActivated&&(this._activationState.set(o,!0),this.host.send("decoration_activatable",{group:o,activatable:!0})),(e.onDecorationPointerEnter||e.onDecorationPointerLeave)&&(this._hoverState.set(o,!0),this.host.send("decoration_hoverable",{group:o,hoverable:!0}))}unregisterDecorationObserver(o){this._observers.forEach((e,a)=>{if(!e.has(o))return;e.delete(o);const t=[...e].some(i=>i.onDecorationActivated);this._activationState.has(a)&&!t&&(this._activationState.delete(a),this.host.send("decoration_activatable",{group:a,activatable:!1}));const r=[...e].some(i=>i.onDecorationPointerEnter||i.onDecorationPointerLeave);this._hoverState.has(a)&&!r&&(this._hoverState.delete(a),this.host.send("decoration_hoverable",{group:a,hoverable:!1}))})}destroy(){this._decorations.clear(),this._activationState.clear(),this._hoverState.clear(),this._observers.clear(),this._hoveredDecorations.clear()}}export{p as DecorationController};
1
+ import{supportsDecorationStyle as v,decorationsEqual as _,resolveDecorationForWire as d}from"../styles.js";class p{constructor(e,o={}){this.host=e,this._decorations=new Map,this._activationState=new Map,this._hoverState=new Map,this._observers=new Map,this._hoveredDecorations=new Map,this._config=o,o.resizeWatchSelectors?.forEach(a=>this.addDecorationResizeTarget(a)),e.on("decoration_activated",a=>{const t=a,r=this._decorations.get(t.group)?.find(i=>i.id===t.decorationId);r&&this._observers.get(t.group)?.forEach(i=>i.onDecorationActivated?.({group:t.group,decoration:r,rect:t.rect,point:t.point}))}),e.on("decoration_pointer_enter",a=>{const t=a,r=this._decorations.get(t.group)?.find(i=>i.id===t.decorationId);r&&(this._hoveredDecorations.set(t.group,r),this._observers.get(t.group)?.forEach(i=>i.onDecorationPointerEnter?.({group:t.group,decoration:r,rect:t.rect,point:t.point})))}),e.on("decoration_pointer_leave",a=>{const t=a,r=this._decorations.get(t.group)?.find(i=>i.id===t.decorationId)??this._hoveredDecorations.get(t.group);this._hoveredDecorations.delete(t.group),r&&this._observers.get(t.group)?.forEach(i=>i.onDecorationPointerLeave?.({group:t.group,decoration:r,rect:t.rect,point:t.point}))})}addDecorationResizeTarget(e){this.host.send("decoration_resize",{action:"watch",selector:e})}removeDecorationResizeTarget(e){this.host.send("decoration_resize",{action:"unwatch",selector:e})}supportsDecorationStyle(e){return v(e,this._config.decorationTemplates)}applyDecorations(e,o){const a=this._decorations.get(o)??[],t=new Map(a.map(s=>[s.id,s])),r=new Map(e.map(s=>[s.id,s]));for(const[s,n]of t){const c=r.get(s);c?_(n,c)||this.host.send("decorate",{group:o,action:"update",decoration:d(c,this._config.decorationTemplates)}):this.host.send("decorate",{group:o,action:"remove",decoration:{id:s}})}for(const[s,n]of r)t.has(s)||this.host.send("decorate",{group:o,action:"add",decoration:d(n,this._config.decorationTemplates)});this._decorations.set(o,e);const i=this._activationState.get(o);i!==void 0&&this.host.send("decoration_activatable",{group:o,activatable:i});const h=this._hoverState.get(o);h!==void 0&&this.host.send("decoration_hoverable",{group:o,hoverable:h})}registerDecorationObserver(e,o){this._observers.has(e)||this._observers.set(e,new Set),this._observers.get(e).add(o),o.onDecorationActivated&&(this._activationState.set(e,!0),this.host.send("decoration_activatable",{group:e,activatable:!0})),(o.onDecorationPointerEnter||o.onDecorationPointerLeave)&&(this._hoverState.set(e,!0),this.host.send("decoration_hoverable",{group:e,hoverable:!0}))}unregisterDecorationObserver(e){this._observers.forEach((o,a)=>{if(!o.has(e))return;o.delete(e);const t=[...o].some(i=>i.onDecorationActivated);this._activationState.has(a)&&!t&&(this._activationState.delete(a),this.host.send("decoration_activatable",{group:a,activatable:!1}));const r=[...o].some(i=>i.onDecorationPointerEnter||i.onDecorationPointerLeave);this._hoverState.has(a)&&!r&&(this._hoverState.delete(a),this.host.send("decoration_hoverable",{group:a,hoverable:!1}))})}destroy(){this._decorations.clear(),this._activationState.clear(),this._hoverState.clear(),this._observers.clear(),this._hoveredDecorations.clear()}}export{p as DecorationController};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@readium/decorator",
3
- "version": "1.1.1",
3
+ "version": "1.2.0",
4
4
  "type": "module",
5
5
  "description": "Standalone decoration controller and renderer for HTML content",
6
6
  "author": "readium",
@@ -32,7 +32,7 @@
32
32
  "node": ">=18"
33
33
  },
34
34
  "dependencies": {
35
- "@readium/navigator-html-injectables": "2.7.1",
35
+ "@readium/navigator-html-injectables": "2.8.0",
36
36
  "@readium/shared": "2.5.0"
37
37
  },
38
38
  "devDependencies": {
@@ -16,6 +16,7 @@ export class DecorationController {
16
16
 
17
17
  constructor(private readonly host: DirectCommsHost, config: DecorationControllerConfig = {}) {
18
18
  this._config = config;
19
+ config.resizeWatchSelectors?.forEach(selector => this.addDecorationResizeTarget(selector));
19
20
  host.on("decoration_activated", (raw) => {
20
21
  const ev = raw as DecorationActivatedEvent;
21
22
  const decoration = this._decorations.get(ev.group)?.find(d => d.id === ev.decorationId);
@@ -47,6 +48,14 @@ export class DecorationController {
47
48
  });
48
49
  }
49
50
 
51
+ addDecorationResizeTarget(selector: string): void {
52
+ this.host.send("decoration_resize", { action: "watch", selector });
53
+ }
54
+
55
+ removeDecorationResizeTarget(selector: string): void {
56
+ this.host.send("decoration_resize", { action: "unwatch", selector });
57
+ }
58
+
50
59
  supportsDecorationStyle(styleTypeId: string): boolean {
51
60
  return supportsDecorationStyle(styleTypeId, this._config.decorationTemplates);
52
61
  }
package/src/styles.ts CHANGED
@@ -36,6 +36,13 @@ export interface DecoratorConfig {
36
36
  * Template-type decoration.
37
37
  */
38
38
  decorationTemplates?: Record<string, HTMLDecorationTemplate>;
39
+
40
+ /**
41
+ * CSS selectors of additional elements to watch for resize, so decoration overlays are
42
+ * relaid-out when they resize for reasons that don't affect the document's own size
43
+ * (e.g. a sidebar collapsing and the reading pane reflowing within a flex/grid layout).
44
+ */
45
+ resizeWatchSelectors?: string[];
39
46
  }
40
47
 
41
48
  export const BUILTIN_DECORATION_TYPES = new Set<string>(Object.values(DecorationStyleType));
@@ -11,6 +11,8 @@ export declare class DecorationController {
11
11
  private _hoveredDecorations;
12
12
  private readonly _config;
13
13
  constructor(host: DirectCommsHost, config?: DecorationControllerConfig);
14
+ addDecorationResizeTarget(selector: string): void;
15
+ removeDecorationResizeTarget(selector: string): void;
14
16
  supportsDecorationStyle(styleTypeId: string): boolean;
15
17
  applyDecorations(decorations: Decoration[], group: string): void;
16
18
  registerDecorationObserver(group: string, observer: DecorationObserver): void;
@@ -25,6 +25,12 @@ export interface DecoratorConfig {
25
25
  * Template-type decoration.
26
26
  */
27
27
  decorationTemplates?: Record<string, HTMLDecorationTemplate>;
28
+ /**
29
+ * CSS selectors of additional elements to watch for resize, so decoration overlays are
30
+ * relaid-out when they resize for reasons that don't affect the document's own size
31
+ * (e.g. a sidebar collapsing and the reading pane reflowing within a flex/grid layout).
32
+ */
33
+ resizeWatchSelectors?: string[];
28
34
  }
29
35
  export declare const BUILTIN_DECORATION_TYPES: Set<string>;
30
36
  /**