@memberjunction/ng-artifacts 5.24.0 → 5.26.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/dist/lib/artifacts.module.d.ts +16 -11
- package/dist/lib/artifacts.module.d.ts.map +1 -1
- package/dist/lib/artifacts.module.js +40 -8
- package/dist/lib/artifacts.module.js.map +1 -1
- package/dist/lib/components/artifact-viewer-panel.component.d.ts +3 -0
- package/dist/lib/components/artifact-viewer-panel.component.d.ts.map +1 -1
- package/dist/lib/components/artifact-viewer-panel.component.js +13 -2
- package/dist/lib/components/artifact-viewer-panel.component.js.map +1 -1
- package/dist/lib/components/base-artifact-viewer.component.d.ts +6 -0
- package/dist/lib/components/base-artifact-viewer.component.d.ts.map +1 -1
- package/dist/lib/components/base-artifact-viewer.component.js +15 -0
- package/dist/lib/components/base-artifact-viewer.component.js.map +1 -1
- package/dist/lib/components/file-artifact-toolbar.component.d.ts +42 -0
- package/dist/lib/components/file-artifact-toolbar.component.d.ts.map +1 -0
- package/dist/lib/components/file-artifact-toolbar.component.js +249 -0
- package/dist/lib/components/file-artifact-toolbar.component.js.map +1 -0
- package/dist/lib/components/plugins/docx-artifact-viewer.component.d.ts +40 -0
- package/dist/lib/components/plugins/docx-artifact-viewer.component.d.ts.map +1 -0
- package/dist/lib/components/plugins/docx-artifact-viewer.component.js +223 -0
- package/dist/lib/components/plugins/docx-artifact-viewer.component.js.map +1 -0
- package/dist/lib/components/plugins/pdf-artifact-viewer.component.d.ts +68 -0
- package/dist/lib/components/plugins/pdf-artifact-viewer.component.d.ts.map +1 -0
- package/dist/lib/components/plugins/pdf-artifact-viewer.component.js +340 -0
- package/dist/lib/components/plugins/pdf-artifact-viewer.component.js.map +1 -0
- package/dist/lib/components/plugins/xlsx-artifact-viewer.component.d.ts +44 -0
- package/dist/lib/components/plugins/xlsx-artifact-viewer.component.d.ts.map +1 -0
- package/dist/lib/components/plugins/xlsx-artifact-viewer.component.js +255 -0
- package/dist/lib/components/plugins/xlsx-artifact-viewer.component.js.map +1 -0
- package/dist/lib/services/artifact-file.service.d.ts +33 -0
- package/dist/lib/services/artifact-file.service.d.ts.map +1 -0
- package/dist/lib/services/artifact-file.service.js +87 -0
- package/dist/lib/services/artifact-file.service.js.map +1 -0
- package/dist/public-api.d.ts +5 -0
- package/dist/public-api.d.ts.map +1 -1
- package/dist/public-api.js +6 -0
- package/dist/public-api.js.map +1 -1
- package/package.json +20 -16
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base-artifact-viewer.component.js","sourceRoot":"","sources":["../../../src/lib/components/base-artifact-viewer.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAgB,KAAK,EAAQ,MAAM,eAAe,CAAC;;AAqErE;;;;GAIG;AAKH,MAAM,OAAgB,iCAAiC;IACrD;;OAEG;IACM,eAAe,CAA2B;IAEnD;;OAEG;IACM,MAAM,CAAU;IAEzB;;OAEG;IACM,QAAQ,GAAY,IAAI,CAAC;IAElC;;OAEG;IACM,QAAQ,CAAU;IAE3B;;;;;;;;;OASG;IACH,IAAW,wBAAwB;QACjC,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;;;;;;;;;;OAWG;IACH,IAAW,0BAA0B;QACnC,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;;;;;OAQG;IACH,IAAW,iBAAiB;QAC1B,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;;OAGG;IACO,UAAU;QAClB,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,OAAO,EAAE,CAAC;YACnC,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,IAAI,OAAO,IAAI,CAAC,eAAe,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;YACrD,OAAO,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC;QACtC,CAAC;QAED,wCAAwC;QACxC,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,eAAe,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IAC/D,CAAC;IAED;;OAEG;IACO,gBAAgB;QACxB,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC;YAClC,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAC7B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,2CAA2C,EAAE,KAAK,CAAC,CAAC;YAClE,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED;;OAEG;IACO,cAAc;QACtB,OAAO,IAAI,CAAC,eAAe,EAAE,IAAI,IAAI,mBAAmB,CAAC;IAC3D,CAAC;IAED;;OAEG;IACO,cAAc;QACtB,OAAO,IAAI,CAAC,eAAe,EAAE,WAAW,IAAI,EAAE,CAAC;IACjD,CAAC;IAED;;;;;OAKG;IACI,WAAW,CAAsB;IAExC;;;;;;OAMG;IACI,iBAAiB,CAAmC;IAE3D;;;;;;OAMG;IACH,IAAW,gBAAgB;QACzB,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;;;;;OAMG;IACI,kBAAkB;QACvB,+CAA+C;IACjD,CAAC;2HAjJmB,iCAAiC;6DAAjC,iCAAiC;;iFAAjC,iCAAiC;cAJtD,SAAS;eAAC;gBACT,UAAU,EAAE,KAAK;gBACjB,QAAQ,EAAE,EAAE;aACb;;kBAKE,KAAK;;kBAKL,KAAK;;kBAKL,KAAK;;kBAKL,KAAK;;kFAnBc,iCAAiC","sourcesContent":["import { Component, EventEmitter, Input, Type } from '@angular/core';\nimport { MJArtifactVersionEntity } from '@memberjunction/core-entities';\nimport { IArtifactViewerComponent } from '../interfaces/artifact-viewer-plugin.interface';\n\n/**\n * General-purpose navigation request emitted by artifact viewer plugins.\n * Allows plugins to request app-level navigation (e.g., switch to a specific\n * application, open a nav item, and set query parameters) without depending\n * on NavigationService directly.\n *\n * The event bubbles through ArtifactTypePluginViewer → ArtifactViewerPanel →\n * consumer (conversations, dashboards, etc.) → ultimately the Explorer wrapper\n * where NavigationService lives.\n */\nexport interface NavigationRequest {\n /** Target application name (e.g., 'Data Explorer'). If omitted, stays in current app. */\n appName?: string;\n\n /** Target navigation item label within the app (e.g., 'Queries') */\n navItemName: string;\n\n /** Query parameters to pass to the navigation target (e.g., { queryId: 'xxx' }) */\n queryParams?: Record<string, string>;\n}\n\n/**\n * Represents an additional tab that a plugin can provide to the artifact viewer.\n *\n * Tabs can render content in two ways:\n * 1. **String content** - Use `contentType` with `content` for built-in renderers (markdown, json, code, etc.)\n * 2. **Custom component** - Use `contentType: 'component'` with `component` and optional `componentInputs`\n *\n * @example\n * // String content tab\n * {\n * label: 'Code',\n * icon: 'fa-code',\n * contentType: 'code',\n * content: () => this.getCodeContent(),\n * language: 'typescript'\n * }\n *\n * @example\n * // Custom component tab\n * {\n * label: 'Data',\n * icon: 'fa-database',\n * contentType: 'component',\n * component: DataRequirementsViewerComponent,\n * componentInputs: { dataRequirements: this.spec.dataRequirements }\n * }\n */\nexport interface ArtifactViewerTab {\n /** Display label for the tab */\n label: string;\n /** Font Awesome icon class (without 'fas' prefix) */\n icon?: string;\n /** Type of content to render */\n contentType: 'plaintext' | 'html' | 'markdown' | 'json' | 'code' | 'component';\n /** String content or function returning string (for non-component tabs) */\n content?: string | (() => string);\n /** Language hint for code highlighting (used with 'code' contentType) */\n language?: string;\n /** Angular component class to render (used with 'component' contentType) */\n component?: Type<any>;\n /** Inputs to pass to the custom component */\n componentInputs?: Record<string, any> | (() => Record<string, any>);\n}\n\n/**\n * Abstract base component for all artifact viewer plugins.\n * Provides common functionality and enforces the contract.\n * Note: This is an abstract class and should not be declared in Angular module.\n */\n@Component({\n standalone: false,\n template: ''\n})\nexport abstract class BaseArtifactViewerPluginComponent implements IArtifactViewerComponent {\n /**\n * The artifact version to display\n */\n @Input() artifactVersion!: MJArtifactVersionEntity;\n\n /**\n * Optional: Custom height for the viewer (defaults to auto)\n */\n @Input() height?: string;\n\n /**\n * Optional: Whether the viewer is in readonly mode\n */\n @Input() readonly: boolean = true;\n\n /**\n * Optional: Additional CSS classes to apply\n */\n @Input() cssClass?: string;\n\n /**\n * Whether this plugin is showing an \"elevated\" display (e.g., extracted markdown/HTML)\n * vs raw content. When true and content type is JSON, the wrapper will show a JSON tab.\n *\n * Subclasses should override this getter to return the appropriate value based on their\n * current display state. For example, JSON plugin returns true when showing displayMarkdown\n * or displayHtml, false when showing raw JSON editor.\n *\n * Default: false (showing raw content)\n */\n public get isShowingElevatedDisplay(): boolean {\n return false;\n }\n\n /**\n * Whether the parent wrapper should show a raw content tab (e.g., JSON tab for JSON content).\n * This gives plugins fine-grained control over the parent's tab display.\n *\n * Use cases:\n * - JSON plugin with extract rules: true (show JSON tab to view source)\n * - Component plugin: true (show JSON tab even when displaying elevated component)\n * - Code plugin: false (already showing raw content)\n *\n * Subclasses can override this to control parent behavior.\n * Default: true (parent should show raw content tab if applicable)\n */\n public get parentShouldShowRawContent(): boolean {\n return true;\n }\n\n /**\n * Whether this plugin has content to display in the Display tab.\n * Used by the parent wrapper to determine if the Display tab should be shown.\n *\n * Subclasses should override this getter to return true when they have\n * meaningful content to display.\n *\n * Default: false (subclasses must opt-in to showing Display tab)\n */\n public get hasDisplayContent(): boolean {\n return false;\n }\n\n /**\n * Get the content from the artifact version.\n * Handles both string content and JSON objects.\n */\n protected getContent(): string {\n if (!this.artifactVersion?.Content) {\n return '';\n }\n\n if (typeof this.artifactVersion.Content === 'string') {\n return this.artifactVersion.Content;\n }\n\n // If Content is an object, stringify it\n return JSON.stringify(this.artifactVersion.Content, null, 2);\n }\n\n /**\n * Parse JSON content safely\n */\n protected parseJsonContent<T = any>(): T | null {\n try {\n const content = this.getContent();\n return JSON.parse(content);\n } catch (error) {\n console.error('Failed to parse artifact content as JSON:', error);\n return null;\n }\n }\n\n /**\n * Get a safe display name for the artifact version\n */\n protected getDisplayName(): string {\n return this.artifactVersion?.Name || 'Untitled Artifact';\n }\n\n /**\n * Get the description\n */\n protected getDescription(): string {\n return this.artifactVersion?.Description || '';\n }\n\n /**\n * Optional: Emitted when the plugin's additional tabs change after async loading.\n * Plugins that load data asynchronously (e.g., from a component registry) should\n * emit this when loading completes and new tabs become available. The parent panel\n * listens for this to re-run change detection and render the updated tab list.\n */\n public tabsChanged?: EventEmitter<void>;\n\n /**\n * Optional: Emitted when the plugin wants to navigate to a different app/nav item.\n * Plugins that need app-level navigation (e.g., \"Open Query\" navigating to the\n * Data Explorer's Queries nav item) should instantiate this emitter and emit a\n * NavigationRequest. The event bubbles up through the artifact viewer chain to the\n * host application where NavigationService handles the actual navigation.\n */\n public navigationRequest?: EventEmitter<NavigationRequest>;\n\n /**\n * Whether this plugin supports user feedback.\n * Plugins that return true will get a feedback button in the artifact viewer header.\n * Override this getter to return true when feedback is available.\n *\n * Default: false\n */\n public get SupportsFeedback(): boolean {\n return false;\n }\n\n /**\n * Ask the user for feedback on the current artifact.\n * Called by the artifact viewer panel when the user clicks the feedback button in the header.\n * Each plugin implements its own feedback UX (dialog, panel, inline form, etc.).\n *\n * Default: no-op. Subclasses should override to implement artifact-type-specific feedback.\n */\n public AskUserForFeedback(): void {\n // Subclasses override to implement feedback UX\n }\n\n /**\n * Get additional tabs that this plugin wants to provide to the artifact viewer.\n * Override this method to provide custom tabs for viewing metadata, code, etc.\n *\n * @returns Array of tab definitions, or undefined if no additional tabs\n */\n public GetAdditionalTabs?(): ArtifactViewerTab[];\n\n /**\n * Get list of standard tabs that this plugin wants to hide/remove.\n * Use this when the plugin provides custom alternatives to standard tabs.\n *\n * Example: Component viewer provides custom \"Resolved JSON\" tab, so removes standard \"JSON\" tab\n *\n * @returns Array of standard tab names to remove (case-insensitive)\n * Valid values: 'JSON', 'Details', 'Links'\n * Note: 'Display' cannot be removed (it's the main view)\n */\n public GetStandardTabRemovals?(): string[];\n}\n"]}
|
|
1
|
+
{"version":3,"file":"base-artifact-viewer.component.js","sourceRoot":"","sources":["../../../src/lib/components/base-artifact-viewer.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAgB,KAAK,EAAQ,MAAM,eAAe,CAAC;;AAqErE;;;;GAIG;AAKH,MAAM,OAAgB,iCAAiC;IACrD;;OAEG;IACM,eAAe,CAA2B;IAEnD;;OAEG;IACM,MAAM,CAAU;IAEzB;;OAEG;IACM,QAAQ,GAAY,IAAI,CAAC;IAElC;;OAEG;IACM,QAAQ,CAAU;IAE3B;;;;;;;;;OASG;IACH,IAAW,wBAAwB;QACjC,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;;;;;;;;;;OAWG;IACH,IAAW,0BAA0B;QACnC,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;;;;;OAQG;IACH,IAAW,iBAAiB;QAC1B,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;;OAGG;IACO,UAAU;QAClB,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,OAAO,EAAE,CAAC;YACnC,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,IAAI,OAAO,IAAI,CAAC,eAAe,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;YACrD,OAAO,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC;QACtC,CAAC;QAED,wCAAwC;QACxC,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,eAAe,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IAC/D,CAAC;IAED;;OAEG;IACO,gBAAgB;QACxB,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC;YAClC,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAC7B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,2CAA2C,EAAE,KAAK,CAAC,CAAC;YAClE,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED;;OAEG;IACO,cAAc;QACtB,OAAO,IAAI,CAAC,eAAe,EAAE,IAAI,IAAI,mBAAmB,CAAC;IAC3D,CAAC;IAED;;OAEG;IACO,cAAc;QACtB,OAAO,IAAI,CAAC,eAAe,EAAE,WAAW,IAAI,EAAE,CAAC;IACjD,CAAC;IAED;;;;;OAKG;IACI,WAAW,CAAsB;IAExC;;;;;;OAMG;IACI,iBAAiB,CAAmC;IAE3D;;;;;;OAMG;IACH,IAAW,gBAAgB;QACzB,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;;;;;OAMG;IACI,kBAAkB;QACvB,+CAA+C;IACjD,CAAC;IAsBD;;;;OAIG;IACO,KAAK,CAAC,sBAAsB,CAAC,GAAW,EAAE,QAAgB;QAClE,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,CAAC;QAClC,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QACnC,MAAM,SAAS,GAAG,GAAG,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;QAC5C,MAAM,MAAM,GAAG,QAAQ,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;QAC3C,MAAM,CAAC,IAAI,GAAG,SAAS,CAAC;QACxB,MAAM,CAAC,QAAQ,GAAG,QAAQ,CAAC;QAC3B,MAAM,CAAC,KAAK,EAAE,CAAC;QACf,GAAG,CAAC,eAAe,CAAC,SAAS,CAAC,CAAC;IACjC,CAAC;2HArLmB,iCAAiC;6DAAjC,iCAAiC;;iFAAjC,iCAAiC;cAJtD,SAAS;eAAC;gBACT,UAAU,EAAE,KAAK;gBACjB,QAAQ,EAAE,EAAE;aACb;;kBAKE,KAAK;;kBAKL,KAAK;;kBAKL,KAAK;;kBAKL,KAAK;;kFAnBc,iCAAiC","sourcesContent":["import { Component, EventEmitter, Input, Type } from '@angular/core';\nimport { MJArtifactVersionEntity } from '@memberjunction/core-entities';\nimport { IArtifactViewerComponent } from '../interfaces/artifact-viewer-plugin.interface';\n\n/**\n * General-purpose navigation request emitted by artifact viewer plugins.\n * Allows plugins to request app-level navigation (e.g., switch to a specific\n * application, open a nav item, and set query parameters) without depending\n * on NavigationService directly.\n *\n * The event bubbles through ArtifactTypePluginViewer → ArtifactViewerPanel →\n * consumer (conversations, dashboards, etc.) → ultimately the Explorer wrapper\n * where NavigationService lives.\n */\nexport interface NavigationRequest {\n /** Target application name (e.g., 'Data Explorer'). If omitted, stays in current app. */\n appName?: string;\n\n /** Target navigation item label within the app (e.g., 'Queries') */\n navItemName: string;\n\n /** Query parameters to pass to the navigation target (e.g., { queryId: 'xxx' }) */\n queryParams?: Record<string, string>;\n}\n\n/**\n * Represents an additional tab that a plugin can provide to the artifact viewer.\n *\n * Tabs can render content in two ways:\n * 1. **String content** - Use `contentType` with `content` for built-in renderers (markdown, json, code, etc.)\n * 2. **Custom component** - Use `contentType: 'component'` with `component` and optional `componentInputs`\n *\n * @example\n * // String content tab\n * {\n * label: 'Code',\n * icon: 'fa-code',\n * contentType: 'code',\n * content: () => this.getCodeContent(),\n * language: 'typescript'\n * }\n *\n * @example\n * // Custom component tab\n * {\n * label: 'Data',\n * icon: 'fa-database',\n * contentType: 'component',\n * component: DataRequirementsViewerComponent,\n * componentInputs: { dataRequirements: this.spec.dataRequirements }\n * }\n */\nexport interface ArtifactViewerTab {\n /** Display label for the tab */\n label: string;\n /** Font Awesome icon class (without 'fas' prefix) */\n icon?: string;\n /** Type of content to render */\n contentType: 'plaintext' | 'html' | 'markdown' | 'json' | 'code' | 'component';\n /** String content or function returning string (for non-component tabs) */\n content?: string | (() => string);\n /** Language hint for code highlighting (used with 'code' contentType) */\n language?: string;\n /** Angular component class to render (used with 'component' contentType) */\n component?: Type<any>;\n /** Inputs to pass to the custom component */\n componentInputs?: Record<string, any> | (() => Record<string, any>);\n}\n\n/**\n * Abstract base component for all artifact viewer plugins.\n * Provides common functionality and enforces the contract.\n * Note: This is an abstract class and should not be declared in Angular module.\n */\n@Component({\n standalone: false,\n template: ''\n})\nexport abstract class BaseArtifactViewerPluginComponent implements IArtifactViewerComponent {\n /**\n * The artifact version to display\n */\n @Input() artifactVersion!: MJArtifactVersionEntity;\n\n /**\n * Optional: Custom height for the viewer (defaults to auto)\n */\n @Input() height?: string;\n\n /**\n * Optional: Whether the viewer is in readonly mode\n */\n @Input() readonly: boolean = true;\n\n /**\n * Optional: Additional CSS classes to apply\n */\n @Input() cssClass?: string;\n\n /**\n * Whether this plugin is showing an \"elevated\" display (e.g., extracted markdown/HTML)\n * vs raw content. When true and content type is JSON, the wrapper will show a JSON tab.\n *\n * Subclasses should override this getter to return the appropriate value based on their\n * current display state. For example, JSON plugin returns true when showing displayMarkdown\n * or displayHtml, false when showing raw JSON editor.\n *\n * Default: false (showing raw content)\n */\n public get isShowingElevatedDisplay(): boolean {\n return false;\n }\n\n /**\n * Whether the parent wrapper should show a raw content tab (e.g., JSON tab for JSON content).\n * This gives plugins fine-grained control over the parent's tab display.\n *\n * Use cases:\n * - JSON plugin with extract rules: true (show JSON tab to view source)\n * - Component plugin: true (show JSON tab even when displaying elevated component)\n * - Code plugin: false (already showing raw content)\n *\n * Subclasses can override this to control parent behavior.\n * Default: true (parent should show raw content tab if applicable)\n */\n public get parentShouldShowRawContent(): boolean {\n return true;\n }\n\n /**\n * Whether this plugin has content to display in the Display tab.\n * Used by the parent wrapper to determine if the Display tab should be shown.\n *\n * Subclasses should override this getter to return true when they have\n * meaningful content to display.\n *\n * Default: false (subclasses must opt-in to showing Display tab)\n */\n public get hasDisplayContent(): boolean {\n return false;\n }\n\n /**\n * Get the content from the artifact version.\n * Handles both string content and JSON objects.\n */\n protected getContent(): string {\n if (!this.artifactVersion?.Content) {\n return '';\n }\n\n if (typeof this.artifactVersion.Content === 'string') {\n return this.artifactVersion.Content;\n }\n\n // If Content is an object, stringify it\n return JSON.stringify(this.artifactVersion.Content, null, 2);\n }\n\n /**\n * Parse JSON content safely\n */\n protected parseJsonContent<T = any>(): T | null {\n try {\n const content = this.getContent();\n return JSON.parse(content);\n } catch (error) {\n console.error('Failed to parse artifact content as JSON:', error);\n return null;\n }\n }\n\n /**\n * Get a safe display name for the artifact version\n */\n protected getDisplayName(): string {\n return this.artifactVersion?.Name || 'Untitled Artifact';\n }\n\n /**\n * Get the description\n */\n protected getDescription(): string {\n return this.artifactVersion?.Description || '';\n }\n\n /**\n * Optional: Emitted when the plugin's additional tabs change after async loading.\n * Plugins that load data asynchronously (e.g., from a component registry) should\n * emit this when loading completes and new tabs become available. The parent panel\n * listens for this to re-run change detection and render the updated tab list.\n */\n public tabsChanged?: EventEmitter<void>;\n\n /**\n * Optional: Emitted when the plugin wants to navigate to a different app/nav item.\n * Plugins that need app-level navigation (e.g., \"Open Query\" navigating to the\n * Data Explorer's Queries nav item) should instantiate this emitter and emit a\n * NavigationRequest. The event bubbles up through the artifact viewer chain to the\n * host application where NavigationService handles the actual navigation.\n */\n public navigationRequest?: EventEmitter<NavigationRequest>;\n\n /**\n * Whether this plugin supports user feedback.\n * Plugins that return true will get a feedback button in the artifact viewer header.\n * Override this getter to return true when feedback is available.\n *\n * Default: false\n */\n public get SupportsFeedback(): boolean {\n return false;\n }\n\n /**\n * Ask the user for feedback on the current artifact.\n * Called by the artifact viewer panel when the user clicks the feedback button in the header.\n * Each plugin implements its own feedback UX (dialog, panel, inline form, etc.).\n *\n * Default: no-op. Subclasses should override to implement artifact-type-specific feedback.\n */\n public AskUserForFeedback(): void {\n // Subclasses override to implement feedback UX\n }\n\n /**\n * Get additional tabs that this plugin wants to provide to the artifact viewer.\n * Override this method to provide custom tabs for viewing metadata, code, etc.\n *\n * @returns Array of tab definitions, or undefined if no additional tabs\n */\n public GetAdditionalTabs?(): ArtifactViewerTab[];\n\n /**\n * Get list of standard tabs that this plugin wants to hide/remove.\n * Use this when the plugin provides custom alternatives to standard tabs.\n *\n * Example: Component viewer provides custom \"Resolved JSON\" tab, so removes standard \"JSON\" tab\n *\n * @returns Array of standard tab names to remove (case-insensitive)\n * Valid values: 'JSON', 'Details', 'Links'\n * Note: 'Display' cannot be removed (it's the main view)\n */\n public GetStandardTabRemovals?(): string[];\n\n /**\n * Trigger a browser file download from a URL.\n * Shared by all file-backed viewer plugins (PDF, XLSX, DOCX) so the fetch →\n * blob → object-URL → anchor pattern lives in exactly one place.\n */\n protected async triggerBrowserDownload(url: string, fileName: string): Promise<void> {\n const response = await fetch(url);\n const blob = await response.blob();\n const objectUrl = URL.createObjectURL(blob);\n const anchor = document.createElement('a');\n anchor.href = objectUrl;\n anchor.download = fileName;\n anchor.click();\n URL.revokeObjectURL(objectUrl);\n }\n}\n"]}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { EventEmitter } from '@angular/core';
|
|
2
|
+
import * as i0 from "@angular/core";
|
|
3
|
+
/**
|
|
4
|
+
* Shared toolbar rendered by file artifact viewer plugins (PDF, XLSX, DOCX).
|
|
5
|
+
* Provides download, print, page-navigation, and optional zoom controls in a
|
|
6
|
+
* consistent style. All colors use design tokens so the toolbar adapts to
|
|
7
|
+
* light/dark themes.
|
|
8
|
+
*/
|
|
9
|
+
export declare class FileArtifactToolbarComponent {
|
|
10
|
+
/** Display name shown in the center of the toolbar. */
|
|
11
|
+
fileName: string;
|
|
12
|
+
/** Current page number (1-based). Only shown when totalPages > 1. */
|
|
13
|
+
currentPage: number;
|
|
14
|
+
/** Total page count. Pass 1 (default) to hide navigation arrows. */
|
|
15
|
+
totalPages: number;
|
|
16
|
+
/** Whether the download button should show a spinner. */
|
|
17
|
+
isDownloading: boolean;
|
|
18
|
+
/** Whether to show the Print button (e.g. hide for binary spreadsheets). */
|
|
19
|
+
showPrint: boolean;
|
|
20
|
+
/** Whether to show zoom in/out/reset controls. Only PDF needs this. */
|
|
21
|
+
showZoom: boolean;
|
|
22
|
+
/** Current zoom level as a percentage integer (e.g. 100 = 100%). */
|
|
23
|
+
zoomPercent: number;
|
|
24
|
+
/** Whether zooming in further is possible. */
|
|
25
|
+
canZoomIn: boolean;
|
|
26
|
+
/** Whether zooming out further is possible. */
|
|
27
|
+
canZoomOut: boolean;
|
|
28
|
+
download: EventEmitter<void>;
|
|
29
|
+
print: EventEmitter<void>;
|
|
30
|
+
prevPage: EventEmitter<void>;
|
|
31
|
+
nextPage: EventEmitter<void>;
|
|
32
|
+
/** Emits the validated target page number when the user edits the page input. */
|
|
33
|
+
pageChange: EventEmitter<number>;
|
|
34
|
+
zoomIn: EventEmitter<void>;
|
|
35
|
+
zoomOut: EventEmitter<void>;
|
|
36
|
+
/** Emitted when the user clicks the zoom-level badge to reset to 100%. */
|
|
37
|
+
zoomReset: EventEmitter<void>;
|
|
38
|
+
onPageInputChange(event: Event): void;
|
|
39
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<FileArtifactToolbarComponent, never>;
|
|
40
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<FileArtifactToolbarComponent, "mj-file-artifact-toolbar", never, { "fileName": { "alias": "fileName"; "required": false; }; "currentPage": { "alias": "currentPage"; "required": false; }; "totalPages": { "alias": "totalPages"; "required": false; }; "isDownloading": { "alias": "isDownloading"; "required": false; }; "showPrint": { "alias": "showPrint"; "required": false; }; "showZoom": { "alias": "showZoom"; "required": false; }; "zoomPercent": { "alias": "zoomPercent"; "required": false; }; "canZoomIn": { "alias": "canZoomIn"; "required": false; }; "canZoomOut": { "alias": "canZoomOut"; "required": false; }; }, { "download": "download"; "print": "print"; "prevPage": "prevPage"; "nextPage": "nextPage"; "pageChange": "pageChange"; "zoomIn": "zoomIn"; "zoomOut": "zoomOut"; "zoomReset": "zoomReset"; }, never, never, false, never>;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=file-artifact-toolbar.component.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-artifact-toolbar.component.d.ts","sourceRoot":"","sources":["../../../src/lib/components/file-artifact-toolbar.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,YAAY,EAA2B,MAAM,eAAe,CAAC;;AAEhG;;;;;GAKG;AACH,qBAqPa,4BAA4B;IACvC,uDAAuD;IAC9C,QAAQ,SAAM;IAEvB,qEAAqE;IAC5D,WAAW,SAAK;IAEzB,oEAAoE;IAC3D,UAAU,SAAK;IAExB,yDAAyD;IAChD,aAAa,UAAS;IAE/B,4EAA4E;IACnE,SAAS,UAAQ;IAE1B,uEAAuE;IAC9D,QAAQ,UAAS;IAE1B,oEAAoE;IAC3D,WAAW,SAAO;IAE3B,8CAA8C;IACrC,SAAS,UAAQ;IAE1B,+CAA+C;IACtC,UAAU,UAAQ;IAEjB,QAAQ,qBAA4B;IACpC,KAAK,qBAA4B;IACjC,QAAQ,qBAA4B;IACpC,QAAQ,qBAA4B;IAC9C,iFAAiF;IACvE,UAAU,uBAA8B;IACxC,MAAM,qBAA4B;IAClC,OAAO,qBAA4B;IAC7C,0EAA0E;IAChE,SAAS,qBAA4B;IAExC,iBAAiB,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI;yCAvCjC,4BAA4B;2CAA5B,4BAA4B;CAiDxC"}
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy } from '@angular/core';
|
|
2
|
+
import * as i0 from "@angular/core";
|
|
3
|
+
function FileArtifactToolbarComponent_Conditional_2_Template(rf, ctx) { if (rf & 1) {
|
|
4
|
+
const _r1 = i0.ɵɵgetCurrentView();
|
|
5
|
+
i0.ɵɵelementStart(0, "div", 2)(1, "button", 11);
|
|
6
|
+
i0.ɵɵlistener("click", function FileArtifactToolbarComponent_Conditional_2_Template_button_click_1_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.prevPage.emit()); });
|
|
7
|
+
i0.ɵɵelement(2, "i", 12);
|
|
8
|
+
i0.ɵɵelementEnd();
|
|
9
|
+
i0.ɵɵelementStart(3, "input", 13);
|
|
10
|
+
i0.ɵɵlistener("change", function FileArtifactToolbarComponent_Conditional_2_Template_input_change_3_listener($event) { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onPageInputChange($event)); })("keydown.enter", function FileArtifactToolbarComponent_Conditional_2_Template_input_keydown_enter_3_listener($event) { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onPageInputChange($event)); });
|
|
11
|
+
i0.ɵɵelementEnd();
|
|
12
|
+
i0.ɵɵelementStart(4, "span", 14);
|
|
13
|
+
i0.ɵɵtext(5);
|
|
14
|
+
i0.ɵɵelementEnd();
|
|
15
|
+
i0.ɵɵelementStart(6, "button", 15);
|
|
16
|
+
i0.ɵɵlistener("click", function FileArtifactToolbarComponent_Conditional_2_Template_button_click_6_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.nextPage.emit()); });
|
|
17
|
+
i0.ɵɵelement(7, "i", 16);
|
|
18
|
+
i0.ɵɵelementEnd()();
|
|
19
|
+
} if (rf & 2) {
|
|
20
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
21
|
+
i0.ɵɵadvance();
|
|
22
|
+
i0.ɵɵproperty("disabled", ctx_r1.currentPage <= 1);
|
|
23
|
+
i0.ɵɵadvance(2);
|
|
24
|
+
i0.ɵɵproperty("value", ctx_r1.currentPage)("min", 1)("max", ctx_r1.totalPages);
|
|
25
|
+
i0.ɵɵadvance(2);
|
|
26
|
+
i0.ɵɵtextInterpolate1("/ ", ctx_r1.totalPages);
|
|
27
|
+
i0.ɵɵadvance();
|
|
28
|
+
i0.ɵɵproperty("disabled", ctx_r1.currentPage >= ctx_r1.totalPages);
|
|
29
|
+
} }
|
|
30
|
+
function FileArtifactToolbarComponent_Conditional_3_Template(rf, ctx) { if (rf & 1) {
|
|
31
|
+
const _r3 = i0.ɵɵgetCurrentView();
|
|
32
|
+
i0.ɵɵelementStart(0, "div", 3)(1, "button", 17);
|
|
33
|
+
i0.ɵɵlistener("click", function FileArtifactToolbarComponent_Conditional_3_Template_button_click_1_listener() { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.zoomOut.emit()); });
|
|
34
|
+
i0.ɵɵelement(2, "i", 18);
|
|
35
|
+
i0.ɵɵelementEnd();
|
|
36
|
+
i0.ɵɵelementStart(3, "button", 19);
|
|
37
|
+
i0.ɵɵlistener("click", function FileArtifactToolbarComponent_Conditional_3_Template_button_click_3_listener() { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.zoomReset.emit()); });
|
|
38
|
+
i0.ɵɵtext(4);
|
|
39
|
+
i0.ɵɵelementEnd();
|
|
40
|
+
i0.ɵɵelementStart(5, "button", 20);
|
|
41
|
+
i0.ɵɵlistener("click", function FileArtifactToolbarComponent_Conditional_3_Template_button_click_5_listener() { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.zoomIn.emit()); });
|
|
42
|
+
i0.ɵɵelement(6, "i", 21);
|
|
43
|
+
i0.ɵɵelementEnd()();
|
|
44
|
+
} if (rf & 2) {
|
|
45
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
46
|
+
i0.ɵɵadvance();
|
|
47
|
+
i0.ɵɵproperty("disabled", !ctx_r1.canZoomOut);
|
|
48
|
+
i0.ɵɵadvance(3);
|
|
49
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r1.zoomPercent, "% ");
|
|
50
|
+
i0.ɵɵadvance();
|
|
51
|
+
i0.ɵɵproperty("disabled", !ctx_r1.canZoomIn);
|
|
52
|
+
} }
|
|
53
|
+
function FileArtifactToolbarComponent_Conditional_7_Template(rf, ctx) { if (rf & 1) {
|
|
54
|
+
const _r4 = i0.ɵɵgetCurrentView();
|
|
55
|
+
i0.ɵɵelementStart(0, "button", 22);
|
|
56
|
+
i0.ɵɵlistener("click", function FileArtifactToolbarComponent_Conditional_7_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r4); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.print.emit()); });
|
|
57
|
+
i0.ɵɵelement(1, "i", 23);
|
|
58
|
+
i0.ɵɵelementStart(2, "span", 10);
|
|
59
|
+
i0.ɵɵtext(3, "Print");
|
|
60
|
+
i0.ɵɵelementEnd()();
|
|
61
|
+
} }
|
|
62
|
+
function FileArtifactToolbarComponent_Conditional_9_Template(rf, ctx) { if (rf & 1) {
|
|
63
|
+
i0.ɵɵelement(0, "i", 8);
|
|
64
|
+
} }
|
|
65
|
+
function FileArtifactToolbarComponent_Conditional_10_Template(rf, ctx) { if (rf & 1) {
|
|
66
|
+
i0.ɵɵelement(0, "i", 9);
|
|
67
|
+
} }
|
|
68
|
+
/**
|
|
69
|
+
* Shared toolbar rendered by file artifact viewer plugins (PDF, XLSX, DOCX).
|
|
70
|
+
* Provides download, print, page-navigation, and optional zoom controls in a
|
|
71
|
+
* consistent style. All colors use design tokens so the toolbar adapts to
|
|
72
|
+
* light/dark themes.
|
|
73
|
+
*/
|
|
74
|
+
export class FileArtifactToolbarComponent {
|
|
75
|
+
/** Display name shown in the center of the toolbar. */
|
|
76
|
+
fileName = '';
|
|
77
|
+
/** Current page number (1-based). Only shown when totalPages > 1. */
|
|
78
|
+
currentPage = 1;
|
|
79
|
+
/** Total page count. Pass 1 (default) to hide navigation arrows. */
|
|
80
|
+
totalPages = 1;
|
|
81
|
+
/** Whether the download button should show a spinner. */
|
|
82
|
+
isDownloading = false;
|
|
83
|
+
/** Whether to show the Print button (e.g. hide for binary spreadsheets). */
|
|
84
|
+
showPrint = true;
|
|
85
|
+
/** Whether to show zoom in/out/reset controls. Only PDF needs this. */
|
|
86
|
+
showZoom = false;
|
|
87
|
+
/** Current zoom level as a percentage integer (e.g. 100 = 100%). */
|
|
88
|
+
zoomPercent = 100;
|
|
89
|
+
/** Whether zooming in further is possible. */
|
|
90
|
+
canZoomIn = true;
|
|
91
|
+
/** Whether zooming out further is possible. */
|
|
92
|
+
canZoomOut = true;
|
|
93
|
+
download = new EventEmitter();
|
|
94
|
+
print = new EventEmitter();
|
|
95
|
+
prevPage = new EventEmitter();
|
|
96
|
+
nextPage = new EventEmitter();
|
|
97
|
+
/** Emits the validated target page number when the user edits the page input. */
|
|
98
|
+
pageChange = new EventEmitter();
|
|
99
|
+
zoomIn = new EventEmitter();
|
|
100
|
+
zoomOut = new EventEmitter();
|
|
101
|
+
/** Emitted when the user clicks the zoom-level badge to reset to 100%. */
|
|
102
|
+
zoomReset = new EventEmitter();
|
|
103
|
+
onPageInputChange(event) {
|
|
104
|
+
const input = event.target;
|
|
105
|
+
const value = parseInt(input.value, 10);
|
|
106
|
+
if (!isNaN(value) && value >= 1 && value <= this.totalPages) {
|
|
107
|
+
this.pageChange.emit(value);
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
// Snap back to current valid page
|
|
111
|
+
input.value = String(this.currentPage);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
static ɵfac = function FileArtifactToolbarComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || FileArtifactToolbarComponent)(); };
|
|
115
|
+
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: FileArtifactToolbarComponent, selectors: [["mj-file-artifact-toolbar"]], inputs: { fileName: "fileName", currentPage: "currentPage", totalPages: "totalPages", isDownloading: "isDownloading", showPrint: "showPrint", showZoom: "showZoom", zoomPercent: "zoomPercent", canZoomIn: "canZoomIn", canZoomOut: "canZoomOut" }, outputs: { download: "download", print: "print", prevPage: "prevPage", nextPage: "nextPage", pageChange: "pageChange", zoomIn: "zoomIn", zoomOut: "zoomOut", zoomReset: "zoomReset" }, standalone: false, decls: 13, vars: 7, consts: [[1, "file-toolbar"], [1, "file-toolbar__left"], [1, "file-toolbar__nav"], [1, "file-toolbar__zoom"], [1, "file-toolbar__title", 3, "title"], [1, "file-toolbar__actions"], ["title", "Print", 1, "file-toolbar__btn"], ["title", "Download file", 1, "file-toolbar__btn", "file-toolbar__btn--primary", 3, "click", "disabled"], [1, "fas", "fa-spinner", "fa-spin"], [1, "fas", "fa-download"], [1, "file-toolbar__btn-label"], ["title", "Previous page", 1, "file-toolbar__icon-btn", 3, "click", "disabled"], [1, "fas", "fa-chevron-left"], ["type", "number", "title", "Jump to page", 1, "file-toolbar__page-input", 3, "change", "keydown.enter", "value", "min", "max"], [1, "file-toolbar__page-of"], ["title", "Next page", 1, "file-toolbar__icon-btn", 3, "click", "disabled"], [1, "fas", "fa-chevron-right"], ["title", "Zoom out", 1, "file-toolbar__icon-btn", 3, "click", "disabled"], [1, "fas", "fa-minus"], ["title", "Reset zoom to 100%", 1, "file-toolbar__zoom-level", 3, "click"], ["title", "Zoom in", 1, "file-toolbar__icon-btn", 3, "click", "disabled"], [1, "fas", "fa-plus"], ["title", "Print", 1, "file-toolbar__btn", 3, "click"], [1, "fas", "fa-print"]], template: function FileArtifactToolbarComponent_Template(rf, ctx) { if (rf & 1) {
|
|
116
|
+
i0.ɵɵelementStart(0, "div", 0)(1, "div", 1);
|
|
117
|
+
i0.ɵɵconditionalCreate(2, FileArtifactToolbarComponent_Conditional_2_Template, 8, 6, "div", 2);
|
|
118
|
+
i0.ɵɵconditionalCreate(3, FileArtifactToolbarComponent_Conditional_3_Template, 7, 3, "div", 3);
|
|
119
|
+
i0.ɵɵelementEnd();
|
|
120
|
+
i0.ɵɵelementStart(4, "div", 4);
|
|
121
|
+
i0.ɵɵtext(5);
|
|
122
|
+
i0.ɵɵelementEnd();
|
|
123
|
+
i0.ɵɵelementStart(6, "div", 5);
|
|
124
|
+
i0.ɵɵconditionalCreate(7, FileArtifactToolbarComponent_Conditional_7_Template, 4, 0, "button", 6);
|
|
125
|
+
i0.ɵɵelementStart(8, "button", 7);
|
|
126
|
+
i0.ɵɵlistener("click", function FileArtifactToolbarComponent_Template_button_click_8_listener() { return ctx.download.emit(); });
|
|
127
|
+
i0.ɵɵconditionalCreate(9, FileArtifactToolbarComponent_Conditional_9_Template, 1, 0, "i", 8)(10, FileArtifactToolbarComponent_Conditional_10_Template, 1, 0, "i", 9);
|
|
128
|
+
i0.ɵɵelementStart(11, "span", 10);
|
|
129
|
+
i0.ɵɵtext(12, "Download");
|
|
130
|
+
i0.ɵɵelementEnd()()()();
|
|
131
|
+
} if (rf & 2) {
|
|
132
|
+
i0.ɵɵadvance(2);
|
|
133
|
+
i0.ɵɵconditional(ctx.totalPages > 1 ? 2 : -1);
|
|
134
|
+
i0.ɵɵadvance();
|
|
135
|
+
i0.ɵɵconditional(ctx.showZoom ? 3 : -1);
|
|
136
|
+
i0.ɵɵadvance();
|
|
137
|
+
i0.ɵɵproperty("title", ctx.fileName);
|
|
138
|
+
i0.ɵɵadvance();
|
|
139
|
+
i0.ɵɵtextInterpolate(ctx.fileName);
|
|
140
|
+
i0.ɵɵadvance(2);
|
|
141
|
+
i0.ɵɵconditional(ctx.showPrint ? 7 : -1);
|
|
142
|
+
i0.ɵɵadvance();
|
|
143
|
+
i0.ɵɵproperty("disabled", ctx.isDownloading);
|
|
144
|
+
i0.ɵɵadvance();
|
|
145
|
+
i0.ɵɵconditional(ctx.isDownloading ? 9 : 10);
|
|
146
|
+
} }, styles: [".file-toolbar[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n padding: 6px 12px;\n background: var(--mj-bg-surface-card);\n border-bottom: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n }\n\n .file-toolbar__left[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n min-width: 0;\n }\n\n .file-toolbar__nav[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 3px;\n }\n\n .file-toolbar__zoom[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 2px;\n border-left: 1px solid var(--mj-border-default);\n padding-left: 8px;\n }\n\n .file-toolbar__title[_ngcontent-%COMP%] {\n flex: 1;\n text-align: center;\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n padding: 0 8px;\n }\n\n .file-toolbar__actions[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 4px;\n min-width: 0;\n }\n\n \n\n .file-toolbar__icon-btn[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 26px;\n height: 26px;\n padding: 0;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 4px;\n color: var(--mj-text-secondary);\n font-size: 11px;\n cursor: pointer;\n transition: background 0.15s, border-color 0.15s, color 0.15s;\n }\n\n .file-toolbar__icon-btn[_ngcontent-%COMP%]:hover:not([disabled]) {\n background: var(--mj-bg-surface-hover);\n border-color: var(--mj-border-strong);\n color: var(--mj-text-primary);\n }\n\n .file-toolbar__icon-btn[disabled][_ngcontent-%COMP%] {\n opacity: 0.4;\n cursor: not-allowed;\n }\n\n \n\n .file-toolbar__page-input[_ngcontent-%COMP%] {\n width: 40px;\n height: 26px;\n padding: 0 4px;\n text-align: center;\n font-size: 12px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 4px;\n color: var(--mj-text-primary);\n \n\n -moz-appearance: textfield;\n }\n\n .file-toolbar__page-input[_ngcontent-%COMP%]::-webkit-inner-spin-button, \n .file-toolbar__page-input[_ngcontent-%COMP%]::-webkit-outer-spin-button {\n -webkit-appearance: none;\n margin: 0;\n }\n\n .file-toolbar__page-input[_ngcontent-%COMP%]:focus {\n outline: none;\n border-color: var(--mj-border-focus);\n box-shadow: 0 0 0 2px color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n }\n\n .file-toolbar__page-of[_ngcontent-%COMP%] {\n font-size: 12px;\n color: var(--mj-text-muted);\n white-space: nowrap;\n }\n\n \n\n .file-toolbar__zoom-level[_ngcontent-%COMP%] {\n min-width: 42px;\n height: 26px;\n padding: 0 4px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 4px;\n color: var(--mj-text-secondary);\n font-size: 12px;\n text-align: center;\n cursor: pointer;\n transition: background 0.15s, color 0.15s;\n }\n\n .file-toolbar__zoom-level[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n }\n\n \n\n .file-toolbar__btn[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 5px;\n padding: 5px 10px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 4px;\n color: var(--mj-text-secondary);\n font-size: 12px;\n cursor: pointer;\n transition: background 0.15s, border-color 0.15s, color 0.15s;\n }\n\n .file-toolbar__btn[_ngcontent-%COMP%]:hover:not([disabled]) {\n background: var(--mj-bg-surface-hover);\n border-color: var(--mj-border-strong);\n color: var(--mj-text-primary);\n }\n\n .file-toolbar__btn[disabled][_ngcontent-%COMP%] {\n opacity: 0.4;\n cursor: not-allowed;\n }\n\n .file-toolbar__btn--primary[_ngcontent-%COMP%] {\n background: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n }\n\n .file-toolbar__btn--primary[_ngcontent-%COMP%]:hover:not([disabled]) {\n background: var(--mj-brand-primary-hover);\n border-color: var(--mj-brand-primary-hover);\n color: var(--mj-text-inverse);\n }\n\n @media (max-width: 480px) {\n .file-toolbar__btn-label[_ngcontent-%COMP%] {\n display: none;\n }\n }"], changeDetection: 0 });
|
|
147
|
+
}
|
|
148
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(FileArtifactToolbarComponent, [{
|
|
149
|
+
type: Component,
|
|
150
|
+
args: [{ standalone: false, selector: 'mj-file-artifact-toolbar', template: `
|
|
151
|
+
<div class="file-toolbar">
|
|
152
|
+
<!-- Left: page navigation + zoom (only rendered when relevant) -->
|
|
153
|
+
<div class="file-toolbar__left">
|
|
154
|
+
@if (totalPages > 1) {
|
|
155
|
+
<div class="file-toolbar__nav">
|
|
156
|
+
<button class="file-toolbar__icon-btn" [disabled]="currentPage <= 1" (click)="prevPage.emit()" title="Previous page">
|
|
157
|
+
<i class="fas fa-chevron-left"></i>
|
|
158
|
+
</button>
|
|
159
|
+
<input
|
|
160
|
+
class="file-toolbar__page-input"
|
|
161
|
+
type="number"
|
|
162
|
+
[value]="currentPage"
|
|
163
|
+
[min]="1"
|
|
164
|
+
[max]="totalPages"
|
|
165
|
+
(change)="onPageInputChange($event)"
|
|
166
|
+
(keydown.enter)="onPageInputChange($event)"
|
|
167
|
+
title="Jump to page"
|
|
168
|
+
/>
|
|
169
|
+
<span class="file-toolbar__page-of">/ {{ totalPages }}</span>
|
|
170
|
+
<button class="file-toolbar__icon-btn" [disabled]="currentPage >= totalPages" (click)="nextPage.emit()" title="Next page">
|
|
171
|
+
<i class="fas fa-chevron-right"></i>
|
|
172
|
+
</button>
|
|
173
|
+
</div>
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
@if (showZoom) {
|
|
177
|
+
<div class="file-toolbar__zoom">
|
|
178
|
+
<button class="file-toolbar__icon-btn" [disabled]="!canZoomOut" (click)="zoomOut.emit()" title="Zoom out">
|
|
179
|
+
<i class="fas fa-minus"></i>
|
|
180
|
+
</button>
|
|
181
|
+
<button class="file-toolbar__zoom-level" (click)="zoomReset.emit()" title="Reset zoom to 100%">
|
|
182
|
+
{{ zoomPercent }}%
|
|
183
|
+
</button>
|
|
184
|
+
<button class="file-toolbar__icon-btn" [disabled]="!canZoomIn" (click)="zoomIn.emit()" title="Zoom in">
|
|
185
|
+
<i class="fas fa-plus"></i>
|
|
186
|
+
</button>
|
|
187
|
+
</div>
|
|
188
|
+
}
|
|
189
|
+
</div>
|
|
190
|
+
|
|
191
|
+
<!-- Center: filename -->
|
|
192
|
+
<div class="file-toolbar__title" [title]="fileName">{{ fileName }}</div>
|
|
193
|
+
|
|
194
|
+
<!-- Right: actions -->
|
|
195
|
+
<div class="file-toolbar__actions">
|
|
196
|
+
@if (showPrint) {
|
|
197
|
+
<button class="file-toolbar__btn" (click)="print.emit()" title="Print">
|
|
198
|
+
<i class="fas fa-print"></i>
|
|
199
|
+
<span class="file-toolbar__btn-label">Print</span>
|
|
200
|
+
</button>
|
|
201
|
+
}
|
|
202
|
+
<button class="file-toolbar__btn file-toolbar__btn--primary" (click)="download.emit()" [disabled]="isDownloading" title="Download file">
|
|
203
|
+
@if (isDownloading) {
|
|
204
|
+
<i class="fas fa-spinner fa-spin"></i>
|
|
205
|
+
} @else {
|
|
206
|
+
<i class="fas fa-download"></i>
|
|
207
|
+
}
|
|
208
|
+
<span class="file-toolbar__btn-label">Download</span>
|
|
209
|
+
</button>
|
|
210
|
+
</div>
|
|
211
|
+
</div>
|
|
212
|
+
`, changeDetection: ChangeDetectionStrategy.OnPush, styles: ["\n .file-toolbar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n padding: 6px 12px;\n background: var(--mj-bg-surface-card);\n border-bottom: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n }\n\n .file-toolbar__left {\n display: flex;\n align-items: center;\n gap: 8px;\n min-width: 0;\n }\n\n .file-toolbar__nav {\n display: flex;\n align-items: center;\n gap: 3px;\n }\n\n .file-toolbar__zoom {\n display: flex;\n align-items: center;\n gap: 2px;\n border-left: 1px solid var(--mj-border-default);\n padding-left: 8px;\n }\n\n .file-toolbar__title {\n flex: 1;\n text-align: center;\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n padding: 0 8px;\n }\n\n .file-toolbar__actions {\n display: flex;\n align-items: center;\n gap: 4px;\n min-width: 0;\n }\n\n /* Shared icon button (chevrons, +, -) */\n .file-toolbar__icon-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 26px;\n height: 26px;\n padding: 0;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 4px;\n color: var(--mj-text-secondary);\n font-size: 11px;\n cursor: pointer;\n transition: background 0.15s, border-color 0.15s, color 0.15s;\n }\n\n .file-toolbar__icon-btn:hover:not([disabled]) {\n background: var(--mj-bg-surface-hover);\n border-color: var(--mj-border-strong);\n color: var(--mj-text-primary);\n }\n\n .file-toolbar__icon-btn[disabled] {\n opacity: 0.4;\n cursor: not-allowed;\n }\n\n /* Page number input */\n .file-toolbar__page-input {\n width: 40px;\n height: 26px;\n padding: 0 4px;\n text-align: center;\n font-size: 12px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 4px;\n color: var(--mj-text-primary);\n /* hide spin buttons \u2014 we use the chevrons instead */\n -moz-appearance: textfield;\n }\n\n .file-toolbar__page-input::-webkit-inner-spin-button,\n .file-toolbar__page-input::-webkit-outer-spin-button {\n -webkit-appearance: none;\n margin: 0;\n }\n\n .file-toolbar__page-input:focus {\n outline: none;\n border-color: var(--mj-border-focus);\n box-shadow: 0 0 0 2px color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n }\n\n .file-toolbar__page-of {\n font-size: 12px;\n color: var(--mj-text-muted);\n white-space: nowrap;\n }\n\n /* Zoom level display \u2014 clickable to reset */\n .file-toolbar__zoom-level {\n min-width: 42px;\n height: 26px;\n padding: 0 4px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 4px;\n color: var(--mj-text-secondary);\n font-size: 12px;\n text-align: center;\n cursor: pointer;\n transition: background 0.15s, color 0.15s;\n }\n\n .file-toolbar__zoom-level:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n }\n\n /* Labelled action buttons (Print, Download) */\n .file-toolbar__btn {\n display: inline-flex;\n align-items: center;\n gap: 5px;\n padding: 5px 10px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 4px;\n color: var(--mj-text-secondary);\n font-size: 12px;\n cursor: pointer;\n transition: background 0.15s, border-color 0.15s, color 0.15s;\n }\n\n .file-toolbar__btn:hover:not([disabled]) {\n background: var(--mj-bg-surface-hover);\n border-color: var(--mj-border-strong);\n color: var(--mj-text-primary);\n }\n\n .file-toolbar__btn[disabled] {\n opacity: 0.4;\n cursor: not-allowed;\n }\n\n .file-toolbar__btn--primary {\n background: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n }\n\n .file-toolbar__btn--primary:hover:not([disabled]) {\n background: var(--mj-brand-primary-hover);\n border-color: var(--mj-brand-primary-hover);\n color: var(--mj-text-inverse);\n }\n\n @media (max-width: 480px) {\n .file-toolbar__btn-label {\n display: none;\n }\n }\n "] }]
|
|
213
|
+
}], null, { fileName: [{
|
|
214
|
+
type: Input
|
|
215
|
+
}], currentPage: [{
|
|
216
|
+
type: Input
|
|
217
|
+
}], totalPages: [{
|
|
218
|
+
type: Input
|
|
219
|
+
}], isDownloading: [{
|
|
220
|
+
type: Input
|
|
221
|
+
}], showPrint: [{
|
|
222
|
+
type: Input
|
|
223
|
+
}], showZoom: [{
|
|
224
|
+
type: Input
|
|
225
|
+
}], zoomPercent: [{
|
|
226
|
+
type: Input
|
|
227
|
+
}], canZoomIn: [{
|
|
228
|
+
type: Input
|
|
229
|
+
}], canZoomOut: [{
|
|
230
|
+
type: Input
|
|
231
|
+
}], download: [{
|
|
232
|
+
type: Output
|
|
233
|
+
}], print: [{
|
|
234
|
+
type: Output
|
|
235
|
+
}], prevPage: [{
|
|
236
|
+
type: Output
|
|
237
|
+
}], nextPage: [{
|
|
238
|
+
type: Output
|
|
239
|
+
}], pageChange: [{
|
|
240
|
+
type: Output
|
|
241
|
+
}], zoomIn: [{
|
|
242
|
+
type: Output
|
|
243
|
+
}], zoomOut: [{
|
|
244
|
+
type: Output
|
|
245
|
+
}], zoomReset: [{
|
|
246
|
+
type: Output
|
|
247
|
+
}] }); })();
|
|
248
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(FileArtifactToolbarComponent, { className: "FileArtifactToolbarComponent", filePath: "src/lib/components/file-artifact-toolbar.component.ts", lineNumber: 254 }); })();
|
|
249
|
+
//# sourceMappingURL=file-artifact-toolbar.component.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-artifact-toolbar.component.js","sourceRoot":"","sources":["../../../src/lib/components/file-artifact-toolbar.component.ts"],"names":[],"mappings":"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","sourcesContent":["import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy } from '@angular/core';\n\n/**\n * Shared toolbar rendered by file artifact viewer plugins (PDF, XLSX, DOCX).\n * Provides download, print, page-navigation, and optional zoom controls in a\n * consistent style. All colors use design tokens so the toolbar adapts to\n * light/dark themes.\n */\n@Component({\n standalone: false,\n selector: 'mj-file-artifact-toolbar',\n template: `\n <div class=\"file-toolbar\">\n <!-- Left: page navigation + zoom (only rendered when relevant) -->\n <div class=\"file-toolbar__left\">\n @if (totalPages > 1) {\n <div class=\"file-toolbar__nav\">\n <button class=\"file-toolbar__icon-btn\" [disabled]=\"currentPage <= 1\" (click)=\"prevPage.emit()\" title=\"Previous page\">\n <i class=\"fas fa-chevron-left\"></i>\n </button>\n <input\n class=\"file-toolbar__page-input\"\n type=\"number\"\n [value]=\"currentPage\"\n [min]=\"1\"\n [max]=\"totalPages\"\n (change)=\"onPageInputChange($event)\"\n (keydown.enter)=\"onPageInputChange($event)\"\n title=\"Jump to page\"\n />\n <span class=\"file-toolbar__page-of\">/ {{ totalPages }}</span>\n <button class=\"file-toolbar__icon-btn\" [disabled]=\"currentPage >= totalPages\" (click)=\"nextPage.emit()\" title=\"Next page\">\n <i class=\"fas fa-chevron-right\"></i>\n </button>\n </div>\n }\n\n @if (showZoom) {\n <div class=\"file-toolbar__zoom\">\n <button class=\"file-toolbar__icon-btn\" [disabled]=\"!canZoomOut\" (click)=\"zoomOut.emit()\" title=\"Zoom out\">\n <i class=\"fas fa-minus\"></i>\n </button>\n <button class=\"file-toolbar__zoom-level\" (click)=\"zoomReset.emit()\" title=\"Reset zoom to 100%\">\n {{ zoomPercent }}%\n </button>\n <button class=\"file-toolbar__icon-btn\" [disabled]=\"!canZoomIn\" (click)=\"zoomIn.emit()\" title=\"Zoom in\">\n <i class=\"fas fa-plus\"></i>\n </button>\n </div>\n }\n </div>\n\n <!-- Center: filename -->\n <div class=\"file-toolbar__title\" [title]=\"fileName\">{{ fileName }}</div>\n\n <!-- Right: actions -->\n <div class=\"file-toolbar__actions\">\n @if (showPrint) {\n <button class=\"file-toolbar__btn\" (click)=\"print.emit()\" title=\"Print\">\n <i class=\"fas fa-print\"></i>\n <span class=\"file-toolbar__btn-label\">Print</span>\n </button>\n }\n <button class=\"file-toolbar__btn file-toolbar__btn--primary\" (click)=\"download.emit()\" [disabled]=\"isDownloading\" title=\"Download file\">\n @if (isDownloading) {\n <i class=\"fas fa-spinner fa-spin\"></i>\n } @else {\n <i class=\"fas fa-download\"></i>\n }\n <span class=\"file-toolbar__btn-label\">Download</span>\n </button>\n </div>\n </div>\n `,\n styles: [`\n .file-toolbar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n padding: 6px 12px;\n background: var(--mj-bg-surface-card);\n border-bottom: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n }\n\n .file-toolbar__left {\n display: flex;\n align-items: center;\n gap: 8px;\n min-width: 0;\n }\n\n .file-toolbar__nav {\n display: flex;\n align-items: center;\n gap: 3px;\n }\n\n .file-toolbar__zoom {\n display: flex;\n align-items: center;\n gap: 2px;\n border-left: 1px solid var(--mj-border-default);\n padding-left: 8px;\n }\n\n .file-toolbar__title {\n flex: 1;\n text-align: center;\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n padding: 0 8px;\n }\n\n .file-toolbar__actions {\n display: flex;\n align-items: center;\n gap: 4px;\n min-width: 0;\n }\n\n /* Shared icon button (chevrons, +, -) */\n .file-toolbar__icon-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 26px;\n height: 26px;\n padding: 0;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 4px;\n color: var(--mj-text-secondary);\n font-size: 11px;\n cursor: pointer;\n transition: background 0.15s, border-color 0.15s, color 0.15s;\n }\n\n .file-toolbar__icon-btn:hover:not([disabled]) {\n background: var(--mj-bg-surface-hover);\n border-color: var(--mj-border-strong);\n color: var(--mj-text-primary);\n }\n\n .file-toolbar__icon-btn[disabled] {\n opacity: 0.4;\n cursor: not-allowed;\n }\n\n /* Page number input */\n .file-toolbar__page-input {\n width: 40px;\n height: 26px;\n padding: 0 4px;\n text-align: center;\n font-size: 12px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 4px;\n color: var(--mj-text-primary);\n /* hide spin buttons — we use the chevrons instead */\n -moz-appearance: textfield;\n }\n\n .file-toolbar__page-input::-webkit-inner-spin-button,\n .file-toolbar__page-input::-webkit-outer-spin-button {\n -webkit-appearance: none;\n margin: 0;\n }\n\n .file-toolbar__page-input:focus {\n outline: none;\n border-color: var(--mj-border-focus);\n box-shadow: 0 0 0 2px color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n }\n\n .file-toolbar__page-of {\n font-size: 12px;\n color: var(--mj-text-muted);\n white-space: nowrap;\n }\n\n /* Zoom level display — clickable to reset */\n .file-toolbar__zoom-level {\n min-width: 42px;\n height: 26px;\n padding: 0 4px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 4px;\n color: var(--mj-text-secondary);\n font-size: 12px;\n text-align: center;\n cursor: pointer;\n transition: background 0.15s, color 0.15s;\n }\n\n .file-toolbar__zoom-level:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n }\n\n /* Labelled action buttons (Print, Download) */\n .file-toolbar__btn {\n display: inline-flex;\n align-items: center;\n gap: 5px;\n padding: 5px 10px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 4px;\n color: var(--mj-text-secondary);\n font-size: 12px;\n cursor: pointer;\n transition: background 0.15s, border-color 0.15s, color 0.15s;\n }\n\n .file-toolbar__btn:hover:not([disabled]) {\n background: var(--mj-bg-surface-hover);\n border-color: var(--mj-border-strong);\n color: var(--mj-text-primary);\n }\n\n .file-toolbar__btn[disabled] {\n opacity: 0.4;\n cursor: not-allowed;\n }\n\n .file-toolbar__btn--primary {\n background: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n }\n\n .file-toolbar__btn--primary:hover:not([disabled]) {\n background: var(--mj-brand-primary-hover);\n border-color: var(--mj-brand-primary-hover);\n color: var(--mj-text-inverse);\n }\n\n @media (max-width: 480px) {\n .file-toolbar__btn-label {\n display: none;\n }\n }\n `],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class FileArtifactToolbarComponent {\n /** Display name shown in the center of the toolbar. */\n @Input() fileName = '';\n\n /** Current page number (1-based). Only shown when totalPages > 1. */\n @Input() currentPage = 1;\n\n /** Total page count. Pass 1 (default) to hide navigation arrows. */\n @Input() totalPages = 1;\n\n /** Whether the download button should show a spinner. */\n @Input() isDownloading = false;\n\n /** Whether to show the Print button (e.g. hide for binary spreadsheets). */\n @Input() showPrint = true;\n\n /** Whether to show zoom in/out/reset controls. Only PDF needs this. */\n @Input() showZoom = false;\n\n /** Current zoom level as a percentage integer (e.g. 100 = 100%). */\n @Input() zoomPercent = 100;\n\n /** Whether zooming in further is possible. */\n @Input() canZoomIn = true;\n\n /** Whether zooming out further is possible. */\n @Input() canZoomOut = true;\n\n @Output() download = new EventEmitter<void>();\n @Output() print = new EventEmitter<void>();\n @Output() prevPage = new EventEmitter<void>();\n @Output() nextPage = new EventEmitter<void>();\n /** Emits the validated target page number when the user edits the page input. */\n @Output() pageChange = new EventEmitter<number>();\n @Output() zoomIn = new EventEmitter<void>();\n @Output() zoomOut = new EventEmitter<void>();\n /** Emitted when the user clicks the zoom-level badge to reset to 100%. */\n @Output() zoomReset = new EventEmitter<void>();\n\n public onPageInputChange(event: Event): void {\n const input = event.target as HTMLInputElement;\n const value = parseInt(input.value, 10);\n if (!isNaN(value) && value >= 1 && value <= this.totalPages) {\n this.pageChange.emit(value);\n } else {\n // Snap back to current valid page\n input.value = String(this.currentPage);\n }\n }\n}\n"]}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { OnInit, ChangeDetectorRef } from '@angular/core';
|
|
2
|
+
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
|
|
3
|
+
import { BaseArtifactViewerPluginComponent } from '../base-artifact-viewer.component';
|
|
4
|
+
import { ArtifactFileService } from '../../services/artifact-file.service';
|
|
5
|
+
import * as i0 from "@angular/core";
|
|
6
|
+
/**
|
|
7
|
+
* Viewer plugin for Word (DOCX) artifact versions stored in MJStorage.
|
|
8
|
+
*
|
|
9
|
+
* Downloads the binary file, converts it to HTML using mammoth.js, then
|
|
10
|
+
* sanitizes the HTML before binding it with [innerHTML].
|
|
11
|
+
*
|
|
12
|
+
* Printing opens the sanitized content in a child window so the browser's
|
|
13
|
+
* native print dialog renders the Word document faithfully.
|
|
14
|
+
*/
|
|
15
|
+
export declare class DocxArtifactViewerComponent extends BaseArtifactViewerPluginComponent implements OnInit {
|
|
16
|
+
private fileService;
|
|
17
|
+
private sanitizer;
|
|
18
|
+
private cdr;
|
|
19
|
+
isLoading: boolean;
|
|
20
|
+
isDownloading: boolean;
|
|
21
|
+
errorMessage: string;
|
|
22
|
+
safeHtml: SafeHtml;
|
|
23
|
+
/** Raw HTML produced by mammoth — kept for print. */
|
|
24
|
+
private rawHtml;
|
|
25
|
+
private downloadUrl;
|
|
26
|
+
constructor(fileService: ArtifactFileService, sanitizer: DomSanitizer, cdr: ChangeDetectorRef);
|
|
27
|
+
get hasDisplayContent(): boolean;
|
|
28
|
+
ngOnInit(): Promise<void>;
|
|
29
|
+
onDownload(): Promise<void>;
|
|
30
|
+
onPrint(): void;
|
|
31
|
+
private loadDocument;
|
|
32
|
+
private convertDocxToHtml;
|
|
33
|
+
private fetchAsArrayBuffer;
|
|
34
|
+
/** Open a minimal print window containing only the document HTML. */
|
|
35
|
+
private openPrintWindow;
|
|
36
|
+
private showError;
|
|
37
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<DocxArtifactViewerComponent, never>;
|
|
38
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<DocxArtifactViewerComponent, "mj-docx-artifact-viewer", never, {}, {}, never, never, false, never>;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=docx-artifact-viewer.component.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docx-artifact-viewer.component.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/plugins/docx-artifact-viewer.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,MAAM,EAAE,iBAAiB,EAAmB,MAAM,eAAe,CAAC;AACtF,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAEnE,OAAO,EAAE,iCAAiC,EAAE,MAAM,mCAAmC,CAAC;AACtF,OAAO,EAAE,mBAAmB,EAAE,MAAM,sCAAsC,CAAC;;AAE3E;;;;;;;;GAQG;AACH,qBAsGa,2BAA4B,SAAQ,iCAAkC,YAAW,MAAM;IAYhG,OAAO,CAAC,WAAW;IACnB,OAAO,CAAC,SAAS;IACjB,OAAO,CAAC,GAAG;IAZN,SAAS,UAAQ;IACjB,aAAa,UAAS;IACtB,YAAY,SAAM;IAClB,QAAQ,EAAE,QAAQ,CAAM;IAE/B,qDAAqD;IACrD,OAAO,CAAC,OAAO,CAAM;IACrB,OAAO,CAAC,WAAW,CAAM;gBAGf,WAAW,EAAE,mBAAmB,EAChC,SAAS,EAAE,YAAY,EACvB,GAAG,EAAE,iBAAiB;IAKhC,IAAoB,iBAAiB,IAAI,OAAO,CAAiB;IAE3D,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAIlB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAiBjC,OAAO,IAAI,IAAI;YASR,YAAY;YAuCZ,iBAAiB;YAUjB,kBAAkB;IAQhC,qEAAqE;IACrE,OAAO,CAAC,eAAe;IA6BvB,OAAO,CAAC,SAAS;yCA1IN,2BAA2B;2CAA3B,2BAA2B;CAgJvC"}
|