diff --git a/package-lock.json b/package-lock.json index 0941c1390..4a98aa5af 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12367,7 +12367,6 @@ "version": "4.12.3", "resolved": "https://registry.npmjs.org/@stencil/core/-/core-4.12.3.tgz", "integrity": "sha512-9XkE9i2aXPlApMNeq3tbVHKx0eAfDc7QGyIl6t5NMuQFTOGL5Xd1soF38d+hCIDpUoUUtY7jXWg+iFrlrMzQhg==", - "dev": true, "bin": { "stencil": "bin/stencil" }, @@ -17797,9 +17796,9 @@ "optional": true }, "node_modules/bare-fs": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-2.1.5.tgz", - "integrity": "sha512-5t0nlecX+N2uJqdxe9d18A98cp2u9BETelbjKpiVgQqzzmVNFYWEAjQHqS+2Khgto1vcwhik9cXucaj5ve2WWA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-2.2.0.tgz", + "integrity": "sha512-+VhW202E9eTVGkX7p+TNXtZC4RTzj9JfJW7PtfIbZ7mIQ/QT9uOafQTx7lx2n9ERmWsXvLHF4hStAFn4gl2mQw==", "dev": true, "optional": true, "dependencies": { @@ -42723,12 +42722,13 @@ "version": "1.0.1", "license": "Apache-2.0", "dependencies": { - "@beeq/core": "*", + "@beeq/core": "^1.0.1", "tslib": "^2.6.2" }, "peerDependencies": { "@angular/common": ">=14.0.0", - "@angular/core": ">=14.0.0" + "@angular/core": ">=14.0.0", + "@stencil/core": ">=4.0.0" } }, "packages/beeq-react": { @@ -42736,7 +42736,7 @@ "version": "1.0.1", "license": "Apache-2.0", "dependencies": { - "@beeq/core": "*" + "@beeq/core": "^1.0.1" }, "peerDependencies": { "react": ">=18.0.0", @@ -42758,7 +42758,7 @@ "version": "1.0.1", "license": "Apache-2.0", "dependencies": { - "@beeq/core": "*", + "@beeq/core": "^1.0.1", "tslib": "^2.3.0" }, "peerDependencies": { diff --git a/packages/beeq-angular/README.md b/packages/beeq-angular/README.md index c845bdae5..629854ef1 100644 --- a/packages/beeq-angular/README.md +++ b/packages/beeq-angular/README.md @@ -118,15 +118,11 @@ To enable two-way binding and the use of [ngModel] within BEEQ form components, import { NgModule } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { BrowserModule } from '@angular/platform-browser'; -import { BeeQModule, BooleanValueAccessor, TextValueAccessor } from '@beeq/angular'; import { AppComponent } from './app.component'; -/** 💡 More Value Accessors will be exported later and should be included as well */ -const VALUE_ACCESSORS = [BooleanValueAccessor, TextValueAccessor]; - @NgModule({ - declarations: [AppComponent, ...VALUE_ACCESSORS], + declarations: [AppComponent], imports: [BeeQModule.forRoot(), BrowserModule, FormsModule], providers: [], bootstrap: [AppComponent], @@ -135,6 +131,22 @@ const VALUE_ACCESSORS = [BooleanValueAccessor, TextValueAccessor]; export class AppModule {} ``` +> 🙋🏼‍♂️ If you are using `@beeq/angular` v1.0.1 or below, **you also need to import the values accessors**, as shown below: + +```ts +... +import { BeeQModule, BooleanValueAccessor, TextValueAccessor } from '@beeq/angular'; +... +const VALUE_ACCESSORS = [BooleanValueAccessor, TextValueAccessor]; + +@NgModule({ + declarations: [AppComponent, ...VALUE_ACCESSORS], + imports: [BeeQModule.forRoot(), BrowserModule, FormsModule], + ... +}) +export class AppModule {} +``` + ### Usage ```html @@ -176,3 +188,36 @@ export class AppComponent { } } ``` + +### Using BEEQ components in Angular standalone + +You can also use BEEQ components in Angular standalone. To do so, you will need to import the components from `@beeq/angular/standalone` and use them as you would use any other Angular component. + +```ts +import { Component } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { BqButton, BqCard, BqInput } from '@beeq/angular/standalone'; + +@Component({ + selector: 'app-component', + standalone: true, + imports: [BqButton, BqCard, BqInput], + template: ` + + + + + Subscribe me! + + `, + styles: [], + schemas: [], +}) +export class AppComponent2 { + emailValue = 'BEEQ Design System'; + + onInputChange(event: CustomEvent<{ value: string }>) { + console.log('emailValue', event.detail.value); + } +} +``` diff --git a/packages/beeq-angular/package.json b/packages/beeq-angular/package.json index 11ecaea57..07e787ce3 100644 --- a/packages/beeq-angular/package.json +++ b/packages/beeq-angular/package.json @@ -7,7 +7,7 @@ "module": "dist/esm2015/index.js", "types": "index.d.ts", "dependencies": { - "@beeq/core": "*", + "@beeq/core": "^1.0.1", "tslib": "^2.6.2" }, "peerDependencies": { diff --git a/packages/beeq-angular/project.json b/packages/beeq-angular/project.json index 4c0149246..583247900 100644 --- a/packages/beeq-angular/project.json +++ b/packages/beeq-angular/project.json @@ -11,14 +11,14 @@ "executor": "@nx/angular:package", "outputs": ["{workspaceRoot}/dist/beeq-angular"], "options": { - "project": "packages/beeq-angular/ng-package.json" + "project": "{projectRoot}/ng-package.json" }, "configurations": { "production": { - "tsConfig": "packages/beeq-angular/tsconfig.lib.prod.json" + "tsConfig": "{projectRoot}/tsconfig.lib.prod.json" }, "development": { - "tsConfig": "packages/beeq-angular/tsconfig.lib.json" + "tsConfig": "{projectRoot}/tsconfig.lib.json" } }, "defaultConfiguration": "production" diff --git a/packages/beeq-angular/src/beeq.module.ts b/packages/beeq-angular/src/beeq.module.ts index 3606d6936..2305e0b71 100644 --- a/packages/beeq-angular/src/beeq.module.ts +++ b/packages/beeq-angular/src/beeq.module.ts @@ -1,19 +1,41 @@ -import { CommonModule } from '@angular/common'; -import { ModuleWithProviders, NgModule } from '@angular/core'; +import { CommonModule, DOCUMENT } from '@angular/common'; +import { APP_INITIALIZER, ModuleWithProviders, NgModule, NgZone } from '@angular/core'; import { defineCustomElements } from '@beeq/core/dist/loader'; import { DIRECTIVES } from './directives'; +import { BooleanValueAccessor } from './directives/boolean-value-accessor'; +import { NumericValueAccessor } from './directives/number-value-accessor'; +import { RadioValueAccessor } from './directives/radio-value-accessor'; +import { SelectValueAccessor } from './directives/select-value-accessor'; +import { TextValueAccessor } from './directives/text-value-accessor'; + +const DECLARATIONS = [ + ...DIRECTIVES, + // ngModel Accessors + BooleanValueAccessor, + NumericValueAccessor, + RadioValueAccessor, + SelectValueAccessor, + TextValueAccessor, +]; @NgModule({ imports: [CommonModule], - declarations: [...DIRECTIVES], - exports: [...DIRECTIVES], + declarations: DECLARATIONS, + exports: DECLARATIONS, }) export class BeeQModule { static forRoot(): ModuleWithProviders { - defineCustomElements(); return { ngModule: BeeQModule, + providers: [ + { + provide: APP_INITIALIZER, + useFactory: () => defineCustomElements, + multi: true, + deps: [DOCUMENT, NgZone], + }, + ], }; } } diff --git a/packages/beeq-angular/src/index.ts b/packages/beeq-angular/src/index.ts index de4b639e4..5beb5c85a 100644 --- a/packages/beeq-angular/src/index.ts +++ b/packages/beeq-angular/src/index.ts @@ -2,6 +2,9 @@ /* DIRECTIVES */ /* -------------------------------------------------------------------------- */ export { BooleanValueAccessor } from './directives/boolean-value-accessor'; +export { NumericValueAccessor } from './directives/number-value-accessor'; +export { RadioValueAccessor } from './directives/radio-value-accessor'; +export { SelectValueAccessor } from './directives/select-value-accessor'; export { TextValueAccessor } from './directives/text-value-accessor'; export * from './directives/components'; diff --git a/packages/beeq-angular/standalone/ng-package.json b/packages/beeq-angular/standalone/ng-package.json new file mode 100644 index 000000000..c781f0df4 --- /dev/null +++ b/packages/beeq-angular/standalone/ng-package.json @@ -0,0 +1,5 @@ +{ + "lib": { + "entryFile": "src/index.ts" + } +} diff --git a/packages/beeq-angular/standalone/src/index.ts b/packages/beeq-angular/standalone/src/index.ts new file mode 100644 index 000000000..a92280940 --- /dev/null +++ b/packages/beeq-angular/standalone/src/index.ts @@ -0,0 +1,5 @@ +/* -------------------------------------------------------------------------- */ +/* DIRECTIVES */ +/* -------------------------------------------------------------------------- */ +// @ts-ignore +export * from './directives/components'; diff --git a/packages/beeq-angular/tsconfig.lib.json b/packages/beeq-angular/tsconfig.lib.json index 719ef60dc..c5acfc4a1 100644 --- a/packages/beeq-angular/tsconfig.lib.json +++ b/packages/beeq-angular/tsconfig.lib.json @@ -8,5 +8,5 @@ "types": [] }, "exclude": ["src/test-setup.ts", "**/*.spec.ts", "jest.config.ts", "**/*.test.ts"], - "include": ["**/*.ts"] + "include": ["src/index.ts", "standalone/src/index.ts"] } diff --git a/packages/beeq-angular/tsconfig.lib.prod.json b/packages/beeq-angular/tsconfig.lib.prod.json index 2dac8b1d1..6affae4f9 100644 --- a/packages/beeq-angular/tsconfig.lib.prod.json +++ b/packages/beeq-angular/tsconfig.lib.prod.json @@ -1,7 +1,8 @@ { "extends": "./tsconfig.lib.json", "compilerOptions": { - "declarationMap": false + "declarationMap": false, + "removeComments": false }, "angularCompilerOptions": { "annotateForClosureCompiler": true, diff --git a/packages/beeq-react/package.json b/packages/beeq-react/package.json index 6e63f6d41..2d7a4213d 100644 --- a/packages/beeq-react/package.json +++ b/packages/beeq-react/package.json @@ -7,7 +7,7 @@ "module": "./src/index.js", "types": "./src/index.d.ts", "dependencies": { - "@beeq/core": "*" + "@beeq/core": "^1.0.1" }, "peerDependencies": { "react": ">=18.0.0", diff --git a/packages/beeq-react/tsconfig.json b/packages/beeq-react/tsconfig.json index 61eb6796d..e2084856c 100644 --- a/packages/beeq-react/tsconfig.json +++ b/packages/beeq-react/tsconfig.json @@ -5,7 +5,8 @@ "allowJs": false, "esModuleInterop": false, "allowSyntheticDefaultImports": true, - "declaration": true + "declaration": true, + "removeComments": false }, "files": [], "include": [], diff --git a/packages/beeq-vue/package.json b/packages/beeq-vue/package.json index 04cddf8aa..46a2e578c 100644 --- a/packages/beeq-vue/package.json +++ b/packages/beeq-vue/package.json @@ -7,7 +7,7 @@ "module": "./src/index.js", "types": "./src/index.d.ts", "dependencies": { - "@beeq/core": "*", + "@beeq/core": "^1.0.1", "tslib": "^2.3.0" }, "peerDependencies": { diff --git a/packages/beeq-vue/tsconfig.lib.json b/packages/beeq-vue/tsconfig.lib.json index 19b4921cd..04cb4d8e4 100644 --- a/packages/beeq-vue/tsconfig.lib.json +++ b/packages/beeq-vue/tsconfig.lib.json @@ -2,6 +2,7 @@ "extends": "./tsconfig.json", "compilerOptions": { "outDir": "../../dist/out-tsc", + "removeComments": false, "types": ["node"] }, "include": ["src/**/*.ts"], diff --git a/packages/beeq/project.json b/packages/beeq/project.json index f6541f0d4..5816e3fa0 100644 --- a/packages/beeq/project.json +++ b/packages/beeq/project.json @@ -76,6 +76,7 @@ "{options.outputPath}", "{projectRoot}/custom-elements.json", "{workspaceRoot}/packages/beeq-angular/src/directives", + "{workspaceRoot}/packages/beeq-angular/standalone/src/directives", "{workspaceRoot}/packages/beeq-react/src/react-component-lib", "{workspaceRoot}/packages/beeq-vue/src/components.ts" ], diff --git a/packages/beeq/src/components/input/bq-input.tsx b/packages/beeq/src/components/input/bq-input.tsx index d29018cd6..dd4fd7d1b 100644 --- a/packages/beeq/src/components/input/bq-input.tsx +++ b/packages/beeq/src/components/input/bq-input.tsx @@ -246,7 +246,7 @@ export class BqInput { this.debounceBqInput?.cancel(); if (!isHTMLElement(ev.target, 'input')) return; - this.value = ev.target.value; + this.value = this.type === 'number' ? Number(ev.target.value) : ev.target.value; this.debounceBqInput = debounce(() => { this.bqInput.emit({ value: this.value, el: this.el }); @@ -258,7 +258,7 @@ export class BqInput { if (this.disabled) return; if (!isHTMLElement(ev.target, 'input')) return; - this.value = ev.target.value; + this.value = this.type === 'number' ? Number(ev.target.value) : ev.target.value; this.bqChange.emit({ value: this.value, el: this.el }); }; diff --git a/packages/beeq/src/tools/angular-value-accessor-config.ts b/packages/beeq/src/tools/angular-value-accessor-config.ts index 1b25272b3..1d52b9398 100644 --- a/packages/beeq/src/tools/angular-value-accessor-config.ts +++ b/packages/beeq/src/tools/angular-value-accessor-config.ts @@ -1,14 +1,41 @@ import { ValueAccessorConfig } from '@stencil/angular-output-target'; +/** + * This lets you define which components should be integrated with ngModel (i.e. form components). + * It lets you set what the target prop is (i.e. value), which event will cause the target prop to change. + */ export const angularValueAccessorBindings: ValueAccessorConfig[] = [ { + // Boolean elementSelectors: ['bq-checkbox', 'bq-switch'], event: 'bqChange', targetAttr: 'checked', type: 'boolean', }, { - elementSelectors: ['bq-input', 'bq-radio-group', 'bq-select', 'bq-slider', 'bq-textarea'], + // Number + elementSelectors: ['bq-input[type="number"]', 'bq-slider'], + event: 'bqChange', + targetAttr: 'value', + type: 'number', + }, + { + // Radio + elementSelectors: ['bq-radio-group'], + event: 'bqChange', + targetAttr: 'value', + type: 'radio', + }, + { + // Select + elementSelectors: ['bq-select'], + event: 'bqChange', + targetAttr: 'value', + type: 'select', + }, + { + // Text + elementSelectors: ['bq-input:not[type="number"]', 'bq-textarea'], event: 'bqChange', targetAttr: 'value', type: 'text', diff --git a/packages/beeq/stencil.config.ts b/packages/beeq/stencil.config.ts index 9d8b6f25f..b4b6900b9 100644 --- a/packages/beeq/stencil.config.ts +++ b/packages/beeq/stencil.config.ts @@ -16,10 +16,12 @@ const tailwindOpts: PluginConfigOpts = { stripComments: true, }; -const componentCorePackage = '@beeq/core'; +const namespace = 'beeq'; +const componentCorePackage = `@${namespace}/core`; +const customElementsDir = 'dist/components'; export const config: Config = { - namespace: 'beeq', + namespace, taskQueue: 'async', buildDist: true, enableCache: true, @@ -50,7 +52,8 @@ export const config: Config = { { type: 'dist-hydrate-script', dir: 'dist/hydrate' }, { type: 'dist-custom-elements', - customElementsExportBehavior: 'auto-define-custom-elements', + customElementsExportBehavior: 'single-export-module', + dir: customElementsDir, minify: true, }, { @@ -63,19 +66,35 @@ export const config: Config = { }, angular({ componentCorePackage, + outputType: 'component', // Generate many component wrappers tied to a single Angular module (lazy/hydrated approach) directivesProxyFile: resolve(__dirname, '../beeq-angular/src/directives/components.ts').replace(/\\/g, '/'), directivesArrayFile: resolve(__dirname, '../beeq-angular/src/directives/index.ts').replace(/\\/g, '/'), valueAccessorConfigs: angularValueAccessorBindings, + customElementsDir, + }), + angular({ + componentCorePackage, + outputType: 'standalone', // Generate a component with the standalone flag set to true. + directivesProxyFile: resolve(__dirname, '../beeq-angular/standalone/src/directives/components.ts').replace( + /\\/g, + '/', + ), + directivesArrayFile: resolve(__dirname, '../beeq-angular/standalone/src/directives/index.ts').replace(/\\/g, '/'), + valueAccessorConfigs: angularValueAccessorBindings, + customElementsDir, }), react({ componentCorePackage, proxiesFile: resolve(__dirname, '../beeq-react/src/components.ts').replace(/\\/g, '/'), - includeDefineCustomElements: true, + includeImportCustomElements: true, + customElementsDir, }), vue({ componentCorePackage, proxiesFile: resolve(__dirname, '../beeq-vue/src/components.ts').replace(/\\/g, '/'), componentModels: vueComponentModels, + includeImportCustomElements: true, + customElementsDir, }), ], extras: { diff --git a/packages/beeq/tsconfig.lib.json b/packages/beeq/tsconfig.lib.json index 4c555c60f..0204e1d38 100644 --- a/packages/beeq/tsconfig.lib.json +++ b/packages/beeq/tsconfig.lib.json @@ -3,7 +3,8 @@ "compilerOptions": { "outDir": "../../dist/out-tsc", "types": ["node"], - "composite": true + "composite": true, + "removeComments": false }, "exclude": [ "**/*.spec.ts",