Skip to content

Stencil main with tag name transformer #12

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: stencil-main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .prettierrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"plugins": ["@ionic/prettier-config"]
}
30 changes: 30 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,33 @@
## @public-ui/[email protected] (2024-03-11)

#### :bug: Bug Fix

- fix(vue): v-model does not update when other events bubble up [#425](https://github.com/ionic-team/stencil-ds-output-targets/pull/425)

## @public-ui/[email protected] (2024-03-11)

#### :rocket: Enhancement

- Add runtime option `setTagNameTransformer`

## @public-ui/[email protected] (2024-03-11)

#### :rocket: Enhancement

- Add runtime option `setTagNameTransformer`

## @public-ui/[email protected] (2024-03-11)

#### :rocket: Enhancement

- Add runtime option `setTagNameTransformer`

## @public-ui/[email protected] (2024-03-11)

#### :rocket: Enhancement

- Add runtime option `setTagNameTransformer`

## @stencil/[email protected] (2020-06-26)

#### :rocket: Enhancement
Expand Down
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@
| Angular Output Target | [`@stencil/angular-output-target`](https://www.npmjs.com/package/@stencil/angular-output-target) | [![version](https://img.shields.io/npm/v/@stencil/angular-output-target/latest.svg)](https://www.npmjs.com/package/@stencil/angular-output-target) | [README](./packages/angular/README.md) |
| Vue Output Target | [`@stencil/vue-output-target`](https://www.npmjs.com/package/@stencil/vue-output-target) | [![version](https://img.shields.io/npm/v/@stencil/vue-output-target/latest.svg)](https://www.npmjs.com/package/@stencil/vue-output-target) | [README](./packages/vue/README.md) |

| Project | Package | Version | Documentation |
| --------------------- | ------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------- |
| React Output Target | [`@public-ui/stencil-react-output-target`](https://www.npmjs.com/package/@public-ui/stencil-react-output-target) | [![version](https://img.shields.io/npm/v/@public-ui/stencil-react-output-target/latest.svg)](https://www.npmjs.com/package/@public-ui/stencil-react-output-target) | [README](./packages/react-output-target/README.md) |
| Angular Output Target | [`@public-ui/stencil-angular-output-target`](https://www.npmjs.com/package/@public-ui/stencil-angular-output-target) | [![version](https://img.shields.io/npm/v/@public-ui/stencil-angular-output-target/latest.svg)](https://www.npmjs.com/package/@public-ui/stencil-angular-output-target) | [README](./packages/angular-output-target/README.md) |
| Vue Output Target | [`@public-ui/stencil-vue-output-target`](https://www.npmjs.com/package/@public-ui/stencil-vue-output-target) | [![version](https://img.shields.io/npm/v/@public-ui/stencil-vue-output-target/latest.svg)](https://www.npmjs.com/package/@public-ui/stencil-vue-output-target) | [README](./packages/vue-output-target/README.md) |
| Solid Output Target | [`@public-ui/stencil-solid-output-target`](https://www.npmjs.com/package/@public-ui/stencil-solid-output-target) | [![version](https://img.shields.io/npm/v/@public-ui/stencil-solid-output-target/latest.svg)](https://www.npmjs.com/package/@public-ui/stencil-solid-output-target) | [README](./packages/solid-output-target/README.md) |

# Introduction

Integrating web components into existing framework applications can be difficult at times. More about this can be read at https://custom-elements-everywhere.com/.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { AfterViewChecked, Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';
import { StencilProxyComponent } from './stencil-proxy-component';

@Directive({
selector: '[replaceTag]',
})
export class ReplaceTagDirective implements AfterViewChecked {
constructor(
private templateRef: TemplateRef<any>,
private vcf: ViewContainerRef,
private host: StencilProxyComponent
) {}

private _tag: string;
private _needUpdate: boolean = false;
private _webComponentElement: HTMLElement;

@Input('replaceTag')
set tag(t: string) {
this._tag = t;
this._needUpdate = true;
this.vcf.clear();
let template = this.templateRef.elementRef.nativeElement.previousElementSibling;
if (template) {
this.templateRef.elementRef.nativeElement.parentNode.removeChild(template);
}
this.vcf.createEmbeddedView(this.templateRef);
}

ngAfterViewChecked() {
if (this._needUpdate) {
this._updateTemplate();
this._needUpdate = false;
}
}

private _updateTemplate() {
let template = this.templateRef.elementRef.nativeElement.previousElementSibling;
if (template) {
this._webComponentElement = document.createElement(this._tag);
this.mirrorProperties(
this.templateRef.elementRef.nativeElement.previousElementSibling,
this._webComponentElement
);

Array.from(template.childNodes).forEach((node) => {
this._webComponentElement.appendChild(node as Node);
});

this.templateRef.elementRef.nativeElement.parentNode.replaceChild(this._webComponentElement, template);
// template.style.display = 'none';
// this.templateRef.elementRef.nativeElement.parentNode.appendChild(this._webComponentElement, template);
}
}

public handlePropertyChanges() {
this.mirrorProperties(this.templateRef.elementRef.nativeElement.parentElement, this._webComponentElement);
}

private mirrorProperties(from: HTMLElement, to: HTMLElement) {
const properties: [string, any][] = Object.entries(from);

for (let [key, value] of properties) {
if (this.host.availableInputProperties.includes(key) && value !== undefined) {
(to as Record<string, any>)[key] = value;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export abstract class StencilProxyComponent {
public availableInputProperties: string[];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
type TagNameTransformer = (tagName: string) => string;

export let tagNameTransformer: TagNameTransformer | undefined;
export const setTagNameTransformer = (_tagNameTransformer: TagNameTransformer) => {
tagNameTransformer = _tagNameTransformer;
};
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
/* tslint:disable */
/* auto-generated angular directive proxies */
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, EventEmitter, NgZone } from '@angular/core';

import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, OnChanges, ViewChild, forwardRef, CUSTOM_ELEMENTS_SCHEMA, EventEmitter, NgZone } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ProxyCmp, proxyOutputs } from './angular-component-lib/utils';
import { ReplaceTagDirective } from './angular-component-lib/replace-tag-directive';
import { StencilProxyComponent } from './angular-component-lib/stencil-proxy-component';
import { tagNameTransformer } from './angular-component-lib/tag-name-transformer';

import { Components } from 'component-library';

Expand Down Expand Up @@ -59,7 +62,7 @@ export class MyCheckbox {
}


import type { CheckboxChangeEventDetail as IMyCheckboxCheckboxChangeEventDetail } from 'component-library';
import /* import type - is not possible with ng@^8 */ { CheckboxChangeEventDetail as IMyCheckboxCheckboxChangeEventDetail } from 'component-library';

export declare interface MyCheckbox extends Components.MyCheckbox {
/**
Expand Down Expand Up @@ -126,7 +129,7 @@ export class MyInput {
}


import type { InputChangeEventDetail as IMyInputInputChangeEventDetail } from 'component-library';
import /* import type - is not possible with ng@^8 */ { InputChangeEventDetail as IMyInputInputChangeEventDetail } from 'component-library';

export declare interface MyInput extends Components.MyInput {
/**
Expand Down Expand Up @@ -169,7 +172,7 @@ export class MyPopover {
}


import type { OverlayEventDetail as IMyPopoverOverlayEventDetail } from 'component-library';
import /* import type - is not possible with ng@^8 */ { OverlayEventDetail as IMyPopoverOverlayEventDetail } from 'component-library';

export declare interface MyPopover extends Components.MyPopover {
/**
Expand Down Expand Up @@ -247,7 +250,7 @@ export class MyRadioGroup {
}


import type { RadioGroupChangeEventDetail as IMyRadioGroupRadioGroupChangeEventDetail } from 'component-library';
import /* import type - is not possible with ng@^8 */ { RadioGroupChangeEventDetail as IMyRadioGroupRadioGroupChangeEventDetail } from 'component-library';

export declare interface MyRadioGroup extends Components.MyRadioGroup {
/**
Expand Down Expand Up @@ -277,7 +280,7 @@ export class MyRange {
}


import type { RangeChangeEventDetail as IMyRangeRangeChangeEventDetail } from 'component-library';
import /* import type - is not possible with ng@^8 */ { RangeChangeEventDetail as IMyRangeRangeChangeEventDetail } from 'component-library';

export declare interface MyRange extends Components.MyRange {
/**
Expand Down
2 changes: 1 addition & 1 deletion example-project/component-library-react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"test": "wdio run ./wdio.conf.ts"
},
"dependencies": {
"@stencil/react-output-target": "workspace:*",
"@public-ui/stencil-react-output-target": "workspace:*",
"component-library": "workspace:*",
"react-dom": "^18.3.1"
},
Expand Down
4 changes: 2 additions & 2 deletions example-project/component-library-react/src/components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@

/* eslint-disable */

import type { EventName, StencilReactComponent } from '@stencil/react-output-target/runtime';
import { createComponent } from '@stencil/react-output-target/runtime';
import type { EventName, StencilReactComponent } from '@public-ui/stencil-react-output-target/runtime';
import { createComponent } from '@public-ui/stencil-react-output-target/runtime';
import { type CheckboxChangeEventDetail, type InputChangeEventDetail, type MyCheckboxCustomEvent, type MyInputCustomEvent, type MyPopoverCustomEvent, type MyRadioGroupCustomEvent, type MyRangeCustomEvent, type OverlayEventDetail, type RadioGroupChangeEventDetail, type RangeChangeEventDetail } from "component-library";
import { MyButton as MyButtonElement, defineCustomElement as defineMyButton } from "component-library/components/my-button.js";
import { MyCheckbox as MyCheckboxElement, defineCustomElement as defineMyCheckbox } from "component-library/components/my-checkbox.js";
Expand Down
2 changes: 1 addition & 1 deletion example-project/component-library-vue/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,6 @@
},
"dependencies": {
"component-library": "workspace:*",
"@stencil/vue-output-target": "workspace:*"
"@public-ui/stencil-vue-output-target": "workspace:*"
}
}
4 changes: 3 additions & 1 deletion example-project/component-library-vue/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
/* eslint-disable */
/* tslint:disable */
/* auto-generated vue proxies */
import { defineContainer, defineStencilSSRComponent } from '@stencil/vue-output-target/runtime';
import { defineContainer, defineStencilSSRComponent } from '@public-ui/stencil-vue-output-target/runtime';
import { setTagNameTransformer } from './tag-name-transformer';

import type { JSX } from 'component-library/components';

Expand Down Expand Up @@ -290,3 +291,4 @@ export const MyRange = /*@__PURE__*/ globalThis.window ? defineContainer<JSX.MyR
}
});

export { setTagNameTransformer };
6 changes: 3 additions & 3 deletions example-project/component-library/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,10 @@
"prettier.dry-run": "pnpm run prettier.base --list-different"
},
"devDependencies": {
"@stencil/angular-output-target": "workspace:*",
"@public-ui/stencil-angular-output-target": "workspace:*",
"@stencil/core": "^4.22.1",
"@stencil/react-output-target": "workspace:*",
"@stencil/vue-output-target": "workspace:*",
"@public-ui/stencil-react-output-target": "workspace:*",
"@public-ui/stencil-vue-output-target": "workspace:*",
"@types/puppeteer": "2.0.1",
"jest-cli": "26.0.1"
}
Expand Down
6 changes: 3 additions & 3 deletions example-project/component-library/stencil.config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Config } from '@stencil/core';
import { angularOutputTarget, ValueAccessorConfig } from '@stencil/angular-output-target';
import { reactOutputTarget } from '@stencil/react-output-target';
import { vueOutputTarget, ComponentModelConfig } from '@stencil/vue-output-target';
import { angularOutputTarget, ValueAccessorConfig } from '@public-ui/stencil-angular-output-target';
import { reactOutputTarget } from '@public-ui/stencil-react-output-target';
import { vueOutputTarget, ComponentModelConfig } from '@public-ui/stencil-vue-output-target';


const angularValueAccessorBindings: ValueAccessorConfig[] = [
Expand Down
2 changes: 1 addition & 1 deletion example-project/next-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"react-dom": "^18"
},
"devDependencies": {
"@stencil/react-output-target": "workspace:*",
"@public-ui/stencil-react-output-target": "workspace:*",
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
Expand Down
4 changes: 2 additions & 2 deletions example-project/next-app/src/app/components.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@

/* eslint-disable */

import type { EventName, StencilReactComponent } from '@stencil/react-output-target/runtime';
import { createComponent, createSSRComponent } from '@stencil/react-output-target/runtime';
import type { EventName, StencilReactComponent } from '@public-ui/stencil-react-output-target/runtime';
import { createComponent, createSSRComponent } from '@public-ui/stencil-react-output-target/runtime';
import { type CheckboxChangeEventDetail, type InputChangeEventDetail, type MyCheckboxCustomEvent, type MyInputCustomEvent, type MyPopoverCustomEvent, type MyRadioGroupCustomEvent, type MyRangeCustomEvent, type OverlayEventDetail, type RadioGroupChangeEventDetail, type RangeChangeEventDetail } from "component-library";
import { MyButton as MyButtonElement, defineCustomElement as defineMyButton } from "component-library/components/my-button.js";
import { MyCheckbox as MyCheckboxElement, defineCustomElement as defineMyCheckbox } from "component-library/components/my-checkbox.js";
Expand Down
10 changes: 10 additions & 0 deletions lerna.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
{
"packages": [
"packages/angular-output-target",
"packages/react-output-target",
"packages/solid-output-target",
"packages/vue-output-target",
"packages/example-project/component-library",
"packages/example-project/component-library-angular",
"packages/example-project/component-library-react",
"packages/example-project/component-library-vue"
],
"version": "independent",
"command": {
"publish": {
Expand Down
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@
"semver": "^5.5.0",
"ts-jest": "^26.2.0"
},
"prettier": "@ionic/prettier-config",
"engines": {
"node": ">=20.10.0",
"pnpm": ">=9.4.0"
Expand Down
69 changes: 69 additions & 0 deletions packages/angular/angular-component-lib/replace-tag-directive.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { AfterViewChecked, Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';
import { StencilProxyComponent } from './stencil-proxy-component';

@Directive({
selector: '[replaceTag]',
})
export class ReplaceTagDirective implements AfterViewChecked {
constructor(
private templateRef: TemplateRef<any>,
private vcf: ViewContainerRef,
private host: StencilProxyComponent
) {}

private _tag: string;
private _needUpdate: boolean = false;
private _webComponentElement: HTMLElement;

@Input('replaceTag')
set tag(t: string) {
this._tag = t;
this._needUpdate = true;
this.vcf.clear();
let template = this.templateRef.elementRef.nativeElement.previousElementSibling;
if (template) {
this.templateRef.elementRef.nativeElement.parentNode.removeChild(template);
}
this.vcf.createEmbeddedView(this.templateRef);
}

ngAfterViewChecked() {
if (this._needUpdate) {
this._updateTemplate();
this._needUpdate = false;
}
}

private _updateTemplate() {
let template = this.templateRef.elementRef.nativeElement.previousElementSibling;
if (template) {
this._webComponentElement = document.createElement(this._tag);
this.mirrorProperties(
this.templateRef.elementRef.nativeElement.previousElementSibling,
this._webComponentElement
);

Array.from(template.childNodes).forEach((node) => {
this._webComponentElement.appendChild(node as Node);
});

this.templateRef.elementRef.nativeElement.parentNode.replaceChild(this._webComponentElement, template);
// template.style.display = 'none';
// this.templateRef.elementRef.nativeElement.parentNode.appendChild(this._webComponentElement, template);
}
}

public handlePropertyChanges() {
this.mirrorProperties(this.templateRef.elementRef.nativeElement.parentElement, this._webComponentElement);
}

private mirrorProperties(from: HTMLElement, to: HTMLElement) {
const properties: [string, any][] = Object.entries(from);

for (let [key, value] of properties) {
if (this.host.availableInputProperties.includes(key) && value !== undefined) {
(to as Record<string, any>)[key] = value;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export abstract class StencilProxyComponent {
public availableInputProperties: string[];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
type TagNameTransformer = (tagName: string) => string;

export let tagNameTransformer: TagNameTransformer | undefined;
export const setTagNameTransformer = (_tagNameTransformer: TagNameTransformer) => {
tagNameTransformer = _tagNameTransformer;
};
4 changes: 2 additions & 2 deletions packages/angular/package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "@stencil/angular-output-target",
"name": "@public-ui/stencil-angular-output-target",
"version": "0.10.0",
"description": "Angular output target for @stencil/core components.",
"main": "dist/index.cjs.js",
Expand All @@ -15,7 +15,7 @@
},
"scripts": {
"prepublishOnly": "pnpm run build",
"prebuild": "rimraf ./dist && pnpm run test",
"prebuild": "rimraf ./dist",
"build": "tsc && pnpm run rollup",
"dev": "run-p dev:*",
"dev:build": "tsc --watch --preserveWatchOutput",
Expand Down
Loading