@gemx-dev/clarity-visualize 0.8.47 → 0.8.48

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gemx-dev/clarity-visualize",
3
- "version": "0.8.47",
3
+ "version": "0.8.48",
4
4
  "description": "Clarity visualize",
5
5
  "author": "Microsoft Corp.",
6
6
  "license": "MIT",
@@ -9,7 +9,7 @@
9
9
  "unpkg": "build/clarity.visualize.min.js",
10
10
  "types": "types/index.d.ts",
11
11
  "dependencies": {
12
- "@gemx-dev/clarity-decode": "^0.8.47"
12
+ "@gemx-dev/clarity-decode": "^0.8.48"
13
13
  },
14
14
  "devDependencies": {
15
15
  "@rollup/plugin-commonjs": "^24.0.0",
@@ -0,0 +1,60 @@
1
+ # Custom Clarity Visualize Modules
2
+
3
+ This directory contains custom rendering modules that extend the base Clarity visualization for GemX.
4
+
5
+ ## Structure
6
+
7
+ All custom modules follow the functional module pattern:
8
+ - Pure functions for testability
9
+ - No side effects in utility functions
10
+ - Clear separation of concerns
11
+ - Type-safe interfaces
12
+
13
+ ## Modules
14
+
15
+ ### Dialog Module (`dialog.ts`)
16
+
17
+ Renders HTML `<dialog>` elements with proper top-layer and backdrop support.
18
+
19
+ **Functions:**
20
+
21
+ - `getDialogRenderOptions(attributes, dialogElement)` - Extracts rendering options from attributes
22
+ - `showDialog(dialogElement, isModal, onError)` - Shows dialog with proper modal/non-modal handling
23
+ - `closeDialog(dialogElement)` - Safely closes a dialog
24
+ - `renderDialog(dialogElement, options, onError)` - Main entry point for dialog rendering
25
+ - `cleanDialogAttributes(attributes)` - Removes internal tracking attributes
26
+
27
+ **Usage:**
28
+
29
+ ```typescript
30
+ import * as dialogCustom from "./custom/dialog";
31
+
32
+ const renderOptions = dialogCustom.getDialogRenderOptions(node.attributes, dialogElement);
33
+ node.attributes = dialogCustom.cleanDialogAttributes(node.attributes);
34
+
35
+ dialogCustom.renderDialog(
36
+ dialogElement,
37
+ renderOptions,
38
+ this.state.options.logerror
39
+ );
40
+ ```
41
+
42
+ **Features:**
43
+
44
+ - Automatic detection of modal vs non-modal dialogs
45
+ - Proper top-layer rendering via `showModal()`
46
+ - Backdrop support for modal dialogs
47
+ - Handles dialogs already open from HTML attributes
48
+ - Error handling and logging
49
+
50
+ ## Adding New Custom Modules
51
+
52
+ 1. Create a new file in this directory (e.g., `my-feature.ts`)
53
+ 2. Follow the functional module pattern
54
+ 3. Export functions with clear type signatures
55
+ 4. Add exports to `index.ts`
56
+ 5. Document the module in this README
57
+
58
+ ## Testing
59
+
60
+ Custom modules should be unit tested separately from the main Clarity codebase.
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Custom module for rendering HTML Dialog elements
3
+ * Handles proper visualization of modal dialogs in the top-layer
4
+ */
5
+
6
+ export interface DialogRenderOptions {
7
+ isModal: boolean;
8
+ shouldBeOpen: boolean;
9
+ isExistingDialog: boolean;
10
+ }
11
+
12
+ /**
13
+ * Extracts dialog rendering options from node attributes
14
+ *
15
+ * @param attributes - Node attributes containing dialog state
16
+ * @param dialogElement - The dialog element being rendered
17
+ * @returns Dialog render options
18
+ */
19
+ export function getDialogRenderOptions(
20
+ attributes: { [key: string]: string },
21
+ dialogElement: HTMLDialogElement | null
22
+ ): DialogRenderOptions {
23
+ return {
24
+ isModal: attributes["data-clarity-modal"] === "true",
25
+ shouldBeOpen: attributes["open"] !== undefined,
26
+ isExistingDialog: !!dialogElement
27
+ };
28
+ }
29
+
30
+ /**
31
+ * Shows a dialog element with proper modal/non-modal handling
32
+ * Modal dialogs are shown via showModal() to render in top-layer with backdrop
33
+ * Non-modal dialogs are shown via show() method
34
+ *
35
+ * @param dialogElement - The dialog element to show
36
+ * @param isModal - Whether this is a modal dialog
37
+ * @param onError - Optional error callback
38
+ */
39
+ export function showDialog(
40
+ dialogElement: HTMLDialogElement,
41
+ isModal: boolean,
42
+ onError?: (error: Error) => void
43
+ ): void {
44
+ try {
45
+ if (!dialogElement.isConnected) {
46
+ console.warn('⚠️ Dialog not connected to DOM, skipping show');
47
+ return;
48
+ }
49
+
50
+ // IMPORTANT: If dialog is already open (from HTML attribute),
51
+ // we need to close and reopen to ensure showModal() is called
52
+ // and dialog enters top-layer properly
53
+ if (dialogElement.open) {
54
+ dialogElement.close();
55
+ }
56
+
57
+ if (isModal) {
58
+ dialogElement.showModal();
59
+ } else {
60
+ dialogElement.show();
61
+ }
62
+ } catch (e) {
63
+ console.error('❌ Error showing dialog:', e);
64
+ if (onError) {
65
+ onError(e as Error);
66
+ }
67
+ }
68
+ }
69
+
70
+ /**
71
+ * Closes a dialog element safely
72
+ *
73
+ * @param dialogElement - The dialog element to close
74
+ */
75
+ export function closeDialog(dialogElement: HTMLDialogElement): void {
76
+ try {
77
+ if (dialogElement.open) {
78
+ dialogElement.close();
79
+ }
80
+ } catch (e) {
81
+ console.warn('⚠️ Error closing dialog:', e);
82
+ }
83
+ }
84
+
85
+ /**
86
+ * Handles dialog rendering based on its state
87
+ * This is the main entry point for dialog rendering logic
88
+ *
89
+ * @param dialogElement - The dialog element to render
90
+ * @param options - Dialog render options
91
+ * @param onError - Optional error callback
92
+ */
93
+ export function renderDialog(
94
+ dialogElement: HTMLDialogElement,
95
+ options: DialogRenderOptions,
96
+ onError?: (error: Error) => void
97
+ ): void {
98
+ const { isModal, shouldBeOpen, isExistingDialog } = options;
99
+
100
+ if (shouldBeOpen) {
101
+ const doShow = () => showDialog(dialogElement, isModal, onError);
102
+
103
+ if (isExistingDialog) {
104
+ // Dialog already exists, call immediately
105
+ doShow();
106
+ } else {
107
+ // New dialog, wait for DOM insertion
108
+ setTimeout(doShow, 0);
109
+ }
110
+ } else if (dialogElement.open) {
111
+ // Dialog should be closed
112
+ closeDialog(dialogElement);
113
+ }
114
+ }
115
+
116
+ /**
117
+ * Removes custom tracking attributes before rendering
118
+ * These attributes are only for internal use
119
+ *
120
+ * @param attributes - Attributes object to clean
121
+ * @returns Cleaned attributes
122
+ */
123
+ export function cleanDialogAttributes(
124
+ attributes: { [key: string]: string }
125
+ ): { [key: string]: string } {
126
+ const cleaned = { ...attributes };
127
+ delete cleaned["data-clarity-modal"];
128
+ return cleaned;
129
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Custom modules for GemX Clarity Visualize extensions
3
+ * Contains custom rendering logic that extends the base Clarity visualization
4
+ */
5
+
6
+ export * from "./dialog";
package/src/layout.ts CHANGED
@@ -5,6 +5,7 @@ import { StyleSheetOperation } from "@gemx-dev/clarity-js/types/layout";
5
5
  import { AnimationOperation } from "@gemx-dev/clarity-js/types/layout";
6
6
  import { Constant as LayoutConstants } from "@gemx-dev/clarity-js/types/layout";
7
7
  import sharedStyle from "./styles/shared.css";
8
+ import * as dialogCustom from "./custom/dialog";
8
9
 
9
10
  /* BEGIN blobUnavailableSvgs */
10
11
  import blobUnavailableSvgEnglish from "./styles/blobUnavailable/english.svg";
@@ -459,6 +460,31 @@ export class LayoutHelper {
459
460
  this.insertDefaultElement(node, parent, pivot, doc, insert);
460
461
  break;
461
462
  }
463
+ case "DIALOG":
464
+ {
465
+ // Use custom module for dialog rendering
466
+ let dialogElement = this.element(node.id) as HTMLDialogElement;
467
+ dialogElement = dialogElement ? dialogElement : this.createElement(doc, node.tag) as HTMLDialogElement;
468
+ if (!node.attributes) { node.attributes = {}; }
469
+
470
+ // Extract render options before cleaning attributes
471
+ const renderOptions = dialogCustom.getDialogRenderOptions(node.attributes, dialogElement);
472
+
473
+ // Clean custom tracking attributes
474
+ node.attributes = dialogCustom.cleanDialogAttributes(node.attributes);
475
+
476
+ // Set attributes and insert into DOM
477
+ this.setAttributes(dialogElement, node);
478
+ insert(node, parent, dialogElement, pivot);
479
+
480
+ // Render dialog with proper modal/non-modal handling
481
+ dialogCustom.renderDialog(
482
+ dialogElement,
483
+ renderOptions,
484
+ this.state.options.logerror
485
+ );
486
+ break;
487
+ }
462
488
  default:
463
489
  this.insertDefaultElement(node, parent, pivot, doc, insert);
464
490
  break;
@@ -3,4 +3,22 @@ iframe[data-clarity-unavailable-small], iframe[data-clarity-unavailable], img[da
3
3
  border-style: dashed;
4
4
  border-width: 6px;
5
5
  border-color: #827DFF;
6
+ }
7
+
8
+ /* Dialog top-layer styles */
9
+ /* Ensure modal dialogs appear in the top-layer with proper z-index */
10
+ dialog[open] {
11
+ /* Modal dialogs shown via showModal() are automatically placed in top-layer by the browser */
12
+ /* This ensures proper stacking even during replay */
13
+ z-index: auto;
14
+ }
15
+
16
+ /* Style the backdrop for modal dialogs */
17
+ dialog::backdrop {
18
+ background: rgba(0, 0, 0, 0.5);
19
+ }
20
+
21
+ /* Ensure non-modal dialogs (shown via show()) are positioned correctly */
22
+ dialog:not([open]) {
23
+ display: none;
6
24
  }