@ruc-lib/widget 2.0.1 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +171 -62
- package/fesm2022/ruc-lib-widget.mjs +243 -0
- package/fesm2022/ruc-lib-widget.mjs.map +1 -0
- package/index.d.ts +167 -3
- package/package.json +7 -18
- package/esm2020/index.mjs +0 -4
- package/esm2020/interface/widget.mjs +0 -2
- package/esm2020/lib/ruclib-widget/ruclib-widget.component.mjs +0 -108
- package/esm2020/lib/ruclib-widget-item/ruclib-widget-item.component.mjs +0 -103
- package/esm2020/lib/ruclib-widget.module.mjs +0 -34
- package/esm2020/model/default-values.mjs +0 -32
- package/esm2020/pipes/safe-html.pipe.mjs +0 -36
- package/esm2020/ruc-lib-widget.mjs +0 -5
- package/fesm2015/ruc-lib-widget.mjs +0 -307
- package/fesm2015/ruc-lib-widget.mjs.map +0 -1
- package/fesm2020/ruc-lib-widget.mjs +0 -305
- package/fesm2020/ruc-lib-widget.mjs.map +0 -1
- package/interface/widget.d.ts +0 -92
- package/lib/ruclib-widget/ruclib-widget.component.d.ts +0 -77
- package/lib/ruclib-widget-item/ruclib-widget-item.component.d.ts +0 -60
- package/lib/ruclib-widget.module.d.ts +0 -13
- package/model/default-values.d.ts +0 -2
- package/pipes/safe-html.pipe.d.ts +0 -24
package/README.md
CHANGED
|
@@ -1,88 +1,153 @@
|
|
|
1
1
|
# ruclib-widget
|
|
2
2
|
|
|
3
|
-
This library provides a
|
|
4
|
-
|
|
5
|
-
##
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
3
|
+
This library provides a highly configurable widget component that allows for the creation of a dynamic, dashboard-like interface. Users can create, customize, and arrange multiple widgets within a container, with support for dragging, resizing, and dynamic content.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Dynamic Creation**: Create any number of widgets, each with its own configuration and content.
|
|
8
|
+
- **Drag & Drop**: Easily rearrange widgets within the host container. Layout changes are emitted to be saved.
|
|
9
|
+
- **Resizing**: Set responsive width and height for each widget. Widgets can also be made resizable by the user.
|
|
10
|
+
- **Custom Content**:
|
|
11
|
+
- Set a title and an optional header icon for each widget.
|
|
12
|
+
- Embed custom components (like charts, graphs, forms, etc.) as widget content.
|
|
13
|
+
- **Interactivity**:
|
|
14
|
+
- Optionally display a close icon to remove a widget from the layout.
|
|
15
|
+
- Disable individual widgets to make them non-interactive.
|
|
16
|
+
- **Styling & Theming**:
|
|
17
|
+
- Set a custom background color for each widget.
|
|
18
|
+
- Apply custom theme classes for a consistent look and feel.
|
|
19
|
+
- Visual feedback is provided when a widget is being dragged.
|
|
20
|
+
- **Layout Persistence**: The component emits an event with the updated widget configuration whenever a widget is moved or resized, allowing the application to save and restore the layout.
|
|
21
|
+
|
|
22
|
+
## Installation Guide
|
|
23
|
+
|
|
24
|
+
### Install the Entire Library
|
|
25
|
+
```bash
|
|
26
|
+
npm install @uxpractice/ruc-lib
|
|
27
|
+
```
|
|
12
28
|
|
|
13
|
-
|
|
29
|
+
### Install Individual Component
|
|
30
|
+
If you only need the Widget component:
|
|
31
|
+
```bash
|
|
32
|
+
npm install @ruc-lib/widget
|
|
33
|
+
```
|
|
14
34
|
|
|
15
|
-
|
|
35
|
+
## Version Compatibility
|
|
36
|
+
|
|
37
|
+
Please ensure you install the correct version of `@ruc-lib/widget` based on your Angular version.
|
|
38
|
+
|
|
39
|
+
| Angular Version | Compatible `@ruc-lib/widget` Version |
|
|
40
|
+
|--------------------|---------------------------------------------|
|
|
41
|
+
| 15.x.x | `npm install @ruc-lib/widget@^3.0.0` |
|
|
42
|
+
| 16.x.x | `npm install @ruc-lib/widget@^3.0.0` |
|
|
16
43
|
|
|
17
|
-
`npm install @ruc-lib/widget`
|
|
18
44
|
|
|
19
45
|
## Usage
|
|
20
46
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
47
|
+
### 1. Import the Component
|
|
48
|
+
In your Angular component file (e.g., `app.component.ts`), import the `RuclibWidgetComponent`:
|
|
49
|
+
|
|
50
|
+
```typescript
|
|
51
|
+
import { Component } from '@angular/core';
|
|
52
|
+
|
|
53
|
+
// For Complete Library
|
|
54
|
+
import { RuclibWidgetComponent } from '@uxpractice/ruc-lib/widget';
|
|
55
|
+
|
|
56
|
+
// For Individual Package
|
|
57
|
+
import { RuclibWidgetComponent } from '@ruc-lib/widget';
|
|
58
|
+
|
|
59
|
+
@Component({
|
|
60
|
+
selector: 'app-root',
|
|
61
|
+
imports: [RuclibWidgetComponent],
|
|
62
|
+
templateUrl: './app.component.html',
|
|
63
|
+
})
|
|
64
|
+
export class AppComponent {
|
|
65
|
+
// Component code here
|
|
66
|
+
}
|
|
67
|
+
```
|
|
24
68
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
2. Use the widget selector in your HTML file.
|
|
28
|
-
3. Handle input and output bindings accordingly.
|
|
69
|
+
### 2. Use the Component
|
|
70
|
+
In your component's template, use the `<uxp-ruclib-widget>` selector:
|
|
29
71
|
|
|
30
|
-
```
|
|
72
|
+
```html
|
|
31
73
|
<uxp-ruclib-widget
|
|
32
74
|
[rucInputData]="widgetInput"
|
|
33
75
|
[customTheme]="customTheme"
|
|
34
|
-
(widgetClose)="
|
|
35
|
-
(layoutChanged)="
|
|
76
|
+
(widgetClose)="handleWidgetClose($event)"
|
|
77
|
+
(layoutChanged)="handleLayoutChange($event)">
|
|
36
78
|
></uxp-ruclib-widget>
|
|
37
79
|
```
|
|
38
80
|
|
|
39
|
-
|
|
81
|
+
## API Reference
|
|
40
82
|
|
|
41
|
-
|
|
83
|
+
### Component Inputs
|
|
42
84
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
85
|
+
| Input | Type | Description |
|
|
86
|
+
|----------------|-----------------------|--------------------------------------------------|
|
|
87
|
+
| `rucInputData` | `WidgetConfig` | The main configuration object for the widgets. |
|
|
88
|
+
| `customTheme` | `string` | An optional CSS class for custom theming. |
|
|
46
89
|
|
|
47
|
-
Outputs
|
|
48
|
-
• widgetClose: Event emitted from the widget on clicking close button of a widget.
|
|
49
|
-
• layoutChanged: Event emitted from the widget on resizing/dragging a widget.
|
|
90
|
+
### Component Outputs
|
|
50
91
|
|
|
92
|
+
| Output | Type | Description |
|
|
93
|
+
|-----------------|--------------------|--------------------------------------------------------------------------|
|
|
94
|
+
| `widgetClose` | `string` | Emitted when a widget's close icon is clicked. Returns the widget's `id`. |
|
|
95
|
+
| `layoutChanged` | `WidgetConfigData[]` | Emitted when a widget is moved or resized. Returns the full widget data array. |
|
|
51
96
|
|
|
52
|
-
|
|
97
|
+
### `rucInputData` (`WidgetConfig`)
|
|
98
|
+
This object defines the global configuration for the widget container.
|
|
53
99
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
{
|
|
59
|
-
id: string = 'default-widget-1',
|
|
60
|
-
title: string = 'Default Widget 1',
|
|
61
|
-
description: string = 'Widget Description'
|
|
62
|
-
contentType: string = 'text',
|
|
63
|
-
contentData: any = 'This is the default content for the first widget. It is draggable and has a close icon.',
|
|
64
|
-
top: string = '20px',
|
|
65
|
-
left: string = '20px',
|
|
66
|
-
width: string = '300px',
|
|
67
|
-
height: string = '200px',
|
|
68
|
-
draggable: boolean = true,
|
|
69
|
-
showCloseIcon: boolean = true,
|
|
70
|
-
disabled: boolean = false,
|
|
71
|
-
headerIcon: string = 'widgets'
|
|
72
|
-
}
|
|
73
|
-
]
|
|
74
|
-
};
|
|
75
|
-
```
|
|
100
|
+
| Property | Type | Description |
|
|
101
|
+
|--------------|----------------------|--------------------------------------------------|
|
|
102
|
+
| `color` | `string` | A theme color to apply to the widgets. |
|
|
103
|
+
| `widgetData` | `WidgetConfigData[]` | An array of individual widget configuration objects. |
|
|
76
104
|
|
|
77
|
-
|
|
105
|
+
### `widgetData` Item (`WidgetConfigData`)
|
|
106
|
+
This object defines the configuration for each individual widget.
|
|
78
107
|
|
|
79
|
-
|
|
80
|
-
|
|
108
|
+
| Property | Type | Description |
|
|
109
|
+
|-----------------|-----------|-------------------------------------------------------------------------------|
|
|
110
|
+
| `id` | `string` | A unique identifier for the widget. |
|
|
111
|
+
| `title` | `string` | The title displayed in the widget's header. |
|
|
112
|
+
| `description` | `string` | A description for the widget, often used for tooltips. |
|
|
113
|
+
| `contentType` | `string` | The type of content to render. Can be `'text'`, `'html'`, or `'component'`. |
|
|
114
|
+
| `contentData` | `any` | The data for the content. Can be a string, HTML, or a component reference. |
|
|
115
|
+
| `top` | `string` | The initial top position of the widget (e.g., `'20px'`). |
|
|
116
|
+
| `left` | `string` | The initial left position of the widget (e.g., `'20px'`). |
|
|
117
|
+
| `width` | `string` | The initial width of the widget (e.g., `'300px'`). |
|
|
118
|
+
| `height` | `string` | The initial height of the widget (e.g., `'200px'`). |
|
|
119
|
+
| `draggable` | `boolean` | If `true`, the widget can be dragged. Default is `true`. |
|
|
120
|
+
| `showCloseIcon` | `boolean` | If `true`, a close icon is displayed in the header. Default is `true`. |
|
|
121
|
+
| `disabled` | `boolean` | If `true`, the widget is non-interactive. Default is `false`. |
|
|
122
|
+
| `headerIcon` | `string` | The name of the Material Icon to display in the header. |
|
|
123
|
+
| `backgroundColor`| `string` | An optional background color for the widget. |
|
|
124
|
+
|
|
125
|
+
## Example Configuration
|
|
126
|
+
|
|
127
|
+
Here's an example of how to configure the Widget component in your component's TypeScript file.
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
import { Component } from '@angular/core';
|
|
131
|
+
|
|
132
|
+
// For Complete Library
|
|
133
|
+
import { RuclibWidgetModule, WidgetConfig, WidgetConfigData } from '@uxpractice/ruc-lib/widget';
|
|
134
|
+
|
|
135
|
+
// For Individual package
|
|
136
|
+
import { RuclibWidgetModule, WidgetConfig, WidgetConfigData } from '@ruc-lib/widget';
|
|
137
|
+
|
|
138
|
+
@Component({
|
|
139
|
+
selector: 'app-root',
|
|
140
|
+
templateUrl: './app.component.html',
|
|
141
|
+
})
|
|
142
|
+
export class AppComponent {
|
|
143
|
+
customTheme = 'ruc-custom-theme';
|
|
144
|
+
|
|
145
|
+
widgetInput: WidgetConfig = {
|
|
81
146
|
color: 'primary',
|
|
82
147
|
widgetData: [
|
|
83
148
|
{
|
|
84
|
-
id: '
|
|
85
|
-
title: '
|
|
149
|
+
id: 'widget-1',
|
|
150
|
+
title: 'Text Widget',
|
|
86
151
|
contentType: 'text',
|
|
87
152
|
contentData: 'This is the default content for the first widget. It is draggable and has a close icon.',
|
|
88
153
|
top: '20px',
|
|
@@ -94,8 +159,8 @@ export const mockDrawerInput = {
|
|
|
94
159
|
headerIcon: 'widgets'
|
|
95
160
|
},
|
|
96
161
|
{
|
|
97
|
-
id: '
|
|
98
|
-
title: '
|
|
162
|
+
id: 'widget-2',
|
|
163
|
+
title: 'HTML Widget',
|
|
99
164
|
contentType: 'html',
|
|
100
165
|
contentData: '<h3>HTML Content</h3><p>This widget contains <strong>HTML</strong> and has a different background color.</p>',
|
|
101
166
|
top: '240px',
|
|
@@ -104,10 +169,54 @@ export const mockDrawerInput = {
|
|
|
104
169
|
height: '200px',
|
|
105
170
|
draggable: true,
|
|
106
171
|
showCloseIcon: true,
|
|
107
|
-
backgroundColor: '#f0f0f0'
|
|
172
|
+
backgroundColor: '#f0f0f0',
|
|
173
|
+
headerIcon: 'code'
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
id: 'widget-3',
|
|
177
|
+
title: 'Disabled Widget',
|
|
178
|
+
contentType: 'text',
|
|
179
|
+
contentData: 'This widget cannot be moved or closed.',
|
|
180
|
+
top: '20px',
|
|
181
|
+
left: '340px',
|
|
182
|
+
width: '300px',
|
|
183
|
+
height: '150px',
|
|
184
|
+
draggable: false,
|
|
185
|
+
showCloseIcon: false,
|
|
186
|
+
disabled: true,
|
|
187
|
+
headerIcon: 'block'
|
|
108
188
|
}
|
|
109
189
|
]
|
|
110
|
-
};
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
handleWidgetClose(widgetId: string) {
|
|
193
|
+
console.log('Widget closed:', widgetId);
|
|
194
|
+
// Logic to remove the widget from the widgetInput.widgetData array
|
|
195
|
+
this.widgetInput.widgetData = this.widgetInput.widgetData.filter(w => w.id !== widgetId);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
handleLayoutChange(widgets: WidgetConfigData[]) {
|
|
199
|
+
console.log('Layout changed:', widgets);
|
|
200
|
+
// Logic to save the new layout configuration
|
|
201
|
+
this.widgetInput.widgetData = widgets;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
> ⚠️ **IMPORTANT: Custom Theme Usage in Angular Material**
|
|
207
|
+
|
|
208
|
+
When implementing **custom themes**, such as:
|
|
209
|
+
|
|
210
|
+
```scss
|
|
211
|
+
.custom-theme-1 {
|
|
212
|
+
@include angular-material-theme($custom-theme);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// You must also include the typography mixin to ensure text styles are applied correctly as shown below:
|
|
216
|
+
.custom-theme-1 {
|
|
217
|
+
@include angular-material-theme($custom-theme);
|
|
218
|
+
@include mat.typography-level($theme-custom-typography-name, body-1);
|
|
219
|
+
}
|
|
111
220
|
```
|
|
112
221
|
|
|
113
222
|
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import * as i3 from '@angular/material/button';
|
|
2
|
+
import { MatButtonModule } from '@angular/material/button';
|
|
3
|
+
import * as i2 from '@angular/material/icon';
|
|
4
|
+
import { MatIconModule } from '@angular/material/icon';
|
|
5
|
+
import * as i0 from '@angular/core';
|
|
6
|
+
import { Pipe, ViewContainerRef, ViewChild, Input, Component, EventEmitter, Output } from '@angular/core';
|
|
7
|
+
import * as i4 from '@angular/cdk/drag-drop';
|
|
8
|
+
import { DragDropModule } from '@angular/cdk/drag-drop';
|
|
9
|
+
import * as i1$1 from '@angular/common';
|
|
10
|
+
import { CommonModule } from '@angular/common';
|
|
11
|
+
import * as i1 from '@angular/platform-browser';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @Pipe SafeHtmlPipe
|
|
15
|
+
* @name safeHtml
|
|
16
|
+
* @description A pipe that bypasses Angular's built-in security and sanitizes HTML content,
|
|
17
|
+
* allowing it to be safely rendered in the DOM. Use with caution and only with trusted HTML sources.
|
|
18
|
+
*/
|
|
19
|
+
class SafeHtmlPipe {
|
|
20
|
+
/**
|
|
21
|
+
* @param sanitizer - An instance of DomSanitizer used to bypass security.
|
|
22
|
+
*/
|
|
23
|
+
constructor(sanitizer) {
|
|
24
|
+
this.sanitizer = sanitizer;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Transforms a string containing HTML into a SafeHtml object that can be bound to [innerHTML].
|
|
28
|
+
* @param value - The HTML string to sanitize.
|
|
29
|
+
* @returns A SafeHtml object, which Angular trusts as safe HTML.
|
|
30
|
+
*/
|
|
31
|
+
transform(value) {
|
|
32
|
+
if (value === null || value === undefined) {
|
|
33
|
+
return value;
|
|
34
|
+
}
|
|
35
|
+
return this.sanitizer.bypassSecurityTrustHtml(value);
|
|
36
|
+
}
|
|
37
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: SafeHtmlPipe, deps: [{ token: i1.DomSanitizer }], target: i0.ɵɵFactoryTarget.Pipe }); }
|
|
38
|
+
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.3.9", ngImport: i0, type: SafeHtmlPipe, isStandalone: true, name: "safeHtml" }); }
|
|
39
|
+
}
|
|
40
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: SafeHtmlPipe, decorators: [{
|
|
41
|
+
type: Pipe,
|
|
42
|
+
args: [{ name: 'safeHtml', standalone: true }]
|
|
43
|
+
}], ctorParameters: () => [{ type: i1.DomSanitizer }] });
|
|
44
|
+
|
|
45
|
+
class RuclibWidgetItemComponent {
|
|
46
|
+
/**
|
|
47
|
+
* @param cdr The ChangeDetectorRef to manually trigger change detection.
|
|
48
|
+
* @param sanitizer The DomSanitizer service to prevent XSS attacks by sanitizing HTML.
|
|
49
|
+
*/
|
|
50
|
+
constructor(cdr, sanitizer) {
|
|
51
|
+
this.cdr = cdr;
|
|
52
|
+
this.sanitizer = sanitizer;
|
|
53
|
+
/**
|
|
54
|
+
* A reference to the dynamically created component instance.
|
|
55
|
+
* This is used to manage the lifecycle of the injected component.
|
|
56
|
+
*/
|
|
57
|
+
this.dynamicComponentRef = null;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* A lifecycle hook that responds when Angular sets or resets data-bound input properties.
|
|
61
|
+
* It checks for changes in `widgetConfig` and reloads the content accordingly.
|
|
62
|
+
* @param changes An object of the changed properties.
|
|
63
|
+
*/
|
|
64
|
+
ngOnChanges(changes) {
|
|
65
|
+
if (changes['widgetConfig']) {
|
|
66
|
+
if (this.widgetConfig.contentType === 'component' && this.widgetComponentHost) {
|
|
67
|
+
this.loadDynamicContent();
|
|
68
|
+
}
|
|
69
|
+
else if (this.widgetConfig.contentType !== 'component') {
|
|
70
|
+
this.clearDynamicContent();
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* A lifecycle hook that is called after Angular has fully initialized a component's view.
|
|
76
|
+
* It ensures that if the initial content type is 'component', it gets loaded.
|
|
77
|
+
*/
|
|
78
|
+
ngAfterViewInit() {
|
|
79
|
+
if (this.widgetConfig) {
|
|
80
|
+
if (this.widgetConfig.contentType === 'component' && this.widgetComponentHost) {
|
|
81
|
+
this.loadDynamicContent();
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Loads a dynamic component into the `widgetComponentHost` view container.
|
|
87
|
+
* It reads the component type and any input data from the `widgetConfig`.
|
|
88
|
+
*/
|
|
89
|
+
loadDynamicContent() {
|
|
90
|
+
this.clearDynamicContent();
|
|
91
|
+
if (this.widgetConfig.contentType === 'component' && this.widgetConfig.contentData && this.widgetConfig.contentData.component) {
|
|
92
|
+
if (this.widgetComponentHost) {
|
|
93
|
+
const componentType = this.widgetConfig.contentData.component;
|
|
94
|
+
this.dynamicComponentRef = this.widgetComponentHost.createComponent(componentType);
|
|
95
|
+
if (this.widgetConfig.contentData.inputs && this.dynamicComponentRef?.instance) {
|
|
96
|
+
Object.keys(this.widgetConfig.contentData.inputs).forEach(key => {
|
|
97
|
+
if (this.dynamicComponentRef && key in this.dynamicComponentRef.instance) {
|
|
98
|
+
this.dynamicComponentRef.instance[key] = this.widgetConfig.contentData.inputs[key];
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
this.dynamicComponentRef.changeDetectorRef.detectChanges();
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
this.cdr.detectChanges();
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Clears any dynamically loaded component from the view container and destroys its instance.
|
|
109
|
+
*/
|
|
110
|
+
clearDynamicContent() {
|
|
111
|
+
if (this.dynamicComponentRef) {
|
|
112
|
+
this.dynamicComponentRef.destroy();
|
|
113
|
+
this.dynamicComponentRef = null;
|
|
114
|
+
}
|
|
115
|
+
if (this.widgetComponentHost) {
|
|
116
|
+
this.widgetComponentHost.clear();
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* A lifecycle hook that cleans up the component before it's destroyed.
|
|
121
|
+
* Ensures that any dynamically created components are properly destroyed to avoid memory leaks.
|
|
122
|
+
*/
|
|
123
|
+
ngOnDestroy() {
|
|
124
|
+
this.clearDynamicContent();
|
|
125
|
+
}
|
|
126
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: RuclibWidgetItemComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: i1.DomSanitizer }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
127
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.9", type: RuclibWidgetItemComponent, isStandalone: true, selector: "uxp-ruclib-widget-item", inputs: { widgetConfig: "widgetConfig" }, viewQueries: [{ propertyName: "widgetComponentHost", first: true, predicate: ["widgetComponentHost"], descendants: true, read: ViewContainerRef }], usesOnChanges: true, ngImport: i0, template: "<!--\r\nThis container uses a switch to determine how to render the widget's content\r\nbased on the `contentType` property in the widget's configuration.\r\n-->\r\n@if (widgetConfig) {\r\n @switch (widgetConfig.contentType) {\r\n @case ('text') {\r\n <p>{{ widgetConfig.contentData }}</p>\r\n }\r\n @case ('html') {\r\n <div [innerHTML]=\"widgetConfig.contentData | safeHtml\"></div>\r\n }\r\n @case ('video') {\r\n <video\r\n [src]=\"widgetConfig.contentData.src\"\r\n [attr.type]=\"widgetConfig.contentData.type\"\r\n [controls]=\"widgetConfig.contentData.controls\"\r\n [autoplay]=\"widgetConfig.contentData.autoplay\"\r\n [loop]=\"widgetConfig.contentData.loop\"\r\n style=\"width: 100%; height: 100%; object-fit: contain\"\r\n ></video>\r\n }\r\n @case ('component') {\r\n <ng-template #widgetComponentHost></ng-template>\r\n }\r\n @default {\r\n <p>No content provided or content type not supported.</p>\r\n }\r\n }\r\n}\r\n", styles: [":host{display:block;height:100%;width:100%}video{max-width:100%;max-height:100%}p{margin:0}\n"], dependencies: [{ kind: "pipe", type: SafeHtmlPipe, name: "safeHtml" }] }); }
|
|
128
|
+
}
|
|
129
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: RuclibWidgetItemComponent, decorators: [{
|
|
130
|
+
type: Component,
|
|
131
|
+
args: [{ selector: 'uxp-ruclib-widget-item', imports: [SafeHtmlPipe], template: "<!--\r\nThis container uses a switch to determine how to render the widget's content\r\nbased on the `contentType` property in the widget's configuration.\r\n-->\r\n@if (widgetConfig) {\r\n @switch (widgetConfig.contentType) {\r\n @case ('text') {\r\n <p>{{ widgetConfig.contentData }}</p>\r\n }\r\n @case ('html') {\r\n <div [innerHTML]=\"widgetConfig.contentData | safeHtml\"></div>\r\n }\r\n @case ('video') {\r\n <video\r\n [src]=\"widgetConfig.contentData.src\"\r\n [attr.type]=\"widgetConfig.contentData.type\"\r\n [controls]=\"widgetConfig.contentData.controls\"\r\n [autoplay]=\"widgetConfig.contentData.autoplay\"\r\n [loop]=\"widgetConfig.contentData.loop\"\r\n style=\"width: 100%; height: 100%; object-fit: contain\"\r\n ></video>\r\n }\r\n @case ('component') {\r\n <ng-template #widgetComponentHost></ng-template>\r\n }\r\n @default {\r\n <p>No content provided or content type not supported.</p>\r\n }\r\n }\r\n}\r\n", styles: [":host{display:block;height:100%;width:100%}video{max-width:100%;max-height:100%}p{margin:0}\n"] }]
|
|
132
|
+
}], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: i1.DomSanitizer }], propDecorators: { widgetConfig: [{
|
|
133
|
+
type: Input
|
|
134
|
+
}], widgetComponentHost: [{
|
|
135
|
+
type: ViewChild,
|
|
136
|
+
args: ['widgetComponentHost', { read: ViewContainerRef }]
|
|
137
|
+
}] } });
|
|
138
|
+
|
|
139
|
+
class RuclibWidgetComponent {
|
|
140
|
+
/**
|
|
141
|
+
* @param cdr The ChangeDetectorRef to manually trigger change detection when needed.
|
|
142
|
+
*/
|
|
143
|
+
constructor(cdr) {
|
|
144
|
+
this.cdr = cdr;
|
|
145
|
+
/**
|
|
146
|
+
* An event emitter that fires when a widget's close icon is clicked.
|
|
147
|
+
* It emits the unique ID of the widget to be closed.
|
|
148
|
+
*/
|
|
149
|
+
this.widgetClose = new EventEmitter();
|
|
150
|
+
/**
|
|
151
|
+
* An event emitter that fires when a widget is dragged and dropped.
|
|
152
|
+
* It emits the entire updated array of widget configurations, allowing the parent to save the new layout.
|
|
153
|
+
*/
|
|
154
|
+
this.layoutChanged = new EventEmitter();
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* After the view initializes, this hook captures the host container's native element
|
|
158
|
+
* and sets it as the boundary for dragging widgets.
|
|
159
|
+
*/
|
|
160
|
+
ngAfterViewInit() {
|
|
161
|
+
if (this.widgetsHostContainerRef) {
|
|
162
|
+
this.dragBoundaryElement = this.widgetsHostContainerRef.nativeElement;
|
|
163
|
+
this.cdr.detectChanges();
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Handles the click event on a widget's close button.
|
|
168
|
+
* @param widgetId The ID of the widget to be closed.
|
|
169
|
+
*/
|
|
170
|
+
onCloseClick(widgetId) {
|
|
171
|
+
this.widgetClose.emit(widgetId);
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Handles the start of a drag operation for a widget.
|
|
175
|
+
* Sets the widget's isActive state to true for visual feedback.
|
|
176
|
+
* @param widget The WidgetConfigData object being dragged.
|
|
177
|
+
* @param event The CdkDragStart event.
|
|
178
|
+
*/
|
|
179
|
+
onDragStarted(widget, event) {
|
|
180
|
+
widget.isActive = true;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Handles the end of a drag operation.
|
|
184
|
+
* It calculates the new top/left position, updates the widget's configuration,
|
|
185
|
+
* and emits the `layoutChanged` event with the new layout data.
|
|
186
|
+
* @param widget The WidgetConfigData object that was dragged.
|
|
187
|
+
* @param event The CdkDragEnd event.
|
|
188
|
+
*/
|
|
189
|
+
onDragEnded(widget, event) {
|
|
190
|
+
widget.isActive = false;
|
|
191
|
+
const newPosition = event.source.getFreeDragPosition();
|
|
192
|
+
const initialTopPx = parseFloat(widget.top || '0');
|
|
193
|
+
const initialLeftPx = parseFloat(widget.left || '0');
|
|
194
|
+
const newTopPx = initialTopPx + newPosition.y;
|
|
195
|
+
const newLeftPx = initialLeftPx + newPosition.x;
|
|
196
|
+
widget.top = `${newTopPx}px`;
|
|
197
|
+
widget.left = `${newLeftPx}px`;
|
|
198
|
+
event.source.reset();
|
|
199
|
+
if (this.rucInputData.widgetData) {
|
|
200
|
+
this.layoutChanged.emit(this.rucInputData.widgetData);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Retrieves the color for UI elements like icons from the input data.
|
|
205
|
+
* @returns The color string (e.g., 'primary', 'accent') or 'primary' as a default.
|
|
206
|
+
*/
|
|
207
|
+
getColor() {
|
|
208
|
+
return this.rucInputData?.color || 'primary';
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* A safe getter for the widget data array from the main input configuration.
|
|
212
|
+
* @returns The array of widget configurations, or an empty array if not defined.
|
|
213
|
+
*/
|
|
214
|
+
getWidgetData() {
|
|
215
|
+
return typeof this.rucInputData.widgetData !== 'undefined' ? this.rucInputData.widgetData : [];
|
|
216
|
+
}
|
|
217
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: RuclibWidgetComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
218
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.9", type: RuclibWidgetComponent, isStandalone: true, selector: "uxp-ruclib-widget", inputs: { rucInputData: "rucInputData", customTheme: "customTheme" }, outputs: { widgetClose: "widgetClose", layoutChanged: "layoutChanged" }, viewQueries: [{ propertyName: "widgetsHostContainerRef", first: true, predicate: ["widgetsHostContainer"], descendants: true }], ngImport: i0, template: "<!--\r\nThe main container for all widgets.\r\n- Applies a custom theme class if provided.\r\n- Acts as the boundary for dragging operations via the #widgetsHostContainer template reference.\r\n-->\r\n<div\r\n class=\"widgets-host-container {{ customTheme || '' }}\"\r\n #widgetsHostContainer\r\n >\r\n <!--\r\n Iterates through the widget data to render each individual widget.\r\n Each widget is a draggable container powered by Angular CDK.\r\n -->\r\n @for (widgetConfig of getWidgetData(); track widgetConfig) {\r\n <div\r\n [style.width]=\"widgetConfig.width ? widgetConfig.width : '200px'\"\r\n [style.height]=\"widgetConfig.height ? widgetConfig.height : '150px'\"\r\n [style.minWidth]=\"widgetConfig.width ? widgetConfig.width : '200px'\"\r\n [style.minHeight]=\"widgetConfig.height ? widgetConfig.height : '150px'\"\r\n [style.top]=\"widgetConfig.top\"\r\n [style.left]=\"widgetConfig.left\"\r\n [style.background-color]=\"widgetConfig.backgroundColor\"\r\n [class.disabled]=\"widgetConfig.disabled\"\r\n [class.draggable]=\"widgetConfig.draggable && !widgetConfig.disabled\"\r\n [class.resizable]=\"widgetConfig.resizable && !widgetConfig.disabled\"\r\n class=\"widget-container\"\r\n cdkDrag\r\n [cdkDragDisabled]=\"!widgetConfig.draggable || widgetConfig.disabled\"\r\n [cdkDragBoundary]=\"dragBoundaryElement\"\r\n (cdkDragStarted)=\"onDragStarted(widgetConfig, $event)\"\r\n (cdkDragEnded)=\"onDragEnded(widgetConfig, $event)\"\r\n [ngClass]=\"{ 'is-active': widgetConfig.isActive }\"\r\n >\r\n <!--\r\n Widget Header: Contains title and controls. Acts as the handle for dragging.\r\n -->\r\n <div class=\"widget-header\" cdkDragHandle>\r\n <div class=\"widget-header-left\">\r\n <!-- Draggable indicator icon, shown only if the widget is draggable and not disabled -->\r\n @if (widgetConfig.draggable && !widgetConfig.disabled) {\r\n <mat-icon\r\n class=\"widget-drag-handle\"\r\n aria-hidden=\"true\"\r\n >drag_indicator</mat-icon\r\n >\r\n }\r\n <!-- Optional header icon -->\r\n @if (widgetConfig.headerIcon) {\r\n <mat-icon class=\"widget-header-icon\">{{\r\n widgetConfig.headerIcon\r\n }}</mat-icon>\r\n }\r\n <!-- Widget Title -->\r\n @if (widgetConfig?.title) {\r\n <h3>{{ widgetConfig?.title }}</h3>\r\n }\r\n </div>\r\n <!-- Optional close button with accessibility label -->\r\n @if (widgetConfig?.showCloseIcon) {\r\n <button\r\n color=\"{{ getColor() }}\"\r\n mat-icon-button\r\n class=\"widget-close-button\"\r\n [disabled]=\"widgetConfig.disabled\"\r\n (click)=\"onCloseClick(widgetConfig.id)\"\r\n [attr.aria-label]=\"'Close widget ' + widgetConfig?.title\"\r\n >\r\n <mat-icon color=\"{{ getColor() }}\">close</mat-icon>\r\n </button>\r\n }\r\n </div>\r\n <!--\r\n Widget Content: Renders the main content of the widget using the child uxp-ruclib-widget-item component.\r\n -->\r\n <div class=\"widget-content\">\r\n <uxp-ruclib-widget-item\r\n [widgetConfig]=\"widgetConfig\"\r\n ></uxp-ruclib-widget-item>\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n", styles: [":host{display:block}.widget-container{border:1px solid #ccc;border-radius:8px;box-shadow:0 2px 4px #0000001a;display:flex;flex-direction:column;position:absolute;overflow:hidden;padding:15px;transition:box-shadow .2s ease-in-out,border-color .2s ease-in-out}.widget-container:hover{box-shadow:0 4px 8px #0003;border-color:#007bff;transform:translateY(-2px)}.widget-container.is-active{box-shadow:0 10px 20px #00000040,0 6px 6px #0000003b;border-color:#28a745;transform:scale(1.02);z-index:1000;cursor:grabbing!important}.widget-container .cdk-drag-placeholder{opacity:.4;background-color:#f0f0f0;border:1px dashed #999;transition:none}.widget-container .cdk-drag-preview{box-sizing:border-box;border-radius:4px;box-shadow:0 5px 5px -3px #0003,0 8px 10px 1px #00000024,0 3px 14px 2px #0000001f}.widget-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:10px}.widget-container.draggable .widget-header{cursor:move}.widget-header-left{display:flex;align-items:center;gap:8px;overflow:hidden}.widget-header-icon{flex-shrink:0}.widget-header-left h3{margin:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.widget-header h3{margin:0;font-size:1.2em}.widget-drag-handle{color:#aaa;flex-shrink:0}.widget-close-button{background:none;border:none;cursor:pointer;padding:0;line-height:1;transition:color .2s ease-in-out}.widget-close-button mat-icon{font-size:20px;vertical-align:middle}.widget-content{flex-grow:1;overflow:auto;font-size:.9em}.widget-content p{margin-top:0;line-height:1.6}.widgets-host-container{position:relative;width:100%;min-height:600px;border:1px dashed #eee}.widget-container.disabled{opacity:.6;pointer-events:none;cursor:not-allowed}.widget-container.resizable{resize:both}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i3.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: DragDropModule }, { kind: "directive", type: i4.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: i4.CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }, { kind: "component", type: RuclibWidgetItemComponent, selector: "uxp-ruclib-widget-item", inputs: ["widgetConfig"] }] }); }
|
|
219
|
+
}
|
|
220
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: RuclibWidgetComponent, decorators: [{
|
|
221
|
+
type: Component,
|
|
222
|
+
args: [{ selector: 'uxp-ruclib-widget', imports: [CommonModule, MatIconModule,
|
|
223
|
+
MatButtonModule,
|
|
224
|
+
DragDropModule, RuclibWidgetItemComponent], template: "<!--\r\nThe main container for all widgets.\r\n- Applies a custom theme class if provided.\r\n- Acts as the boundary for dragging operations via the #widgetsHostContainer template reference.\r\n-->\r\n<div\r\n class=\"widgets-host-container {{ customTheme || '' }}\"\r\n #widgetsHostContainer\r\n >\r\n <!--\r\n Iterates through the widget data to render each individual widget.\r\n Each widget is a draggable container powered by Angular CDK.\r\n -->\r\n @for (widgetConfig of getWidgetData(); track widgetConfig) {\r\n <div\r\n [style.width]=\"widgetConfig.width ? widgetConfig.width : '200px'\"\r\n [style.height]=\"widgetConfig.height ? widgetConfig.height : '150px'\"\r\n [style.minWidth]=\"widgetConfig.width ? widgetConfig.width : '200px'\"\r\n [style.minHeight]=\"widgetConfig.height ? widgetConfig.height : '150px'\"\r\n [style.top]=\"widgetConfig.top\"\r\n [style.left]=\"widgetConfig.left\"\r\n [style.background-color]=\"widgetConfig.backgroundColor\"\r\n [class.disabled]=\"widgetConfig.disabled\"\r\n [class.draggable]=\"widgetConfig.draggable && !widgetConfig.disabled\"\r\n [class.resizable]=\"widgetConfig.resizable && !widgetConfig.disabled\"\r\n class=\"widget-container\"\r\n cdkDrag\r\n [cdkDragDisabled]=\"!widgetConfig.draggable || widgetConfig.disabled\"\r\n [cdkDragBoundary]=\"dragBoundaryElement\"\r\n (cdkDragStarted)=\"onDragStarted(widgetConfig, $event)\"\r\n (cdkDragEnded)=\"onDragEnded(widgetConfig, $event)\"\r\n [ngClass]=\"{ 'is-active': widgetConfig.isActive }\"\r\n >\r\n <!--\r\n Widget Header: Contains title and controls. Acts as the handle for dragging.\r\n -->\r\n <div class=\"widget-header\" cdkDragHandle>\r\n <div class=\"widget-header-left\">\r\n <!-- Draggable indicator icon, shown only if the widget is draggable and not disabled -->\r\n @if (widgetConfig.draggable && !widgetConfig.disabled) {\r\n <mat-icon\r\n class=\"widget-drag-handle\"\r\n aria-hidden=\"true\"\r\n >drag_indicator</mat-icon\r\n >\r\n }\r\n <!-- Optional header icon -->\r\n @if (widgetConfig.headerIcon) {\r\n <mat-icon class=\"widget-header-icon\">{{\r\n widgetConfig.headerIcon\r\n }}</mat-icon>\r\n }\r\n <!-- Widget Title -->\r\n @if (widgetConfig?.title) {\r\n <h3>{{ widgetConfig?.title }}</h3>\r\n }\r\n </div>\r\n <!-- Optional close button with accessibility label -->\r\n @if (widgetConfig?.showCloseIcon) {\r\n <button\r\n color=\"{{ getColor() }}\"\r\n mat-icon-button\r\n class=\"widget-close-button\"\r\n [disabled]=\"widgetConfig.disabled\"\r\n (click)=\"onCloseClick(widgetConfig.id)\"\r\n [attr.aria-label]=\"'Close widget ' + widgetConfig?.title\"\r\n >\r\n <mat-icon color=\"{{ getColor() }}\">close</mat-icon>\r\n </button>\r\n }\r\n </div>\r\n <!--\r\n Widget Content: Renders the main content of the widget using the child uxp-ruclib-widget-item component.\r\n -->\r\n <div class=\"widget-content\">\r\n <uxp-ruclib-widget-item\r\n [widgetConfig]=\"widgetConfig\"\r\n ></uxp-ruclib-widget-item>\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n", styles: [":host{display:block}.widget-container{border:1px solid #ccc;border-radius:8px;box-shadow:0 2px 4px #0000001a;display:flex;flex-direction:column;position:absolute;overflow:hidden;padding:15px;transition:box-shadow .2s ease-in-out,border-color .2s ease-in-out}.widget-container:hover{box-shadow:0 4px 8px #0003;border-color:#007bff;transform:translateY(-2px)}.widget-container.is-active{box-shadow:0 10px 20px #00000040,0 6px 6px #0000003b;border-color:#28a745;transform:scale(1.02);z-index:1000;cursor:grabbing!important}.widget-container .cdk-drag-placeholder{opacity:.4;background-color:#f0f0f0;border:1px dashed #999;transition:none}.widget-container .cdk-drag-preview{box-sizing:border-box;border-radius:4px;box-shadow:0 5px 5px -3px #0003,0 8px 10px 1px #00000024,0 3px 14px 2px #0000001f}.widget-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:10px}.widget-container.draggable .widget-header{cursor:move}.widget-header-left{display:flex;align-items:center;gap:8px;overflow:hidden}.widget-header-icon{flex-shrink:0}.widget-header-left h3{margin:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.widget-header h3{margin:0;font-size:1.2em}.widget-drag-handle{color:#aaa;flex-shrink:0}.widget-close-button{background:none;border:none;cursor:pointer;padding:0;line-height:1;transition:color .2s ease-in-out}.widget-close-button mat-icon{font-size:20px;vertical-align:middle}.widget-content{flex-grow:1;overflow:auto;font-size:.9em}.widget-content p{margin-top:0;line-height:1.6}.widgets-host-container{position:relative;width:100%;min-height:600px;border:1px dashed #eee}.widget-container.disabled{opacity:.6;pointer-events:none;cursor:not-allowed}.widget-container.resizable{resize:both}\n"] }]
|
|
225
|
+
}], ctorParameters: () => [{ type: i0.ChangeDetectorRef }], propDecorators: { rucInputData: [{
|
|
226
|
+
type: Input
|
|
227
|
+
}], customTheme: [{
|
|
228
|
+
type: Input
|
|
229
|
+
}], widgetClose: [{
|
|
230
|
+
type: Output
|
|
231
|
+
}], layoutChanged: [{
|
|
232
|
+
type: Output
|
|
233
|
+
}], widgetsHostContainerRef: [{
|
|
234
|
+
type: ViewChild,
|
|
235
|
+
args: ['widgetsHostContainer']
|
|
236
|
+
}] } });
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Generated bundle index. Do not edit.
|
|
240
|
+
*/
|
|
241
|
+
|
|
242
|
+
export { RuclibWidgetComponent };
|
|
243
|
+
//# sourceMappingURL=ruc-lib-widget.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ruc-lib-widget.mjs","sources":["../../src/pipes/safe-html.pipe.ts","../../src/lib/ruclib-widget-item/ruclib-widget-item.component.ts","../../src/lib/ruclib-widget-item/ruclib-widget-item.component.html","../../src/lib/ruclib-widget/ruclib-widget.component.ts","../../src/lib/ruclib-widget/ruclib-widget.component.html","../../src/ruc-lib-widget.ts"],"sourcesContent":["import { Pipe, PipeTransform } from '@angular/core';\r\nimport { DomSanitizer } from '@angular/platform-browser';\r\n\r\n/**\r\n * @Pipe SafeHtmlPipe\r\n * @name safeHtml\r\n * @description A pipe that bypasses Angular's built-in security and sanitizes HTML content,\r\n * allowing it to be safely rendered in the DOM. Use with caution and only with trusted HTML sources.\r\n */\r\n@Pipe({ name: 'safeHtml', standalone:true })\r\nexport class SafeHtmlPipe implements PipeTransform {\r\n /**\r\n * @param sanitizer - An instance of DomSanitizer used to bypass security.\r\n */\r\n constructor(private sanitizer: DomSanitizer) { }\r\n /**\r\n * Transforms a string containing HTML into a SafeHtml object that can be bound to [innerHTML].\r\n * @param value - The HTML string to sanitize.\r\n * @returns A SafeHtml object, which Angular trusts as safe HTML.\r\n */\r\n transform(value: any) {\r\n if (value === null || value === undefined) {\r\n return value;\r\n }\r\n return this.sanitizer.bypassSecurityTrustHtml(value);\r\n }\r\n}\r\n","import {\r\n Component,\r\n Input,\r\n ViewChild,\r\n ViewContainerRef,\r\n ComponentRef,\r\n OnChanges,\r\n SimpleChanges,\r\n OnDestroy,\r\n Type,\r\n ChangeDetectorRef,\r\n AfterViewInit\r\n} from '@angular/core';\r\nimport { DomSanitizer } from '@angular/platform-browser';\r\nimport { WidgetConfigData } from '../../interface/widget';\r\n\r\nimport { SafeHtmlPipe } from '../../pipes/safe-html.pipe';\r\n\r\n\r\n@Component({\r\n selector: 'uxp-ruclib-widget-item',\r\n imports: [SafeHtmlPipe],\r\n templateUrl: './ruclib-widget-item.component.html',\r\n styleUrl: './ruclib-widget-item.component.scss'\r\n})\r\nexport class RuclibWidgetItemComponent implements OnChanges, OnDestroy, AfterViewInit {\r\n /**\r\n * The configuration object for this specific widget item.\r\n * It determines the type of content to render and provides the necessary data.\r\n */\r\n @Input() widgetConfig!: WidgetConfigData;\r\n\r\n /**\r\n * A reference to the view container where a dynamic component will be injected.\r\n * This is used when `widgetConfig.contentType` is 'component'.\r\n */\r\n @ViewChild('widgetComponentHost', { read: ViewContainerRef }) widgetComponentHost!: ViewContainerRef;\r\n /**\r\n * A reference to the dynamically created component instance.\r\n * This is used to manage the lifecycle of the injected component.\r\n */\r\n private dynamicComponentRef: ComponentRef<unknown> | null = null;\r\n\r\n /**\r\n * @param cdr The ChangeDetectorRef to manually trigger change detection.\r\n * @param sanitizer The DomSanitizer service to prevent XSS attacks by sanitizing HTML.\r\n */\r\n constructor(private cdr: ChangeDetectorRef, private sanitizer: DomSanitizer) { }\r\n\r\n /**\r\n * A lifecycle hook that responds when Angular sets or resets data-bound input properties.\r\n * It checks for changes in `widgetConfig` and reloads the content accordingly.\r\n * @param changes An object of the changed properties.\r\n */\r\n ngOnChanges(changes: SimpleChanges): void {\r\n if (changes['widgetConfig']) {\r\n if (this.widgetConfig.contentType === 'component' && this.widgetComponentHost) {\r\n this.loadDynamicContent();\r\n } else if (this.widgetConfig.contentType !== 'component') {\r\n this.clearDynamicContent();\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * A lifecycle hook that is called after Angular has fully initialized a component's view.\r\n * It ensures that if the initial content type is 'component', it gets loaded.\r\n */\r\n ngAfterViewInit(): void {\r\n if(this.widgetConfig) {\r\n if (this.widgetConfig.contentType === 'component' && this.widgetComponentHost) {\r\n this.loadDynamicContent();\r\n }\r\n }\r\n\r\n }\r\n\r\n /**\r\n * Loads a dynamic component into the `widgetComponentHost` view container.\r\n * It reads the component type and any input data from the `widgetConfig`.\r\n */\r\n private loadDynamicContent(): void {\r\n this.clearDynamicContent();\r\n if (this.widgetConfig.contentType === 'component' && this.widgetConfig.contentData && this.widgetConfig.contentData.component) {\r\n if (this.widgetComponentHost) {\r\n const componentType = this.widgetConfig.contentData.component as Type<unknown>;\r\n this.dynamicComponentRef = this.widgetComponentHost.createComponent(componentType);\r\n\r\n if (this.widgetConfig.contentData.inputs && this.dynamicComponentRef?.instance) {\r\n Object.keys(this.widgetConfig.contentData.inputs).forEach(key => {\r\n if (this.dynamicComponentRef && key in (this.dynamicComponentRef.instance as any)) {\r\n (this.dynamicComponentRef.instance as any)[key] = this.widgetConfig.contentData.inputs[key];\r\n }\r\n });\r\n this.dynamicComponentRef.changeDetectorRef.detectChanges();\r\n }\r\n }\r\n }\r\n this.cdr.detectChanges();\r\n }\r\n\r\n /**\r\n * Clears any dynamically loaded component from the view container and destroys its instance.\r\n */\r\n private clearDynamicContent(): void {\r\n if (this.dynamicComponentRef) {\r\n this.dynamicComponentRef.destroy();\r\n this.dynamicComponentRef = null;\r\n }\r\n if (this.widgetComponentHost) {\r\n this.widgetComponentHost.clear();\r\n }\r\n }\r\n\r\n /**\r\n * A lifecycle hook that cleans up the component before it's destroyed.\r\n * Ensures that any dynamically created components are properly destroyed to avoid memory leaks.\r\n */\r\n ngOnDestroy(): void {\r\n this.clearDynamicContent();\r\n }\r\n}\r\n","<!--\r\nThis container uses a switch to determine how to render the widget's content\r\nbased on the `contentType` property in the widget's configuration.\r\n-->\r\n@if (widgetConfig) {\r\n @switch (widgetConfig.contentType) {\r\n @case ('text') {\r\n <p>{{ widgetConfig.contentData }}</p>\r\n }\r\n @case ('html') {\r\n <div [innerHTML]=\"widgetConfig.contentData | safeHtml\"></div>\r\n }\r\n @case ('video') {\r\n <video\r\n [src]=\"widgetConfig.contentData.src\"\r\n [attr.type]=\"widgetConfig.contentData.type\"\r\n [controls]=\"widgetConfig.contentData.controls\"\r\n [autoplay]=\"widgetConfig.contentData.autoplay\"\r\n [loop]=\"widgetConfig.contentData.loop\"\r\n style=\"width: 100%; height: 100%; object-fit: contain\"\r\n ></video>\r\n }\r\n @case ('component') {\r\n <ng-template #widgetComponentHost></ng-template>\r\n }\r\n @default {\r\n <p>No content provided or content type not supported.</p>\r\n }\r\n }\r\n}\r\n","import { MatButtonModule } from '@angular/material/button';\r\nimport { MatIconModule } from '@angular/material/icon';\r\nimport { Component, Input, Output, EventEmitter, ViewChild, ElementRef, AfterViewInit, ChangeDetectorRef } from '@angular/core';\r\nimport { WidgetConfig, WidgetConfigData } from '../../interface/widget';\r\nimport { CdkDragStart, CdkDragEnd, DragDropModule } from '@angular/cdk/drag-drop';\r\nimport { CommonModule } from '@angular/common';\r\nimport { RuclibWidgetItemComponent } from '../ruclib-widget-item/ruclib-widget-item.component';\r\n\r\n@Component({\r\n selector: 'uxp-ruclib-widget',\r\n imports: [CommonModule, MatIconModule,\r\n MatButtonModule,\r\n DragDropModule, RuclibWidgetItemComponent],\r\n templateUrl: './ruclib-widget.component.html',\r\n styleUrl: './ruclib-widget.component.scss'\r\n})\r\nexport class RuclibWidgetComponent implements AfterViewInit {\r\n /**\r\n * The main configuration object for the widget container.\r\n * It includes the array of widget data and global settings.\r\n * @see WidgetConfig interface for detailed properties.\r\n */\r\n @Input() rucInputData!: WidgetConfig;\r\n\r\n /**\r\n * An optional custom theme class to be applied to the widget host container.\r\n * @example 'dark-theme', 'custom-theme-one'\r\n */\r\n @Input() customTheme: string | undefined;\r\n\r\n /**\r\n * An event emitter that fires when a widget's close icon is clicked.\r\n * It emits the unique ID of the widget to be closed.\r\n */\r\n @Output() widgetClose = new EventEmitter<string>();\r\n\r\n /**\r\n * An event emitter that fires when a widget is dragged and dropped.\r\n * It emits the entire updated array of widget configurations, allowing the parent to save the new layout.\r\n */\r\n @Output() layoutChanged = new EventEmitter<WidgetConfigData[]>();\r\n\r\n /**\r\n * @param cdr The ChangeDetectorRef to manually trigger change detection when needed.\r\n */\r\n constructor(private cdr: ChangeDetectorRef) { }\r\n\r\n /** A reference to the host container element, used to define the drag boundary. */\r\n @ViewChild('widgetsHostContainer') widgetsHostContainerRef!: ElementRef<HTMLElement>;\r\n /** The HTML element that serves as the boundary for all drag operations. */\r\n public dragBoundaryElement!: HTMLElement;\r\n\r\n /**\r\n * After the view initializes, this hook captures the host container's native element\r\n * and sets it as the boundary for dragging widgets.\r\n */\r\n ngAfterViewInit(): void {\r\n if (this.widgetsHostContainerRef) {\r\n this.dragBoundaryElement = this.widgetsHostContainerRef.nativeElement;\r\n this.cdr.detectChanges();\r\n }\r\n }\r\n\r\n /**\r\n * Handles the click event on a widget's close button.\r\n * @param widgetId The ID of the widget to be closed.\r\n */\r\n onCloseClick(widgetId: string): void {\r\n this.widgetClose.emit(widgetId);\r\n }\r\n\r\n /**\r\n * Handles the start of a drag operation for a widget.\r\n * Sets the widget's isActive state to true for visual feedback.\r\n * @param widget The WidgetConfigData object being dragged.\r\n * @param event The CdkDragStart event.\r\n */\r\n onDragStarted(widget: WidgetConfigData, event: CdkDragStart): void {\r\n widget.isActive = true;\r\n }\r\n\r\n /**\r\n * Handles the end of a drag operation.\r\n * It calculates the new top/left position, updates the widget's configuration,\r\n * and emits the `layoutChanged` event with the new layout data.\r\n * @param widget The WidgetConfigData object that was dragged.\r\n * @param event The CdkDragEnd event.\r\n */\r\n onDragEnded(widget: WidgetConfigData, event: CdkDragEnd): void {\r\n widget.isActive = false;\r\n\r\n const newPosition = event.source.getFreeDragPosition();\r\n\r\n const initialTopPx = parseFloat(widget.top || '0');\r\n const initialLeftPx = parseFloat(widget.left || '0');\r\n\r\n const newTopPx = initialTopPx + newPosition.y;\r\n const newLeftPx = initialLeftPx + newPosition.x;\r\n\r\n widget.top = `${newTopPx}px`;\r\n widget.left = `${newLeftPx}px`;\r\n\r\n event.source.reset();\r\n\r\n if (this.rucInputData.widgetData) {\r\n this.layoutChanged.emit(this.rucInputData.widgetData);\r\n }\r\n }\r\n\r\n /**\r\n * Retrieves the color for UI elements like icons from the input data.\r\n * @returns The color string (e.g., 'primary', 'accent') or 'primary' as a default.\r\n */\r\n getColor(): string {\r\n return this.rucInputData?.color || 'primary';\r\n }\r\n\r\n /**\r\n * A safe getter for the widget data array from the main input configuration.\r\n * @returns The array of widget configurations, or an empty array if not defined.\r\n */\r\n getWidgetData(): WidgetConfigData[] | any {\r\n return typeof this.rucInputData.widgetData !== 'undefined' ? this.rucInputData.widgetData : [];\r\n }\r\n}\r\n\r\n","<!--\r\nThe main container for all widgets.\r\n- Applies a custom theme class if provided.\r\n- Acts as the boundary for dragging operations via the #widgetsHostContainer template reference.\r\n-->\r\n<div\r\n class=\"widgets-host-container {{ customTheme || '' }}\"\r\n #widgetsHostContainer\r\n >\r\n <!--\r\n Iterates through the widget data to render each individual widget.\r\n Each widget is a draggable container powered by Angular CDK.\r\n -->\r\n @for (widgetConfig of getWidgetData(); track widgetConfig) {\r\n <div\r\n [style.width]=\"widgetConfig.width ? widgetConfig.width : '200px'\"\r\n [style.height]=\"widgetConfig.height ? widgetConfig.height : '150px'\"\r\n [style.minWidth]=\"widgetConfig.width ? widgetConfig.width : '200px'\"\r\n [style.minHeight]=\"widgetConfig.height ? widgetConfig.height : '150px'\"\r\n [style.top]=\"widgetConfig.top\"\r\n [style.left]=\"widgetConfig.left\"\r\n [style.background-color]=\"widgetConfig.backgroundColor\"\r\n [class.disabled]=\"widgetConfig.disabled\"\r\n [class.draggable]=\"widgetConfig.draggable && !widgetConfig.disabled\"\r\n [class.resizable]=\"widgetConfig.resizable && !widgetConfig.disabled\"\r\n class=\"widget-container\"\r\n cdkDrag\r\n [cdkDragDisabled]=\"!widgetConfig.draggable || widgetConfig.disabled\"\r\n [cdkDragBoundary]=\"dragBoundaryElement\"\r\n (cdkDragStarted)=\"onDragStarted(widgetConfig, $event)\"\r\n (cdkDragEnded)=\"onDragEnded(widgetConfig, $event)\"\r\n [ngClass]=\"{ 'is-active': widgetConfig.isActive }\"\r\n >\r\n <!--\r\n Widget Header: Contains title and controls. Acts as the handle for dragging.\r\n -->\r\n <div class=\"widget-header\" cdkDragHandle>\r\n <div class=\"widget-header-left\">\r\n <!-- Draggable indicator icon, shown only if the widget is draggable and not disabled -->\r\n @if (widgetConfig.draggable && !widgetConfig.disabled) {\r\n <mat-icon\r\n class=\"widget-drag-handle\"\r\n aria-hidden=\"true\"\r\n >drag_indicator</mat-icon\r\n >\r\n }\r\n <!-- Optional header icon -->\r\n @if (widgetConfig.headerIcon) {\r\n <mat-icon class=\"widget-header-icon\">{{\r\n widgetConfig.headerIcon\r\n }}</mat-icon>\r\n }\r\n <!-- Widget Title -->\r\n @if (widgetConfig?.title) {\r\n <h3>{{ widgetConfig?.title }}</h3>\r\n }\r\n </div>\r\n <!-- Optional close button with accessibility label -->\r\n @if (widgetConfig?.showCloseIcon) {\r\n <button\r\n color=\"{{ getColor() }}\"\r\n mat-icon-button\r\n class=\"widget-close-button\"\r\n [disabled]=\"widgetConfig.disabled\"\r\n (click)=\"onCloseClick(widgetConfig.id)\"\r\n [attr.aria-label]=\"'Close widget ' + widgetConfig?.title\"\r\n >\r\n <mat-icon color=\"{{ getColor() }}\">close</mat-icon>\r\n </button>\r\n }\r\n </div>\r\n <!--\r\n Widget Content: Renders the main content of the widget using the child uxp-ruclib-widget-item component.\r\n -->\r\n <div class=\"widget-content\">\r\n <uxp-ruclib-widget-item\r\n [widgetConfig]=\"widgetConfig\"\r\n ></uxp-ruclib-widget-item>\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":["i1"],"mappings":"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"}
|