nativescript-web-adapter 0.1.5 → 0.1.6

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.
Files changed (70) hide show
  1. package/README.md +32 -14
  2. package/angular/components/absolute-layout.component.ts +18 -0
  3. package/angular/components/action-bar.component.ts +16 -0
  4. package/angular/components/action-item.component.ts +22 -0
  5. package/angular/components/activity-indicator.component.ts +32 -0
  6. package/angular/components/button.component.ts +45 -0
  7. package/angular/components/date-picker.component.ts +35 -0
  8. package/angular/components/directives/absolute-position.directive.ts +31 -0
  9. package/angular/components/directives/grid-position.directive.ts +43 -0
  10. package/angular/components/dock-layout.component.ts +46 -0
  11. package/angular/components/flexbox-layout.component.ts +16 -0
  12. package/angular/components/frame.component.ts +9 -0
  13. package/angular/components/grid-layout.component.ts +48 -0
  14. package/angular/components/html-view.component.ts +23 -0
  15. package/angular/components/image-cache-it.component.ts +21 -0
  16. package/angular/components/image.component.ts +22 -0
  17. package/angular/components/index.ts +156 -0
  18. package/angular/components/label.component.ts +27 -0
  19. package/angular/components/list-picker.component.ts +33 -0
  20. package/angular/components/list-view.component.ts +22 -0
  21. package/angular/components/navigation-button.component.ts +36 -0
  22. package/angular/components/page.component.ts +21 -0
  23. package/angular/components/placeholder.component.ts +16 -0
  24. package/angular/components/progress.component.ts +20 -0
  25. package/angular/components/root-layout.component.ts +19 -0
  26. package/angular/components/scroll-view.component.ts +17 -0
  27. package/angular/components/search-bar.component.ts +34 -0
  28. package/angular/components/segmented-bar-item.component.ts +27 -0
  29. package/angular/components/segmented-bar.component.ts +101 -0
  30. package/angular/components/slider.component.ts +41 -0
  31. package/angular/components/stack-layout.component.ts +17 -0
  32. package/angular/components/switch.component.ts +62 -0
  33. package/angular/components/tab-view-item.component.ts +27 -0
  34. package/angular/components/tab-view.component.ts +89 -0
  35. package/angular/components/text-field.component.ts +38 -0
  36. package/angular/components/text-view.component.ts +29 -0
  37. package/angular/components/time-picker.component.ts +27 -0
  38. package/angular/components/web-view.component.ts +25 -0
  39. package/angular/components/wrap-layout.component.ts +17 -0
  40. package/angular/composables/dialogs.ts +54 -0
  41. package/angular/composables/index.ts +6 -0
  42. package/angular/composables/ref.ts +8 -0
  43. package/angular/composables/useActionBar.ts +20 -0
  44. package/angular/composables/useFrame.ts +26 -0
  45. package/angular/composables/usePage.ts +26 -0
  46. package/angular/index.ts +8 -0
  47. package/core/components/Button.vue +2 -2
  48. package/core/components/GridLayout.vue +2 -2
  49. package/core/components/HtmlView.vue +2 -2
  50. package/core/components/Image.vue +2 -2
  51. package/core/components/ImageCacheIt.vue +2 -2
  52. package/core/components/Label.vue +2 -2
  53. package/core/components/ListPicker.vue +2 -2
  54. package/core/components/NavigationButton.vue +2 -2
  55. package/core/components/Progress.vue +2 -2
  56. package/core/components/SearchBar.vue +2 -2
  57. package/core/components/Slider.vue +2 -2
  58. package/core/components/Switch.vue +2 -2
  59. package/core/components/TextField.vue +2 -2
  60. package/core/components/TextView.vue +2 -2
  61. package/core/components/WebView.vue +2 -2
  62. package/core/composables/dialogs.ts +5 -5
  63. package/dist/nativescript-web-adapter.es.js +22375 -45
  64. package/dist/nativescript-web-adapter.umd.js +534 -1
  65. package/package.json +37 -7
  66. package/tools/cli.cjs +17 -4
  67. package/tools/create-web-platform.cjs +40 -18
  68. package/tools/modules/appPatch.cjs +125 -1
  69. package/tools/modules/templates.cjs +161 -84
  70. package/tools/modules/transform.cjs +69 -2
package/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
  [![npm](https://img.shields.io/npm/v/nativescript-web-adapter.svg?logo=npm&label=npm)](https://www.npmjs.com/package/nativescript-web-adapter)
6
6
  [![MIT License](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
7
7
 
8
- This project is a “web adapter” for NativeScript‑Vue‑Vite. It can transform native app code into a pure Vue Web project that runs in the browser (generated under `platforms/web/`).
8
+ This project is a “web adapter” for NativeScript apps. It can transform native app code into a Web project that runs in the browser (generated under `platforms/<framework>/`).
9
9
 
10
10
  ---
11
11
 
@@ -15,14 +15,26 @@ This project is a “web adapter” for NativeScript‑Vue‑Vite. It can transf
15
15
  npm install nativescript-web-adapter
16
16
  ```
17
17
 
18
- Compile the web template – install dependencies – start the web project's dev server:
18
+ Generate the web template – install dependencies – run the web project:
19
19
 
20
- vue
20
+ vue (dev server)
21
21
 
22
22
  ```bash
23
23
  npx ns-web vue
24
24
  ```
25
25
 
26
+ angular (build)
27
+
28
+ ```bash
29
+ npx ns-web angular
30
+ ```
31
+
32
+ auto (build, detects framework from the project)
33
+
34
+ ```bash
35
+ npx ns-web build
36
+ ```
37
+
26
38
  nuxt
27
39
 
28
40
  ```bash
@@ -61,8 +73,8 @@ ns run android
61
73
  ## Background & Goals
62
74
 
63
75
  - Provide a native app example using `nativescript-vue` + `@nativescript/vite`, showcasing development and HMR on iOS/Android.
64
- - Provide a “web adapter” (local package `nativescript-web-adapter`) that scans and transforms the project's `src/` code to generate a Vue application that runs in the browser, enabling quick preview and collaboration on desktop browsers.
65
- - The generated Web project lives in `platforms/web/` with its own Vite config, dependencies, and entry files, without affecting the native side.
76
+ - Provide a “web adapter” (local package `nativescript-web-adapter`) that scans and transforms the project's `src/` code to generate a Web application that runs in the browser (Vue or Angular), enabling quick preview and collaboration on desktop browsers.
77
+ - The generated Web project lives in `platforms/<framework>/` with its own Vite config, dependencies, and entry files, without affecting the native side.
66
78
 
67
79
  ---
68
80
 
@@ -101,13 +113,15 @@ ns run android
101
113
 
102
114
  ---
103
115
 
104
- ## Web Side (Generated Vue Project)
116
+ ## Web Side (Generated Web Project)
105
117
 
106
- - Output location: `platforms/web/`
118
+ - Output location:
119
+ - Vue: `platforms/vue/`
120
+ - Angular: `platforms/angular/`
107
121
  - Example structure:
108
122
 
109
123
  ```
110
- platforms/web/
124
+ platforms/vue/
111
125
  package.json # generated (includes vue + vue-router)
112
126
  vite.config.ts # generated (server.port = 3005, strictPort = true)
113
127
  index.html
@@ -125,6 +139,8 @@ platforms/web/
125
139
  websfc/ # adapter composables (useActionBar/usePage/useFrame)
126
140
  ```
127
141
 
142
+ Angular platform uses `platforms/angular/` and keeps the same `src/components/websfc` + `src/composables/websfc` structure, but the entry is `src/main.ts` and the HTML root is `<ns-app></ns-app>`.
143
+
128
144
  - Default dev URL: `http://localhost:3005/` (if the port is taken, it fails directly; set `strictPort` to `false` in `vite.config.ts` to enable fallback).
129
145
 
130
146
  ---
@@ -152,8 +168,9 @@ platforms/web/
152
168
  - `npm run dev:android`: run Vite and NS Android debug in parallel.
153
169
  - `npm run ios` / `npm run android`: use `ns debug` for debugging builds.
154
170
  - Generate and run the Web project:
155
- - `npm run dev:web`: run the adapter generator, then enter `platforms/web`, install deps, and start the Web Vite dev server.
156
- - On first run, it creates `platforms/web` along with required templates and configuration.
171
+ - Vue dev server: `npx ns-web vue`
172
+ - Angular build: `npx ns-web angular`
173
+ - Auto build (detects framework): `npx ns-web build`
157
174
 
158
175
  ---
159
176
 
@@ -217,7 +234,8 @@ The generator’s `transformContent()` currently performs only necessary code‑
217
234
  - Uses `@nativescript/vite` with `vite serve -- --env.hmr` to establish an HMR channel, pushing changes to the device.
218
235
  - `patches/nativescript-vue+3.0.1.patch` injects within `app.start`: `globalThis.__NS_VUE_APP__ = app`, helping restore state in deep navigation stacks (e.g., when returning during HMR).
219
236
  - Web side:
220
- - Independent Vite (`platforms/web/vite.config.ts`) runs on port `3005`, using standard Vue HMR and route refresh.
237
+ - Vue Vite (`platforms/vue/vite.config.ts`) runs on port `3005`.
238
+ - Angular Vite (`platforms/angular/vite.config.ts`) runs on port `3006`.
221
239
 
222
240
  ---
223
241
 
@@ -229,8 +247,8 @@ The generator’s `transformContent()` currently performs only necessary code‑
229
247
  - Basic web components are provided (`ActionBar/Page/Frame/Grid/Stack/Flex/Wrap/Scroll/Label/Button/Image/HtmlView/ImageCacheIt`). More detailed property‑to‑style mappings will be added later (e.g., `flexDirection/row`, grid rows/columns).
230
248
  - Regex‑driven transformation:
231
249
  - Edge cases may exist for complex code and templates. Gradually moving to AST‑level transformation is recommended.
232
- - Generator template duplicate writes:
233
- - The generator currently writes `platforms/web/package.json` twice (with different dependency versions). This can be streamlined into a single write to reduce maintenance cost.
250
+ - Generator templates:
251
+ - The generator writes framework-specific Vite templates and copies adapter components/composables into `src/components/websfc` and `src/composables/websfc`.
234
252
 
235
253
  ---
236
254
 
@@ -243,4 +261,4 @@ The generator’s `transformContent()` currently performs only necessary code‑
243
261
  3. Tests & validation:
244
262
  - Add unit tests and e2e tests for transformation rules and template generation to ensure stability across different project structures.
245
263
 
246
- ---
264
+ ---
@@ -0,0 +1,18 @@
1
+ import { ChangeDetectionStrategy, Component } from '@angular/core';
2
+
3
+ @Component({
4
+ selector: 'AbsoluteLayout, absolutelayout',
5
+ standalone: true,
6
+ changeDetection: ChangeDetectionStrategy.OnPush,
7
+ template: `<div class="ns-absolute"><ng-content></ng-content></div>`,
8
+ styles: [
9
+ `
10
+ .ns-absolute {
11
+ position: relative;
12
+ width: 100%;
13
+ height: 100%;
14
+ }
15
+ `,
16
+ ],
17
+ })
18
+ export class AbsoluteLayoutComponent {}
@@ -0,0 +1,16 @@
1
+ import { ChangeDetectionStrategy, Component } from '@angular/core';
2
+
3
+ @Component({
4
+ selector: 'ActionBar, actionbar',
5
+ standalone: true,
6
+ changeDetection: ChangeDetectionStrategy.OnPush,
7
+ template: `<header class="ns-actionbar"><ng-content></ng-content></header>`,
8
+ styles: [
9
+ `
10
+ .ns-actionbar {
11
+ padding: 12px;
12
+ }
13
+ `,
14
+ ],
15
+ })
16
+ export class ActionBarComponent {}
@@ -0,0 +1,22 @@
1
+ import { ChangeDetectionStrategy, Component, EventEmitter, Output } from '@angular/core';
2
+
3
+ @Component({
4
+ selector: 'ActionItem, actionitem',
5
+ standalone: true,
6
+ changeDetection: ChangeDetectionStrategy.OnPush,
7
+ template: `<button class="ns-actionitem" (click)="tap.emit($event)"><ng-content></ng-content></button>`,
8
+ styles: [
9
+ `
10
+ .ns-actionitem {
11
+ background: transparent;
12
+ border: none;
13
+ color: inherit;
14
+ cursor: pointer;
15
+ padding: 8px;
16
+ }
17
+ `,
18
+ ],
19
+ })
20
+ export class ActionItemComponent {
21
+ @Output() tap = new EventEmitter<MouseEvent>();
22
+ }
@@ -0,0 +1,32 @@
1
+ import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
2
+
3
+ @Component({
4
+ selector: 'ActivityIndicator, activityindicator',
5
+ standalone: true,
6
+ changeDetection: ChangeDetectionStrategy.OnPush,
7
+ template: `<div class="ns-activity-indicator" [class.busy]="busy"></div>`,
8
+ styles: [
9
+ `
10
+ .ns-activity-indicator {
11
+ width: 20px;
12
+ height: 20px;
13
+ border-radius: 50%;
14
+ border: 2px solid rgba(255, 255, 255, 0.3);
15
+ border-top-color: currentColor;
16
+ opacity: 0.8;
17
+ }
18
+ .ns-activity-indicator.busy {
19
+ animation: ns-spin 0.8s linear infinite;
20
+ }
21
+ @keyframes ns-spin {
22
+ to {
23
+ transform: rotate(360deg);
24
+ }
25
+ }
26
+ `,
27
+ ],
28
+ })
29
+ export class ActivityIndicatorComponent {
30
+ @Input() busy = true;
31
+ }
32
+
@@ -0,0 +1,45 @@
1
+ import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
2
+
3
+ @Component({
4
+ selector: 'Button, button[nsButton], ns-button',
5
+ standalone: true,
6
+ changeDetection: ChangeDetectionStrategy.OnPush,
7
+ template: `<button class="ns-button" [ngStyle]="btnStyle" (click)="tap.emit($event)"><ng-content></ng-content></button>`,
8
+ styles: [
9
+ `
10
+ .ns-button {
11
+ cursor: pointer;
12
+ }
13
+ `,
14
+ ],
15
+ })
16
+ export class ButtonComponent {
17
+ @Input() horizontalAlignment?: string;
18
+ @Output() tap = new EventEmitter<MouseEvent>();
19
+
20
+ get btnStyle(): Record<string, string> {
21
+ const style: Record<string, string> = {};
22
+ const h = this.horizontalAlignment?.toLowerCase();
23
+ if (h === 'center') {
24
+ style.display = 'block';
25
+ style.width = 'fit-content';
26
+ style.marginLeft = 'auto';
27
+ style.marginRight = 'auto';
28
+ style.alignSelf = 'center';
29
+ style.justifySelf = 'center';
30
+ } else if (h === 'right') {
31
+ style.display = 'block';
32
+ style.width = 'fit-content';
33
+ style.marginLeft = 'auto';
34
+ style.alignSelf = 'flex-end';
35
+ style.justifySelf = 'end';
36
+ } else if (h === 'left') {
37
+ style.display = 'block';
38
+ style.width = 'fit-content';
39
+ style.marginRight = 'auto';
40
+ style.alignSelf = 'flex-start';
41
+ style.justifySelf = 'start';
42
+ }
43
+ return style;
44
+ }
45
+ }
@@ -0,0 +1,35 @@
1
+ import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
2
+
3
+ @Component({
4
+ selector: 'DatePicker, datepicker',
5
+ standalone: true,
6
+ changeDetection: ChangeDetectionStrategy.OnPush,
7
+ template: `<input class="ns-datepicker" type="date" [value]="valueStr" (input)="onInput($event)" />`,
8
+ styles: [
9
+ `
10
+ .ns-datepicker {
11
+ padding: 6px 8px;
12
+ }
13
+ `,
14
+ ],
15
+ })
16
+ export class DatePickerComponent {
17
+ @Input() date?: string | Date;
18
+ @Output() dateChange = new EventEmitter<string | Date>();
19
+ @Output() change = new EventEmitter<string | Date>();
20
+
21
+ get valueStr(): string {
22
+ if (!this.date) return '';
23
+ const d = typeof this.date === 'string' ? new Date(this.date) : this.date;
24
+ if (Number.isNaN(d.getTime())) return '';
25
+ const pad = (n: number) => (n < 10 ? `0${n}` : String(n));
26
+ return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
27
+ }
28
+
29
+ onInput(e: Event) {
30
+ const v = (e.target as HTMLInputElement).value;
31
+ this.dateChange.emit(v);
32
+ this.change.emit(v);
33
+ }
34
+ }
35
+
@@ -0,0 +1,31 @@
1
+ import { Directive, ElementRef, Input, OnChanges, Renderer2 } from '@angular/core';
2
+
3
+ @Directive({
4
+ selector: '[left],[top]',
5
+ standalone: true,
6
+ })
7
+ export class NsAbsolutePositionDirective implements OnChanges {
8
+ @Input() left?: number | string;
9
+ @Input() top?: number | string;
10
+
11
+ constructor(
12
+ private readonly el: ElementRef<HTMLElement>,
13
+ private readonly renderer: Renderer2
14
+ ) {}
15
+
16
+ ngOnChanges(): void {
17
+ const host = this.el.nativeElement;
18
+ this.renderer.setStyle(host, 'position', 'absolute');
19
+ if (this.left !== undefined) this.renderer.setStyle(host, 'left', this.toCssPx(this.left));
20
+ if (this.top !== undefined) this.renderer.setStyle(host, 'top', this.toCssPx(this.top));
21
+ }
22
+
23
+ private toCssPx(v: number | string): string {
24
+ if (typeof v === 'number') return `${v}px`;
25
+ const t = v.trim();
26
+ if (!t) return '0px';
27
+ if (/^-?\d+(\.\d+)?$/.test(t)) return `${t}px`;
28
+ return t;
29
+ }
30
+ }
31
+
@@ -0,0 +1,43 @@
1
+ import { Directive, ElementRef, Input, OnChanges, Renderer2 } from '@angular/core';
2
+
3
+ @Directive({
4
+ selector: '[row],[col],[rowSpan],[colSpan]',
5
+ standalone: true,
6
+ })
7
+ export class NsGridPositionDirective implements OnChanges {
8
+ @Input() row?: number | string;
9
+ @Input() col?: number | string;
10
+ @Input() rowSpan?: number | string;
11
+ @Input() colSpan?: number | string;
12
+
13
+ constructor(
14
+ private readonly el: ElementRef<HTMLElement>,
15
+ private readonly renderer: Renderer2
16
+ ) {}
17
+
18
+ ngOnChanges(): void {
19
+ const host = this.el.nativeElement;
20
+ const row = this.toNumberOrUndefined(this.row);
21
+ const col = this.toNumberOrUndefined(this.col);
22
+ const rowSpan = this.toNumberOrUndefined(this.rowSpan);
23
+ const colSpan = this.toNumberOrUndefined(this.colSpan);
24
+
25
+ if (row !== undefined) {
26
+ const start = row + 1;
27
+ const span = rowSpan && rowSpan > 0 ? rowSpan : 1;
28
+ this.renderer.setStyle(host, 'grid-row', `${start} / span ${span}`);
29
+ }
30
+ if (col !== undefined) {
31
+ const start = col + 1;
32
+ const span = colSpan && colSpan > 0 ? colSpan : 1;
33
+ this.renderer.setStyle(host, 'grid-column', `${start} / span ${span}`);
34
+ }
35
+ }
36
+
37
+ private toNumberOrUndefined(v: number | string | undefined): number | undefined {
38
+ if (v === undefined || v === null) return undefined;
39
+ const n = typeof v === 'number' ? v : Number(v);
40
+ return Number.isFinite(n) ? n : undefined;
41
+ }
42
+ }
43
+
@@ -0,0 +1,46 @@
1
+ import { ChangeDetectionStrategy, Component } from '@angular/core';
2
+
3
+ @Component({
4
+ selector: 'DockLayout, docklayout',
5
+ standalone: true,
6
+ changeDetection: ChangeDetectionStrategy.OnPush,
7
+ template: `
8
+ <div class="ns-dock">
9
+ <div class="ns-dock-top"><ng-content select="[dock='top'],[dock='Top'],[dock='TOP']"></ng-content></div>
10
+ <div class="ns-dock-middle">
11
+ <div class="ns-dock-left"><ng-content select="[dock='left'],[dock='Left'],[dock='LEFT']"></ng-content></div>
12
+ <div class="ns-dock-center"><ng-content></ng-content></div>
13
+ <div class="ns-dock-right"><ng-content select="[dock='right'],[dock='Right'],[dock='RIGHT']"></ng-content></div>
14
+ </div>
15
+ <div class="ns-dock-bottom"><ng-content select="[dock='bottom'],[dock='Bottom'],[dock='BOTTOM']"></ng-content></div>
16
+ </div>
17
+ `,
18
+ styles: [
19
+ `
20
+ .ns-dock {
21
+ display: flex;
22
+ flex-direction: column;
23
+ width: 100%;
24
+ height: 100%;
25
+ }
26
+ .ns-dock-top,
27
+ .ns-dock-bottom {
28
+ flex: 0 0 auto;
29
+ }
30
+ .ns-dock-middle {
31
+ display: flex;
32
+ flex: 1 1 auto;
33
+ min-height: 0;
34
+ }
35
+ .ns-dock-left,
36
+ .ns-dock-right {
37
+ flex: 0 0 auto;
38
+ }
39
+ .ns-dock-center {
40
+ flex: 1 1 auto;
41
+ min-width: 0;
42
+ }
43
+ `,
44
+ ],
45
+ })
46
+ export class DockLayoutComponent {}
@@ -0,0 +1,16 @@
1
+ import { ChangeDetectionStrategy, Component } from '@angular/core';
2
+
3
+ @Component({
4
+ selector: 'FlexboxLayout, flexboxlayout',
5
+ standalone: true,
6
+ changeDetection: ChangeDetectionStrategy.OnPush,
7
+ template: `<div class="ns-flex"><ng-content></ng-content></div>`,
8
+ styles: [
9
+ `
10
+ .ns-flex {
11
+ display: flex;
12
+ }
13
+ `,
14
+ ],
15
+ })
16
+ export class FlexboxLayoutComponent {}
@@ -0,0 +1,9 @@
1
+ import { ChangeDetectionStrategy, Component } from '@angular/core';
2
+
3
+ @Component({
4
+ selector: 'Frame, frame',
5
+ standalone: true,
6
+ changeDetection: ChangeDetectionStrategy.OnPush,
7
+ template: `<div class="ns-frame"><ng-content></ng-content></div>`,
8
+ })
9
+ export class FrameComponent {}
@@ -0,0 +1,48 @@
1
+ import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
2
+
3
+ @Component({
4
+ selector: 'GridLayout, gridlayout',
5
+ standalone: true,
6
+ changeDetection: ChangeDetectionStrategy.OnPush,
7
+ template: `<div class="ns-grid" [ngStyle]="gridStyle"><ng-content></ng-content></div>`,
8
+ styles: [
9
+ `
10
+ .ns-grid {
11
+ padding: 0px;
12
+ width: 100%;
13
+ height: 100%;
14
+ box-sizing: border-box;
15
+ min-width: 0;
16
+ min-height: 0;
17
+ }
18
+ `,
19
+ ],
20
+ })
21
+ export class GridLayoutComponent {
22
+ @Input() rows?: string;
23
+ @Input() columns?: string;
24
+
25
+ get gridStyle(): Record<string, string> {
26
+ const style: Record<string, string> = { display: 'grid' };
27
+ const rows = this.parseSegments(this.rows);
28
+ const cols = this.parseSegments(this.columns);
29
+ if (rows) style.gridTemplateRows = rows;
30
+ if (cols) style.gridTemplateColumns = cols;
31
+ return style;
32
+ }
33
+
34
+ private parseSegments(input?: string): string | undefined {
35
+ if (!input) return undefined;
36
+ const segToCss = (s: string) => {
37
+ const t = s.trim();
38
+ if (!t) return 'auto';
39
+ if (t === '*') return '1fr';
40
+ const star = t.match(/^(\d+)\*$/);
41
+ if (star) return `${star[1]}fr`;
42
+ if (t.toLowerCase() === 'auto') return 'auto';
43
+ if (/^\d+$/.test(t)) return `${t}px`;
44
+ return t;
45
+ };
46
+ return input.split(',').map(segToCss).join(' ');
47
+ }
48
+ }
@@ -0,0 +1,23 @@
1
+ import { CommonModule } from '@angular/common';
2
+ import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
3
+
4
+ @Component({
5
+ selector: 'HtmlView, htmlview',
6
+ standalone: true,
7
+ changeDetection: ChangeDetectionStrategy.OnPush,
8
+ imports: [CommonModule],
9
+ template: `
10
+ <div class="ns-htmlview" *ngIf="html; else projected" [innerHTML]="html"></div>
11
+ <ng-template #projected><div class="ns-htmlview"><ng-content></ng-content></div></ng-template>
12
+ `,
13
+ styles: [
14
+ `
15
+ .ns-htmlview {
16
+ display: block;
17
+ }
18
+ `,
19
+ ],
20
+ })
21
+ export class HtmlViewComponent {
22
+ @Input() html?: string;
23
+ }
@@ -0,0 +1,21 @@
1
+ import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
2
+
3
+ @Component({
4
+ selector: 'ImageCacheIt, imagecacheit',
5
+ standalone: true,
6
+ changeDetection: ChangeDetectionStrategy.OnPush,
7
+ template: `<img class="ns-imagecacheit" [src]="src" />`,
8
+ styles: [
9
+ `
10
+ .ns-imagecacheit {
11
+ display: block;
12
+ max-width: 100%;
13
+ height: auto;
14
+ }
15
+ `,
16
+ ],
17
+ })
18
+ export class ImageCacheItComponent {
19
+ @Input({ required: true }) src!: string;
20
+ }
21
+
@@ -0,0 +1,22 @@
1
+ import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
2
+
3
+ @Component({
4
+ selector: 'Image, image',
5
+ standalone: true,
6
+ changeDetection: ChangeDetectionStrategy.OnPush,
7
+ template: `<img class="ns-image" [src]="src" />`,
8
+ styles: [
9
+ `
10
+ .ns-image {
11
+ display: block;
12
+ max-width: 100%;
13
+ height: auto;
14
+ }
15
+ `,
16
+ ],
17
+ })
18
+ export class ImageComponent {
19
+ @Input({ required: true }) src!: string;
20
+ @Input() stretch?: string;
21
+ }
22
+