kritzel-stencil 0.2.9 → 0.2.11
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/dist/cjs/index.cjs.js +1 -1
- package/dist/cjs/kritzel-active-users_42.cjs.entry.js +291 -75
- package/dist/cjs/loader.cjs.js +1 -1
- package/dist/cjs/stencil.cjs.js +1 -1
- package/dist/cjs/{workspace.migrations-BuN0vRGQ.js → workspace.migrations-D5sPPbQN.js} +47 -33
- package/dist/collection/classes/managers/theme.manager.js +35 -29
- package/dist/collection/classes/tools/text-tool.class.js +12 -4
- package/dist/collection/components/core/kritzel-editor/kritzel-editor.css +8 -1
- package/dist/collection/components/core/kritzel-editor/kritzel-editor.js +25 -6
- package/dist/collection/components/core/kritzel-engine/kritzel-engine.js +85 -0
- package/dist/collection/components/shared/kritzel-dialog/kritzel-dialog.css +29 -0
- package/dist/collection/components/shared/kritzel-dialog/kritzel-dialog.js +201 -5
- package/dist/collection/components/ui/kritzel-controls/kritzel-controls.css +1 -1
- package/dist/collection/components/ui/kritzel-current-user-dialog/kritzel-current-user-dialog.js +1 -1
- package/dist/collection/components/ui/kritzel-export/kritzel-export.js +1 -1
- package/dist/collection/components/ui/kritzel-login-dialog/kritzel-login-dialog.js +1 -1
- package/dist/collection/components/ui/kritzel-settings/kritzel-settings.js +72 -64
- package/dist/collection/components/ui/kritzel-share-dialog/kritzel-share-dialog.js +2 -2
- package/dist/collection/constants/version.js +1 -1
- package/dist/components/index.js +1 -1
- package/dist/components/kritzel-awareness-cursors.js +1 -1
- package/dist/components/kritzel-color-palette.js +1 -1
- package/dist/components/kritzel-color.js +1 -1
- package/dist/components/kritzel-controls.js +1 -1
- package/dist/components/kritzel-current-user-dialog.js +1 -1
- package/dist/components/kritzel-current-user.js +1 -1
- package/dist/components/kritzel-dialog.js +1 -1
- package/dist/components/kritzel-editor.js +1 -1
- package/dist/components/kritzel-engine.js +1 -1
- package/dist/components/kritzel-export.js +1 -1
- package/dist/components/kritzel-login-dialog.js +1 -1
- package/dist/components/kritzel-settings.js +1 -1
- package/dist/components/kritzel-share-dialog.js +1 -1
- package/dist/components/kritzel-stroke-size.js +1 -1
- package/dist/components/kritzel-tool-config.js +1 -1
- package/dist/components/{p-DeqXAEjq.js → p-B638ZH7S.js} +1 -1
- package/dist/components/p-BTSOqHMI.js +1 -0
- package/dist/components/{p-BD-U5p22.js → p-BWRjTm0J.js} +1 -1
- package/dist/components/{p-CNa_5hqn.js → p-B_fA1LTU.js} +2 -2
- package/dist/components/{p-DORo_go4.js → p-C-sJ1r3g.js} +1 -1
- package/dist/components/{p-BhMchyAR.js → p-C4bAtxyk.js} +1 -1
- package/dist/components/p-C51_twnc.js +1 -0
- package/dist/components/{p-DDm8Gefw.js → p-CqAkznU_.js} +1 -1
- package/dist/components/p-CrSLn46K.js +1 -0
- package/dist/components/p-CrmWVXea.js +1 -0
- package/dist/components/{p-CitH48cC.js → p-D9-C4GfD.js} +1 -1
- package/dist/components/{p-BK9c3UTv.js → p-DF8X_22i.js} +1 -1
- package/dist/components/p-DMfU0hHe.js +1 -0
- package/dist/components/{p-D_Tdq4Z0.js → p-DjAiIBXv.js} +1 -1
- package/dist/components/p-DqJ9KC24.js +1 -0
- package/dist/components/p-DsxW_miC.js +1 -0
- package/dist/components/{p-nW05C2cx.js → p-Z9_amVdR.js} +1 -1
- package/dist/esm/index.js +2 -2
- package/dist/esm/kritzel-active-users_42.entry.js +291 -75
- package/dist/esm/loader.js +1 -1
- package/dist/esm/stencil.js +1 -1
- package/dist/esm/{workspace.migrations-DbozNwZA.js → workspace.migrations-BnTvdnKU.js} +47 -33
- package/dist/stencil/index.esm.js +1 -1
- package/dist/stencil/p-836d973c.entry.js +9 -0
- package/dist/stencil/p-BnTvdnKU.js +1 -0
- package/dist/stencil/stencil.esm.js +1 -1
- package/dist/types/classes/managers/theme.manager.d.ts +9 -15
- package/dist/types/components/core/kritzel-editor/kritzel-editor.d.ts +1 -0
- package/dist/types/components/core/kritzel-engine/kritzel-engine.d.ts +11 -0
- package/dist/types/components/shared/kritzel-dialog/kritzel-dialog.d.ts +21 -0
- package/dist/types/components/ui/kritzel-settings/kritzel-settings.d.ts +5 -5
- package/dist/types/components.d.ts +31 -13
- package/dist/types/constants/version.d.ts +1 -1
- package/package.json +1 -1
- package/dist/components/p-B1V6yEGY.js +0 -1
- package/dist/components/p-BuSOJ7Xd.js +0 -1
- package/dist/components/p-DBIK7z89.js +0 -1
- package/dist/components/p-DPxSr1wV.js +0 -1
- package/dist/components/p-DpjrLdtb.js +0 -1
- package/dist/components/p-Dpr_JQam.js +0 -1
- package/dist/components/p-aO27ZwFX.js +0 -1
- package/dist/stencil/p-DbozNwZA.js +0 -1
- package/dist/stencil/p-d444dc76.entry.js +0 -9
|
@@ -279,6 +279,9 @@ export class KritzelEditor {
|
|
|
279
279
|
}
|
|
280
280
|
onCurrentThemeChange() {
|
|
281
281
|
setTimeout(() => this.setOsSpecificCssVariables(), 0);
|
|
282
|
+
if (this.engineRef) {
|
|
283
|
+
this.engineRef.saveSettings(this.currentSettingsConfig);
|
|
284
|
+
}
|
|
282
285
|
}
|
|
283
286
|
onTouchStart(event) {
|
|
284
287
|
if (event.cancelable) {
|
|
@@ -555,6 +558,9 @@ export class KritzelEditor {
|
|
|
555
558
|
this.viewportBoundaryBottom = event.detail.viewportBoundaryBottom ?? Infinity;
|
|
556
559
|
this.debugInfo = event.detail.debugInfo;
|
|
557
560
|
this.themeChange.emit(event.detail.theme);
|
|
561
|
+
if (this.engineRef) {
|
|
562
|
+
this.engineRef.saveSettings(event.detail);
|
|
563
|
+
}
|
|
558
564
|
}
|
|
559
565
|
get moreMenuItems() {
|
|
560
566
|
return [
|
|
@@ -677,6 +683,19 @@ export class KritzelEditor {
|
|
|
677
683
|
async loadShortcuts() {
|
|
678
684
|
this.shortcuts = await this.engineRef.getDisplayableShortcuts();
|
|
679
685
|
}
|
|
686
|
+
get currentSettingsConfig() {
|
|
687
|
+
return {
|
|
688
|
+
scaleMin: this.scaleMin,
|
|
689
|
+
scaleMax: this.scaleMax,
|
|
690
|
+
lockDrawingScale: this.lockDrawingScale,
|
|
691
|
+
theme: this.currentTheme,
|
|
692
|
+
viewportBoundaryLeft: this.viewportBoundaryLeft,
|
|
693
|
+
viewportBoundaryRight: this.viewportBoundaryRight,
|
|
694
|
+
viewportBoundaryTop: this.viewportBoundaryTop,
|
|
695
|
+
viewportBoundaryBottom: this.viewportBoundaryBottom,
|
|
696
|
+
debugInfo: this.debugInfo,
|
|
697
|
+
};
|
|
698
|
+
}
|
|
680
699
|
getContentObjects(objects) {
|
|
681
700
|
return objects.filter(obj => !(obj instanceof KritzelSelectionGroup) && !(obj instanceof KritzelSelectionBox));
|
|
682
701
|
}
|
|
@@ -723,27 +742,27 @@ export class KritzelEditor {
|
|
|
723
742
|
const isLoggedIn = this.isLoggedIn;
|
|
724
743
|
const shouldShowCurrentUser = isLoggedIn;
|
|
725
744
|
const shouldShowLoginButton = !!this.loginConfig && !isLoggedIn;
|
|
726
|
-
return (h(Host, { key: '
|
|
745
|
+
return (h(Host, { key: 'e259a79d0fb8e39be29f338c78b3b686c82790ff' }, h("div", { key: '2aa5768df3fe0442ff4b032c1f3defa3e2414d4b', class: "top-left-buttons" }, h("kritzel-workspace-manager", { key: '6bade436347fba887007843ed3ad71f85ea4acb0', visible: this.isWorkspaceManagerVisible, workspaces: this.workspaces, activeWorkspace: this.activeWorkspace, onWorkspaceChange: event => (this.activeWorkspace = event.detail), onIsWorkspaceManagerReady: () => (this.isWorkspaceManagerReady = true) }), h("kritzel-back-to-content", { key: 'cdbf4c27695a5daefe9548a642a9dcd0aff7a77b', visible: this.isBackToContentButtonVisible, onBackToContent: () => this.backToContent() })), h("kritzel-engine", { key: 'e0ac833bd839c04d90c8788663ea0afd6aa35c39', ref: el => {
|
|
727
746
|
if (el) {
|
|
728
747
|
this.engineRef = el;
|
|
729
748
|
}
|
|
730
|
-
}, workspace: this.activeWorkspace, activeWorkspaceId: this.activeWorkspaceId, editorId: this.editorId, syncConfig: this.syncConfig, assetStorageConfig: this.assetStorageConfig, user: this.user, scaleMax: this.scaleMax, lockDrawingScale: this.lockDrawingScale, scaleMin: this.scaleMin, cursorTarget: this.cursorTarget, isLoading: this.isLoading, viewportBoundaryLeft: this.viewportBoundaryLeft, viewportBoundaryRight: this.viewportBoundaryRight, viewportBoundaryTop: this.viewportBoundaryTop, viewportBoundaryBottom: this.viewportBoundaryBottom, wheelEnabled: this.wheelEnabled, theme: this.currentTheme, debugInfo: this.debugInfo, globalContextMenuItems: this.globalContextMenuItems, objectContextMenuItems: this.objectContextMenuItems, onIsEngineReady: event => this.onEngineReady(event), onWorkspacesChange: event => this.handleWorkspacesChange(event), onActiveWorkspaceChange: event => this.handleActiveWorkspaceChange(event), onObjectsChange: event => this.handleObjectsChange(event), onObjectsAdded: event => this.handleObjectsAdded(event), onObjectsRemoved: event => this.handleObjectsRemoved(event), onObjectsUpdated: event => this.handleObjectsUpdated(event), onUndoStateChange: event => this.handleUndoStateChange(event), onObjectsInViewportChange: event => this.handleObjectsInViewportChange(event), onViewportChange: event => this.handleViewportChange(event), onAwarenessChange: event => this.handleAwarenessChange(event) }), h("kritzel-controls", { key: '
|
|
749
|
+
}, workspace: this.activeWorkspace, activeWorkspaceId: this.activeWorkspaceId, editorId: this.editorId, syncConfig: this.syncConfig, assetStorageConfig: this.assetStorageConfig, user: this.user, scaleMax: this.scaleMax, lockDrawingScale: this.lockDrawingScale, scaleMin: this.scaleMin, cursorTarget: this.cursorTarget, isLoading: this.isLoading, viewportBoundaryLeft: this.viewportBoundaryLeft, viewportBoundaryRight: this.viewportBoundaryRight, viewportBoundaryTop: this.viewportBoundaryTop, viewportBoundaryBottom: this.viewportBoundaryBottom, wheelEnabled: this.wheelEnabled, theme: this.currentTheme, debugInfo: this.debugInfo, globalContextMenuItems: this.globalContextMenuItems, objectContextMenuItems: this.objectContextMenuItems, onIsEngineReady: event => this.onEngineReady(event), onWorkspacesChange: event => this.handleWorkspacesChange(event), onActiveWorkspaceChange: event => this.handleActiveWorkspaceChange(event), onObjectsChange: event => this.handleObjectsChange(event), onObjectsAdded: event => this.handleObjectsAdded(event), onObjectsRemoved: event => this.handleObjectsRemoved(event), onObjectsUpdated: event => this.handleObjectsUpdated(event), onUndoStateChange: event => this.handleUndoStateChange(event), onObjectsInViewportChange: event => this.handleObjectsInViewportChange(event), onViewportChange: event => this.handleViewportChange(event), onAwarenessChange: event => this.handleAwarenessChange(event) }), h("kritzel-controls", { key: '396ed985b464306a845a4ef4f54782ccc42bd344', class: { 'keyboard-open': this.isVirtualKeyboardOpen }, style: { display: this.isControlsVisible ? 'flex' : 'none' }, ref: el => {
|
|
731
750
|
if (el) {
|
|
732
751
|
this.controlsRef = el;
|
|
733
752
|
}
|
|
734
|
-
}, controls: this.controls, isUtilityPanelVisible: this.isUtilityPanelVisible, undoState: this.undoState ?? undefined, theme: this.currentTheme, onIsControlsReady: () => (this.isControlsReady = true) }), h("div", { key: '
|
|
753
|
+
}, controls: this.controls, isUtilityPanelVisible: this.isUtilityPanelVisible, undoState: this.undoState ?? undefined, theme: this.currentTheme, onIsControlsReady: () => (this.isControlsReady = true) }), h("div", { key: '67fdbff9c0259c02bd02bc1342856bc4bdac6d9f', class: "top-right-buttons" }, h("kritzel-settings", { key: '08e1bbfc902e8fe24395babef675654d84bc2dfc', ref: el => {
|
|
735
754
|
if (el) {
|
|
736
755
|
this.settingsRef = el;
|
|
737
756
|
}
|
|
738
|
-
}, shortcuts: this.shortcuts,
|
|
757
|
+
}, shortcuts: this.shortcuts, settings: this.currentSettingsConfig, onSettingsChange: event => this.handleSettingsChange(event) }), h("kritzel-export", { key: 'e3250aaecf428bf763aefea11167b72587cee52d', ref: el => {
|
|
739
758
|
if (el) {
|
|
740
759
|
this.exportRef = el;
|
|
741
760
|
}
|
|
742
|
-
}, workspaceName: this.activeWorkspace?.name || 'workspace', onExportPng: () => this.engineRef.exportViewportAsPng(), onExportSvg: () => this.engineRef.exportViewportAsSvg(), onExportJson: event => this.engineRef.downloadAsJson(event.detail) }), h("kritzel-active-users", { key: '
|
|
761
|
+
}, workspaceName: this.activeWorkspace?.name || 'workspace', onExportPng: () => this.engineRef.exportViewportAsPng(), onExportSvg: () => this.engineRef.exportViewportAsSvg(), onExportJson: event => this.engineRef.downloadAsJson(event.detail) }), h("kritzel-active-users", { key: '6ba93cfeb8eb28d6e7a7c0d8f6813300bd14bd2b', users: this.activeUsers }), shouldShowCurrentUser && h("kritzel-current-user", { key: '4efef69fc7c1b443f6e12fdcda5ed8d26dbebcd3', user: this.user }), shouldShowLoginButton && h("kritzel-button", { key: '54208a3e130fe41f7429516d52f166b08de7e24c', onButtonClick: () => this.loginDialogRef?.open() }, "Sign in"), h("kritzel-more-menu", { key: 'b5a6f5e0e5465655d109ee9047cba97200f94639', items: this.moreMenuItems, visible: this.isMoreMenuVisible }), h("kritzel-share-dialog", { key: 'ee32c5f54395db794ab77ecd0f2e9657db6765c0', ref: el => {
|
|
743
762
|
if (el) {
|
|
744
763
|
this.shareDialogRef = el;
|
|
745
764
|
}
|
|
746
|
-
}, isPublic: this.currentIsPublic, workspaceId: this.activeWorkspace?.id, onToggleIsPublic: this.handleToggleIsPublic }), this.loginConfig && (h("kritzel-login-dialog", { key: '
|
|
765
|
+
}, isPublic: this.currentIsPublic, workspaceId: this.activeWorkspace?.id, onToggleIsPublic: this.handleToggleIsPublic }), this.loginConfig && (h("kritzel-login-dialog", { key: 'b24a52c160d828218d949ac1d8b9edd9b3c00535', ref: el => {
|
|
747
766
|
if (el) {
|
|
748
767
|
this.loginDialogRef = el;
|
|
749
768
|
}
|
|
@@ -1236,6 +1236,31 @@ export class KritzelEngine {
|
|
|
1236
1236
|
this._isAssetStorageInitialized = false;
|
|
1237
1237
|
await this.initializeSyncAndWorkspace();
|
|
1238
1238
|
}
|
|
1239
|
+
/**
|
|
1240
|
+
* Persists the given settings object to localStorage using the namespaced storage key.
|
|
1241
|
+
* @param settings - The settings configuration to persist.
|
|
1242
|
+
*/
|
|
1243
|
+
async saveSettings(settings) {
|
|
1244
|
+
const key = this.core.getStorageKey('kritzel-settings');
|
|
1245
|
+
localStorage.setItem(key, JSON.stringify(settings));
|
|
1246
|
+
}
|
|
1247
|
+
/**
|
|
1248
|
+
* Loads the persisted settings object from localStorage.
|
|
1249
|
+
* @returns The stored settings, or null if nothing is stored or the value is invalid.
|
|
1250
|
+
*/
|
|
1251
|
+
async loadSettings() {
|
|
1252
|
+
const key = this.core.getStorageKey('kritzel-settings');
|
|
1253
|
+
const stored = localStorage.getItem(key);
|
|
1254
|
+
if (!stored) {
|
|
1255
|
+
return null;
|
|
1256
|
+
}
|
|
1257
|
+
try {
|
|
1258
|
+
return JSON.parse(stored);
|
|
1259
|
+
}
|
|
1260
|
+
catch {
|
|
1261
|
+
return null;
|
|
1262
|
+
}
|
|
1263
|
+
}
|
|
1239
1264
|
core;
|
|
1240
1265
|
viewport;
|
|
1241
1266
|
contextMenuHandler;
|
|
@@ -4470,6 +4495,66 @@ export class KritzelEngine {
|
|
|
4470
4495
|
"text": "Reinitializes sync by performing a full teardown and re-initialization.\nDestroys the current Yjs documents and providers, then rebuilds everything\nfrom the current syncConfig. Data reloads from IndexedDB nearly instantly.\nCall this after updating the syncConfig prop (e.g. after authentication state changes).",
|
|
4471
4496
|
"tags": []
|
|
4472
4497
|
}
|
|
4498
|
+
},
|
|
4499
|
+
"saveSettings": {
|
|
4500
|
+
"complexType": {
|
|
4501
|
+
"signature": "(settings: KritzelSettingsConfig) => Promise<void>",
|
|
4502
|
+
"parameters": [{
|
|
4503
|
+
"name": "settings",
|
|
4504
|
+
"type": "KritzelSettingsConfig",
|
|
4505
|
+
"docs": "- The settings configuration to persist."
|
|
4506
|
+
}],
|
|
4507
|
+
"references": {
|
|
4508
|
+
"Promise": {
|
|
4509
|
+
"location": "global",
|
|
4510
|
+
"id": "global::Promise"
|
|
4511
|
+
},
|
|
4512
|
+
"KritzelSettingsConfig": {
|
|
4513
|
+
"location": "import",
|
|
4514
|
+
"path": "../../../interfaces/settings.interface",
|
|
4515
|
+
"id": "src/interfaces/settings.interface.ts::KritzelSettingsConfig",
|
|
4516
|
+
"referenceLocation": "KritzelSettingsConfig"
|
|
4517
|
+
}
|
|
4518
|
+
},
|
|
4519
|
+
"return": "Promise<void>"
|
|
4520
|
+
},
|
|
4521
|
+
"docs": {
|
|
4522
|
+
"text": "Persists the given settings object to localStorage using the namespaced storage key.",
|
|
4523
|
+
"tags": [{
|
|
4524
|
+
"name": "param",
|
|
4525
|
+
"text": "settings - The settings configuration to persist."
|
|
4526
|
+
}]
|
|
4527
|
+
}
|
|
4528
|
+
},
|
|
4529
|
+
"loadSettings": {
|
|
4530
|
+
"complexType": {
|
|
4531
|
+
"signature": "() => Promise<Partial<KritzelSettingsConfig> | null>",
|
|
4532
|
+
"parameters": [],
|
|
4533
|
+
"references": {
|
|
4534
|
+
"Promise": {
|
|
4535
|
+
"location": "global",
|
|
4536
|
+
"id": "global::Promise"
|
|
4537
|
+
},
|
|
4538
|
+
"Partial": {
|
|
4539
|
+
"location": "global",
|
|
4540
|
+
"id": "global::Partial"
|
|
4541
|
+
},
|
|
4542
|
+
"KritzelSettingsConfig": {
|
|
4543
|
+
"location": "import",
|
|
4544
|
+
"path": "../../../interfaces/settings.interface",
|
|
4545
|
+
"id": "src/interfaces/settings.interface.ts::KritzelSettingsConfig",
|
|
4546
|
+
"referenceLocation": "KritzelSettingsConfig"
|
|
4547
|
+
}
|
|
4548
|
+
},
|
|
4549
|
+
"return": "Promise<Partial<KritzelSettingsConfig>>"
|
|
4550
|
+
},
|
|
4551
|
+
"docs": {
|
|
4552
|
+
"text": "Loads the persisted settings object from localStorage.",
|
|
4553
|
+
"tags": [{
|
|
4554
|
+
"name": "returns",
|
|
4555
|
+
"text": "The stored settings, or null if nothing is stored or the value is invalid."
|
|
4556
|
+
}]
|
|
4557
|
+
}
|
|
4473
4558
|
}
|
|
4474
4559
|
};
|
|
4475
4560
|
}
|
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
display: contents;
|
|
3
3
|
}
|
|
4
4
|
|
|
5
|
+
.dialog-content {
|
|
6
|
+
text-align: start;
|
|
7
|
+
line-height: normal;
|
|
8
|
+
}
|
|
9
|
+
|
|
5
10
|
.backdrop {
|
|
6
11
|
position: fixed;
|
|
7
12
|
top: 0;
|
|
@@ -71,6 +76,30 @@
|
|
|
71
76
|
border-radius: 0;
|
|
72
77
|
}
|
|
73
78
|
|
|
79
|
+
/*
|
|
80
|
+
* Contained-mobile: when the wrapping container itself is mobile-sized, the
|
|
81
|
+
* dialog fills the container fully (mirrors the viewport fullscreen-on-mobile
|
|
82
|
+
* behavior). The actual sizing is set inline by JS based on the container
|
|
83
|
+
* rect; here we just remove decorations and ensure body flex layout.
|
|
84
|
+
*/
|
|
85
|
+
.backdrop.contained-fullscreen {
|
|
86
|
+
background-color: transparent;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.dialog-content.contained-fullscreen {
|
|
90
|
+
border-radius: 0;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.dialog-content.contained-fullscreen .dialog-body {
|
|
94
|
+
display: flex;
|
|
95
|
+
flex-direction: column;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.dialog-content.contained-fullscreen .dialog-body ::slotted(*) {
|
|
99
|
+
flex: 1;
|
|
100
|
+
min-height: 0;
|
|
101
|
+
}
|
|
102
|
+
|
|
74
103
|
/* Responsive: auto fullscreen on mobile when enabled (portrait) */
|
|
75
104
|
@media (max-width: 576px),
|
|
76
105
|
(max-height: 576px) and (orientation: landscape) {
|
|
@@ -20,8 +20,12 @@ export class KritzelDialog {
|
|
|
20
20
|
size = 'medium';
|
|
21
21
|
/** Whether to automatically go fullscreen on mobile viewports */
|
|
22
22
|
fullscreenOnMobile = true;
|
|
23
|
+
/** Constrain the dialog to its nearest editor/container ancestor instead of the viewport. */
|
|
24
|
+
contained = false;
|
|
23
25
|
isAnimating = false;
|
|
24
26
|
mobileLockedHeight = null;
|
|
27
|
+
containerRect = null;
|
|
28
|
+
containerBorderRadius = null;
|
|
25
29
|
/** Emitted when the dialog opens */
|
|
26
30
|
dialogOpen;
|
|
27
31
|
/** Emitted when the dialog closes */
|
|
@@ -29,6 +33,9 @@ export class KritzelDialog {
|
|
|
29
33
|
previousOverflow = '';
|
|
30
34
|
previousActiveElement = null;
|
|
31
35
|
visualViewportListenersAttached = false;
|
|
36
|
+
containerElement = null;
|
|
37
|
+
containerResizeObserver = null;
|
|
38
|
+
containerTrackingFrame = null;
|
|
32
39
|
handleIsOpenChange(newValue) {
|
|
33
40
|
if (newValue) {
|
|
34
41
|
this.openDialog();
|
|
@@ -78,13 +85,19 @@ export class KritzelDialog {
|
|
|
78
85
|
}
|
|
79
86
|
disconnectedCallback() {
|
|
80
87
|
this.removeVisualViewportListeners();
|
|
88
|
+
this.stopContainerTracking();
|
|
81
89
|
this.restoreBodyScroll();
|
|
82
90
|
}
|
|
83
91
|
openDialog() {
|
|
84
92
|
this.isAnimating = true;
|
|
85
93
|
this.previousActiveElement = document.activeElement;
|
|
86
|
-
this.
|
|
87
|
-
|
|
94
|
+
if (this.contained) {
|
|
95
|
+
this.startContainerTracking();
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
this.lockBodyScroll();
|
|
99
|
+
this.addVisualViewportListeners();
|
|
100
|
+
}
|
|
88
101
|
this.lockMobileViewportHeight();
|
|
89
102
|
this.dialogOpen.emit();
|
|
90
103
|
if (this.autoFocus) {
|
|
@@ -99,9 +112,106 @@ export class KritzelDialog {
|
|
|
99
112
|
closeDialog() {
|
|
100
113
|
this.restoreBodyScroll();
|
|
101
114
|
this.removeVisualViewportListeners();
|
|
115
|
+
this.stopContainerTracking();
|
|
102
116
|
this.mobileLockedHeight = null;
|
|
103
117
|
this.returnFocusToPreviousElement();
|
|
104
118
|
}
|
|
119
|
+
findContainerElement() {
|
|
120
|
+
// Walk up the composed DOM (crossing shadow roots) and return the first
|
|
121
|
+
// ancestor that opts into containing dialogs. We accept either an explicit
|
|
122
|
+
// [data-kritzel-dialog-container] marker or the kritzel-editor host so any
|
|
123
|
+
// consumer can opt in without modifying the editor itself.
|
|
124
|
+
let node = this.host;
|
|
125
|
+
while (node) {
|
|
126
|
+
if (node instanceof HTMLElement) {
|
|
127
|
+
if (node.hasAttribute('data-kritzel-dialog-container') || node.tagName === 'KRITZEL-EDITOR') {
|
|
128
|
+
return node;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
const parent = node.parentNode;
|
|
132
|
+
if (parent) {
|
|
133
|
+
node = parent;
|
|
134
|
+
}
|
|
135
|
+
else if (node instanceof ShadowRoot) {
|
|
136
|
+
node = node.host;
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
node = null;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
startContainerTracking() {
|
|
145
|
+
this.containerElement = this.findContainerElement();
|
|
146
|
+
if (!this.containerElement) {
|
|
147
|
+
// No container found — fall back to viewport behavior.
|
|
148
|
+
this.containerRect = null;
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
this.updateContainerRect();
|
|
152
|
+
if (typeof ResizeObserver !== 'undefined') {
|
|
153
|
+
this.containerResizeObserver = new ResizeObserver(() => this.updateContainerRect());
|
|
154
|
+
this.containerResizeObserver.observe(this.containerElement);
|
|
155
|
+
}
|
|
156
|
+
window.addEventListener('resize', this.handleContainerTrackingEvent, { passive: true });
|
|
157
|
+
window.addEventListener('scroll', this.handleContainerTrackingEvent, { capture: true, passive: true });
|
|
158
|
+
}
|
|
159
|
+
stopContainerTracking() {
|
|
160
|
+
if (this.containerResizeObserver) {
|
|
161
|
+
this.containerResizeObserver.disconnect();
|
|
162
|
+
this.containerResizeObserver = null;
|
|
163
|
+
}
|
|
164
|
+
window.removeEventListener('resize', this.handleContainerTrackingEvent);
|
|
165
|
+
window.removeEventListener('scroll', this.handleContainerTrackingEvent, { capture: true });
|
|
166
|
+
if (this.containerTrackingFrame !== null) {
|
|
167
|
+
cancelAnimationFrame(this.containerTrackingFrame);
|
|
168
|
+
this.containerTrackingFrame = null;
|
|
169
|
+
}
|
|
170
|
+
this.containerElement = null;
|
|
171
|
+
this.containerRect = null;
|
|
172
|
+
this.containerBorderRadius = null;
|
|
173
|
+
}
|
|
174
|
+
handleContainerTrackingEvent = () => {
|
|
175
|
+
if (this.containerTrackingFrame !== null)
|
|
176
|
+
return;
|
|
177
|
+
this.containerTrackingFrame = requestAnimationFrame(() => {
|
|
178
|
+
this.containerTrackingFrame = null;
|
|
179
|
+
this.updateContainerRect();
|
|
180
|
+
});
|
|
181
|
+
};
|
|
182
|
+
updateContainerRect() {
|
|
183
|
+
if (!this.containerElement)
|
|
184
|
+
return;
|
|
185
|
+
const rect = this.containerElement.getBoundingClientRect();
|
|
186
|
+
const next = { top: rect.top, left: rect.left, width: rect.width, height: rect.height };
|
|
187
|
+
const prev = this.containerRect;
|
|
188
|
+
if (!prev || prev.top !== next.top || prev.left !== next.left || prev.width !== next.width || prev.height !== next.height) {
|
|
189
|
+
this.containerRect = next;
|
|
190
|
+
}
|
|
191
|
+
// Look for the nearest visually-rounded ancestor (the container itself or
|
|
192
|
+
// any ancestor up to the document) so the backdrop matches wrappers that
|
|
193
|
+
// round their corners around the editor.
|
|
194
|
+
const radius = this.findVisualBorderRadius(this.containerElement);
|
|
195
|
+
if (this.containerBorderRadius !== radius) {
|
|
196
|
+
this.containerBorderRadius = radius;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
findVisualBorderRadius(start) {
|
|
200
|
+
let node = start;
|
|
201
|
+
while (node && node !== document.body && node !== document.documentElement) {
|
|
202
|
+
const computed = window.getComputedStyle(node);
|
|
203
|
+
const tl = computed.borderTopLeftRadius;
|
|
204
|
+
const tr = computed.borderTopRightRadius;
|
|
205
|
+
const br = computed.borderBottomRightRadius;
|
|
206
|
+
const bl = computed.borderBottomLeftRadius;
|
|
207
|
+
const isZero = (v) => !v || v === '0px' || v === '0%';
|
|
208
|
+
if (!(isZero(tl) && isZero(tr) && isZero(br) && isZero(bl))) {
|
|
209
|
+
return `${tl} ${tr} ${br} ${bl}`;
|
|
210
|
+
}
|
|
211
|
+
node = node.parentElement;
|
|
212
|
+
}
|
|
213
|
+
return null;
|
|
214
|
+
}
|
|
105
215
|
emitClose(reason) {
|
|
106
216
|
this.dialogClose.emit({ reason });
|
|
107
217
|
}
|
|
@@ -110,6 +220,12 @@ export class KritzelDialog {
|
|
|
110
220
|
document.body.style.overflow = 'hidden';
|
|
111
221
|
}
|
|
112
222
|
lockMobileViewportHeight() {
|
|
223
|
+
// Skip mobile viewport height locking when contained — the dialog is sized
|
|
224
|
+
// by its wrapper, not the viewport.
|
|
225
|
+
if (this.contained) {
|
|
226
|
+
this.mobileLockedHeight = null;
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
113
229
|
// Only lock height on mobile when fullscreenOnMobile is enabled.
|
|
114
230
|
// Use the smaller dimension so landscape phones (wide but short) are also detected.
|
|
115
231
|
const viewportWidth = this.getViewportWidth();
|
|
@@ -244,15 +360,73 @@ export class KritzelDialog {
|
|
|
244
360
|
return null;
|
|
245
361
|
return (h("div", { class: "dialog-footer" }, h("slot", { name: "footer" })));
|
|
246
362
|
}
|
|
363
|
+
getBackdropStyle() {
|
|
364
|
+
if (!this.contained || !this.containerRect)
|
|
365
|
+
return undefined;
|
|
366
|
+
const { top, left, width, height } = this.containerRect;
|
|
367
|
+
const style = {
|
|
368
|
+
top: `${top}px`,
|
|
369
|
+
left: `${left}px`,
|
|
370
|
+
right: 'auto',
|
|
371
|
+
bottom: 'auto',
|
|
372
|
+
width: `${width}px`,
|
|
373
|
+
height: `${height}px`,
|
|
374
|
+
};
|
|
375
|
+
if (this.containerBorderRadius) {
|
|
376
|
+
style.borderRadius = this.containerBorderRadius;
|
|
377
|
+
// Ensure rounded corners actually clip the inner dialog content.
|
|
378
|
+
style.overflow = 'hidden';
|
|
379
|
+
}
|
|
380
|
+
return style;
|
|
381
|
+
}
|
|
382
|
+
getDialogContentStyle() {
|
|
383
|
+
const style = {};
|
|
384
|
+
// In contained mode, cap the dialog dimensions to the container so the
|
|
385
|
+
// declared widths/heights of size variants cannot overflow the wrapper.
|
|
386
|
+
if (this.contained && this.containerRect) {
|
|
387
|
+
const { width, height } = this.containerRect;
|
|
388
|
+
if (this.isContainerMobile()) {
|
|
389
|
+
// Mobile-sized container: behave like fullscreen-on-mobile but scoped
|
|
390
|
+
// to the container instead of the viewport (works in any orientation).
|
|
391
|
+
style.width = `${width}px`;
|
|
392
|
+
style.height = `${height}px`;
|
|
393
|
+
style.maxWidth = `${width}px`;
|
|
394
|
+
style.maxHeight = `${height}px`;
|
|
395
|
+
style.borderRadius = '0';
|
|
396
|
+
}
|
|
397
|
+
else {
|
|
398
|
+
// Leave a small margin so the dialog visibly sits within the container.
|
|
399
|
+
const maxWidth = Math.max(0, width - 32);
|
|
400
|
+
const maxHeight = Math.max(0, height - 32);
|
|
401
|
+
style.maxWidth = `${maxWidth}px`;
|
|
402
|
+
style.maxHeight = `${maxHeight}px`;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
if (this.mobileLockedHeight) {
|
|
406
|
+
style.height = this.mobileLockedHeight;
|
|
407
|
+
style.maxHeight = this.mobileLockedHeight;
|
|
408
|
+
}
|
|
409
|
+
return Object.keys(style).length > 0 ? style : undefined;
|
|
410
|
+
}
|
|
411
|
+
isContainerMobile() {
|
|
412
|
+
if (!this.fullscreenOnMobile || !this.containerRect)
|
|
413
|
+
return false;
|
|
414
|
+
// Match the existing viewport media-query threshold: container is "mobile"
|
|
415
|
+
// when its smaller dimension is <= 576px. This handles both portrait and
|
|
416
|
+
// landscape wrappers symmetrically.
|
|
417
|
+
return Math.min(this.containerRect.width, this.containerRect.height) <= 576;
|
|
418
|
+
}
|
|
247
419
|
render() {
|
|
248
420
|
if (!this.isOpen)
|
|
249
421
|
return null;
|
|
250
|
-
|
|
422
|
+
const containerFullscreen = this.contained && this.isContainerMobile();
|
|
423
|
+
return (h(Host, null, h("div", { class: { backdrop: true, 'is-animating': this.isAnimating, 'contained-fullscreen': containerFullscreen }, style: this.getBackdropStyle(), onClick: this.handleBackdropClick }, h("div", { class: {
|
|
251
424
|
'dialog-content': true,
|
|
252
425
|
'is-animating': this.isAnimating,
|
|
253
426
|
[`size-${this.size}`]: true,
|
|
254
427
|
'fullscreen-on-mobile': this.fullscreenOnMobile,
|
|
255
|
-
|
|
428
|
+
'contained-fullscreen': containerFullscreen,
|
|
429
|
+
}, style: this.getDialogContentStyle(), role: "dialog", "aria-modal": "true", "aria-labelledby": this.dialogTitle ? 'dialog-title' : undefined, tabIndex: -1, onClick: this.handleContentClick }, this.renderHeader(), h("div", { class: "dialog-body" }, h("slot", null)), this.renderFooter()))));
|
|
256
430
|
}
|
|
257
431
|
static get is() { return "kritzel-dialog"; }
|
|
258
432
|
static get encapsulation() { return "shadow"; }
|
|
@@ -446,13 +620,35 @@ export class KritzelDialog {
|
|
|
446
620
|
"reflect": false,
|
|
447
621
|
"attribute": "fullscreen-on-mobile",
|
|
448
622
|
"defaultValue": "true"
|
|
623
|
+
},
|
|
624
|
+
"contained": {
|
|
625
|
+
"type": "boolean",
|
|
626
|
+
"mutable": false,
|
|
627
|
+
"complexType": {
|
|
628
|
+
"original": "boolean",
|
|
629
|
+
"resolved": "boolean",
|
|
630
|
+
"references": {}
|
|
631
|
+
},
|
|
632
|
+
"required": false,
|
|
633
|
+
"optional": false,
|
|
634
|
+
"docs": {
|
|
635
|
+
"tags": [],
|
|
636
|
+
"text": "Constrain the dialog to its nearest editor/container ancestor instead of the viewport."
|
|
637
|
+
},
|
|
638
|
+
"getter": false,
|
|
639
|
+
"setter": false,
|
|
640
|
+
"reflect": true,
|
|
641
|
+
"attribute": "contained",
|
|
642
|
+
"defaultValue": "false"
|
|
449
643
|
}
|
|
450
644
|
};
|
|
451
645
|
}
|
|
452
646
|
static get states() {
|
|
453
647
|
return {
|
|
454
648
|
"isAnimating": {},
|
|
455
|
-
"mobileLockedHeight": {}
|
|
649
|
+
"mobileLockedHeight": {},
|
|
650
|
+
"containerRect": {},
|
|
651
|
+
"containerBorderRadius": {}
|
|
456
652
|
};
|
|
457
653
|
}
|
|
458
654
|
static get events() {
|
package/dist/collection/components/ui/kritzel-current-user-dialog/kritzel-current-user-dialog.js
CHANGED
|
@@ -21,7 +21,7 @@ export class KritzelCurrentUserDialog {
|
|
|
21
21
|
}
|
|
22
22
|
render() {
|
|
23
23
|
const displayName = this.getDisplayName();
|
|
24
|
-
return (h(Host, { key: 'e1dd44cdfdbaebfe886fed0d9feba2ef232b6615' }, h("kritzel-dialog", { key: '
|
|
24
|
+
return (h(Host, { key: 'e1dd44cdfdbaebfe886fed0d9feba2ef232b6615' }, h("kritzel-dialog", { key: 'cd3daa7abd53c10852d63a2fe53d919414cd8904', dialogTitle: "Account", isOpen: this.isDialogOpen, onDialogClose: this.closeDialog, size: "small", contained: true }, h("div", { key: '94d0a691ede73135e6cf4ef144c13e52e410ffbe', class: "user-info" }, h("kritzel-avatar", { key: 'e57592d2f3663b593534055be5aae1b224fa8906', user: this.user, size: 80 }), displayName && h("div", { key: '237db2d0608ee49ea70e5282b61a59077f0f4595', class: "user-name" }, displayName), this.user?.email && h("div", { key: 'd821e8171530b92ce6f1781c1145b611d3c533d0', class: "user-email" }, this.user.email)))));
|
|
25
25
|
}
|
|
26
26
|
static get is() { return "kritzel-current-user-dialog"; }
|
|
27
27
|
static get encapsulation() { return "shadow"; }
|
|
@@ -65,7 +65,7 @@ export class KritzelExport {
|
|
|
65
65
|
return (h("div", { class: "export-tab-content" }, h("kritzel-input", { label: "Filename", value: this.exportFilename, placeholder: "Enter filename", suffix: ".json", onValueChange: this.handleFilenameChange })));
|
|
66
66
|
}
|
|
67
67
|
render() {
|
|
68
|
-
return (h(Host, { key: '5178e66f75b94697c771e2dc6fe7ce317e21cd1a' }, h("kritzel-dialog", { key: '
|
|
68
|
+
return (h(Host, { key: '5178e66f75b94697c771e2dc6fe7ce317e21cd1a' }, h("kritzel-dialog", { key: 'f80cbe3fa709ed7e046303034b7345ca1f94bc48', isOpen: this.isDialogOpen, dialogTitle: "Export", closable: true, contained: true, onDialogClose: this.closeDialog }, h("div", { key: 'e7968807c2b67ebfc800cb1694b4e34af245ffba', class: "export-content" }, h("kritzel-pill-tabs", { key: 'eac62225c4c42431296f330791a1fb2212f579f5', tabs: this.tabs, value: this.activeTab, onValueChange: this.handleTabChange }), this.activeTab === 'viewport' && this.renderViewportExport(), this.activeTab === 'workspace' && this.renderWorkspaceExport(), h("button", { key: '3489affca39a901c2ef05a0698cdf51c0a7f6d1a', class: "export-primary-button", onClick: this.handleExport }, "Export")))));
|
|
69
69
|
}
|
|
70
70
|
static get is() { return "kritzel-export"; }
|
|
71
71
|
static get encapsulation() { return "shadow"; }
|
|
@@ -44,7 +44,7 @@ export class KritzelLoginDialog {
|
|
|
44
44
|
this.dialogClosed.emit();
|
|
45
45
|
};
|
|
46
46
|
render() {
|
|
47
|
-
return (h(Host, { key: '1a664868b840030a773f61c2a0f4388dfb014675' }, h("kritzel-dialog", { key: '
|
|
47
|
+
return (h(Host, { key: '1a664868b840030a773f61c2a0f4388dfb014675' }, h("kritzel-dialog", { key: '54844ffa772a211515c1ef3e6834ec45f7f3d035', dialogTitle: this.dialogTitle, isOpen: this.isDialogOpen, onDialogClose: this.closeDialog, size: "small", contained: true }, h("div", { key: 'd9b981b6904c58bc39173ae37ee5c4c0ee329005', class: "login-content" }, this.subtitle && (h("p", { key: 'd4d200060507d2b8b755796d8313acdfc7e2f587', class: "login-subtitle" }, this.subtitle)), h("div", { key: '3dc1e3c070e62d026eb16ceb48eb63c94bc2bed0', class: "login-providers" }, this.providers.map(provider => (h("button", { key: provider.name, class: {
|
|
48
48
|
'provider-button': true,
|
|
49
49
|
'is-loading': this.loadingProvider === provider.name,
|
|
50
50
|
'is-disabled': this.loadingProvider !== null && this.loadingProvider !== provider.name,
|