diff --git a/.eslintignore b/.eslintignore index 5e6019cab8..cec0ddf16e 100644 --- a/.eslintignore +++ b/.eslintignore @@ -38,3 +38,5 @@ node_modules/ # yarn v2 .yarn + +**/src/index.html diff --git a/.eslintrc.js b/.eslintrc.js index 4817296b7b..f766eb93d5 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -20,7 +20,6 @@ module.exports = { ], rules: { 'prettier/prettier': ['error', prettierConfig], - 'jsdoc/newline-after-description': 1, '@angular-eslint/component-selector': [ 'off', { @@ -171,8 +170,7 @@ module.exports = { '@angular-eslint/no-input-rename': 'off', 'prefer-const': 'off', 'max-len': 'off', - 'deprecation/deprecation': 'warn', - 'jsdoc/newline-after-description': 'off' + 'deprecation/deprecation': 'warn' } }, { diff --git a/.github/workflows/deploy-site.yml b/.github/workflows/deploy-site.yml index 527479dac2..066084e65e 100644 --- a/.github/workflows/deploy-site.yml +++ b/.github/workflows/deploy-site.yml @@ -16,7 +16,7 @@ jobs: - name: build run: | - yarn run site:build:ssr + yarn run site:build cp ./src/dist/browser/index.html ./src/dist/browser/404.html cp ./Dockerfile.docs ./src/dist/browser/Dockerfile.docs cp -r ./_nginx/ ./src/dist/browser/_nginx diff --git a/.nvmrc b/.nvmrc index 3e558c9b3c..d939939b25 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -18.12.0 +18.13.0 diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ec1a6aa4e..e9b0da4b90 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,126 +1,54 @@ -## [16.4.2](https://github.com/ng-alain/delon/compare/16.4.1...16.4.2) (2023-10-26) - - -### Features - -* **abc:qr:** will be removed in 18.0.0 ([#1667](https://github.com/ng-alain/delon/issues/1667)) ([5a7bdd4](https://github.com/ng-alain/delon/commit/5a7bdd42226960c8140397b1a1a377fb87ca725e)) - - -### Performance Improvements - -* **theme:modal:** perf code ([#1671](https://github.com/ng-alain/delon/issues/1671)) ([341ff56](https://github.com/ng-alain/delon/commit/341ff56dc67d65d5a545f124a51938366c7722dc)) - - - -## [16.4.1](https://github.com/ng-alain/delon/compare/16.4.0...16.4.1) (2023-10-20) - - -### Bug Fixes - -* temp downgrade `ng-zorro-antd` to `16.2.0` ([#1663](https://github.com/ng-alain/delon/issues/1663)) ([d3da2b7](https://github.com/ng-alain/delon/commit/d3da2b753c1ebed589ba51e9f184270508eb0ff9)) - - - -# [16.4.0](https://github.com/ng-alain/delon/compare/16.3.0...16.4.0) (2023-10-19) - - -### Bug Fixes - -* **abc:onboarding:** correct dark style ([#1650](https://github.com/ng-alain/delon/issues/1650)) ([7a21933](https://github.com/ng-alain/delon/commit/7a219338660bfbbe18f5ee35ccd1caaabf6e40a4)) -* **abc:st:** correct width misalignment in export excel ([#1655](https://github.com/ng-alain/delon/issues/1655)) ([76db16c](https://github.com/ng-alain/delon/commit/76db16c526057138e470244ccef1cd67e853ccc9)) -* **cli:** missing `fileReplacements` in Angular16 ([#1658](https://github.com/ng-alain/delon/issues/1658)) ([c5b46ee](https://github.com/ng-alain/delon/commit/c5b46ee50a3741dba0f0731f2f2326e9fd47b6cd)) -* **form:array:** fix invalid ui in `ui` schema ([#1657](https://github.com/ng-alain/delon/issues/1657)) ([6b3c711](https://github.com/ng-alain/delon/commit/6b3c711f2f56cbf93f2078b6d83a751b9bdd8bde)) -* **form:select:** correct trigger onSearch in reset when set value ([#1660](https://github.com/ng-alain/delon/issues/1660)) ([fa234cd](https://github.com/ng-alain/delon/commit/fa234cd2e7a127e4df79a82b6965a03220497143)) -* **form:** fix `visibleIf` to correctly trigger `reset` ([#1653](https://github.com/ng-alain/delon/issues/1653)) ([d80f8fb](https://github.com/ng-alain/delon/commit/d80f8fba87659be8099962817cbd17422a5ae249)) -* **form:** fix render UI can't be inherit ([#1661](https://github.com/ng-alain/delon/issues/1661)) ([ee96aaa](https://github.com/ng-alain/delon/commit/ee96aaac047dfa990aa0ffc2d94808939c3311c5)) - - -### Features - -* **abc:onboarding:** add `key` ([#1652](https://github.com/ng-alain/delon/issues/1652)) ([5edaa97](https://github.com/ng-alain/delon/commit/5edaa970f508c402d94843bb8260a5d72bdb5870)) -* **abc:theme:** add `.tag-wrap-list-spacing` class ([#1647](https://github.com/ng-alain/delon/issues/1647)) ([5da4ecb](https://github.com/ng-alain/delon/commit/5da4ecb766c9195609899dbaa543b5eefad82f01)) -* add provide function ([#1654](https://github.com/ng-alain/delon/issues/1654)) ([c8779f4](https://github.com/ng-alain/delon/commit/c8779f41234364bf8690dcf9c9aa5d90c48eadcd)) - - - -# [16.3.0](https://github.com/ng-alain/delon/compare/16.2.1...16.3.0) (2023-09-01) - - -### Bug Fixes - -* **abc:pdf:** fix ignore dependency `pdfjs-dist` ([#1641](https://github.com/ng-alain/delon/issues/1641)) ([b987bab](https://github.com/ng-alain/delon/commit/b987baba6035eb60872c4ee48198568df140869c)) -* **form:select:** fix ignore reset option data when set `onSearch` ([#1644](https://github.com/ng-alain/delon/issues/1644)) ([1f8def7](https://github.com/ng-alain/delon/commit/1f8def70856c091ed677cbd47aed7ca230a2aa79)) -* **theme:http:** fix missing `content` of `HttpOptions` ([#1640](https://github.com/ng-alain/delon/issues/1640)) ([28eeceb](https://github.com/ng-alain/delon/commit/28eecebd7ab71a1b9a8345c0af1ebe22fd3bc1a6)) - - -### Features - -* **abc:cell:** add `cell` component ([#1530](https://github.com/ng-alain/delon/issues/1530)) ([26023ca](https://github.com/ng-alain/delon/commit/26023cac7a91cae5383cfffd26d44fba6a95fb9f)) -* **abc:page-header:** add `titleSub` property ([#1643](https://github.com/ng-alain/delon/issues/1643)) ([79e229f](https://github.com/ng-alain/delon/commit/79e229f5c1b509dd463c48e4a82b361e5d923920)) -* **abc:st:** add `tooltip` in tag or badge ([#1634](https://github.com/ng-alain/delon/issues/1634)) ([0e9006e](https://github.com/ng-alain/delon/commit/0e9006e5b9fd30092b5a808f9b3d8012fd3a060c)) -* **abc:sv:** add `bordered` property ([#1628](https://github.com/ng-alain/delon/issues/1628)) ([ccfa5e1](https://github.com/ng-alain/delon/commit/ccfa5e1d6f5cf1d3f9bc5360bc2e373604ae22a2)) -* **cli:** add `bindToComponentInputs` of `ng add` ([#1630](https://github.com/ng-alain/delon/issues/1630)) ([9717d9d](https://github.com/ng-alain/delon/commit/9717d9dd4ee1d5ab1526616a99da7b70e8664bd2)) -* **theme:drawer:** add `closeAll`, `openDrawers` method ([#1627](https://github.com/ng-alain/delon/issues/1627)) ([bab3d0c](https://github.com/ng-alain/delon/commit/bab3d0c3c648d933784c4623b2714ac227219c5c)) -* **theme:modal:** add size support percentage ([#1626](https://github.com/ng-alain/delon/issues/1626)) ([8b52a08](https://github.com/ng-alain/delon/commit/8b52a08d82378a42e06c316757e19e5434e109dc)) - - - -## [16.2.1](https://github.com/ng-alain/delon/compare/16.2.0...16.2.1) (2023-08-06) - - -### Bug Fixes - -* **abc:reuse-tab:** fix missing export cache ([#1633](https://github.com/ng-alain/delon/issues/1633)) ([2c7def7](https://github.com/ng-alain/delon/commit/2c7def75a5b219a58319ab129407f4058010fc44)) -* **auth:cookie:** fix cookie expires ([#1636](https://github.com/ng-alain/delon/issues/1636)) ([eca7bcb](https://github.com/ng-alain/delon/commit/eca7bcb2e7ba43b3a4b3bb4ab3cd17a7d762a967)) -* **theme:table:** fix table image spacing ([#1629](https://github.com/ng-alain/delon/issues/1629)) ([994e2be](https://github.com/ng-alain/delon/commit/994e2be90354a55a538ed1b55c413b8ce8cde872)) -* **theme:title:** fix ignore empty title ([#1638](https://github.com/ng-alain/delon/issues/1638)) ([c7bf339](https://github.com/ng-alain/delon/commit/c7bf339ee417a3b238cdb7dc18cccd1fe99a6c88)) - - - -# [16.2.0](https://github.com/ng-alain/delon/compare/16.1.1...16.2.0) (2023-07-21) - - -### Bug Fixes - -* **abc:onboarding:** fix `ComponentFactoryResolver` ([#1624](https://github.com/ng-alain/delon/issues/1624)) ([ae065c2](https://github.com/ng-alain/delon/commit/ae065c21e9ba1ea0d56bae9ceb1e44b7bbb9b0fb)) -* **chat:timeline:** fix `y2` to be optional ([#1622](https://github.com/ng-alain/delon/issues/1622)) ([b565ddf](https://github.com/ng-alain/delon/commit/b565ddfdd7872a43f9fd3b3a1fd33d739f08074c)) -* **cli:** remove `skipTests` from generating module ([#1616](https://github.com/ng-alain/delon/issues/1616)) ([0da83f8](https://github.com/ng-alain/delon/commit/0da83f83b90ea5a367d35c6761554d7ebc07bfd0)) -* fix misalignment of `col` placeholders in `se`, `sv`, `sg` ([#1617](https://github.com/ng-alain/delon/issues/1617)) ([83b08c9](https://github.com/ng-alain/delon/commit/83b08c95ba803cf29e0f10bb354ae4f9170b2295)) -* **theme:modal:** removed `nzComponentParams` ([#1615](https://github.com/ng-alain/delon/issues/1615)) ([45863a1](https://github.com/ng-alain/delon/commit/45863a1d62e5751416321cb1d591faf820bb82d3)) - - -### Features - -* **abc:st:** add `onCell`, support colSpan and rowSpan merging ([#1613](https://github.com/ng-alain/delon/issues/1613)) ([9ab109e](https://github.com/ng-alain/delon/commit/9ab109e8f99fb1bd4e5b4e99b0b814bf34f0b4ac)) -* **abc:st:** button support function method of `icon`, `className` ([#1618](https://github.com/ng-alain/delon/issues/1618)) ([6bf27da](https://github.com/ng-alain/delon/commit/6bf27dac696818ff78b0ee955333e308597c968b)) -* **theme:layout-default:** add `fetching` property ([#1614](https://github.com/ng-alain/delon/issues/1614)) ([8446da6](https://github.com/ng-alain/delon/commit/8446da6fdd10d07f2e917d91830d95e1c81d2622)) - - - -## [16.1.1](https://github.com/ng-alain/delon/compare/16.0.1...16.1.1) (2023-07-16) - - -### Bug Fixes - -* **abc:reuse-tab:** fix cache ([#1611](https://github.com/ng-alain/delon/issues/1611)) ([3185ab8](https://github.com/ng-alain/delon/commit/3185ab8939da0b1eefd900a1a003223ad3eb8dba)) -* **cli:** remove `stylelint-config-prettier` ([#1606](https://github.com/ng-alain/delon/issues/1606)) ([2ecc28b](https://github.com/ng-alain/delon/commit/2ecc28b53773d9b5215ebd720be4ead55d78c705)) -* **theme:** fix ant-btn preserve white spaces when is link type ([#1605](https://github.com/ng-alain/delon/issues/1605)) ([0fdd15d](https://github.com/ng-alain/delon/commit/0fdd15dd0922bf6570d38fabd1afc1ca755b87a2)) - - -### Features - -* **abc:reuse-tab:** support custom cache data ([#1609](https://github.com/ng-alain/delon/issues/1609)) ([11599d9](https://github.com/ng-alain/delon/commit/11599d9566712c707146e4ac299ec6efc2d82b01)) -* **theme:modal:** support drag ([#1607](https://github.com/ng-alain/delon/issues/1607)) ([3cd73f7](https://github.com/ng-alain/delon/commit/3cd73f7f86a76a7ea450f839e9ad359f6afd0da4)) - - -## [16.0.1](https://github.com/ng-alain/delon/compare/16.0.0...16.0.1) (2023-06-08) - -* fix `ng-zorro-antd` dependency - -# [16.0.0](https://github.com/ng-alain/delon/compare/15.2.1...16.0.0) (2023-06-07) - -### Bug Fixes - -* **abc:st:** fix error row class in fixed column title cell ([#1598](https://github.com/ng-alain/delon/issues/1598)) ([d2bf211](https://github.com/ng-alain/delon/commit/d2bf211a35df8bcbee165b54bcda4b2dcf69c6f0)) -* **form:** fix inheriting references to other of ui ([#1600](https://github.com/ng-alain/delon/issues/1600)) ([a0150e7](https://github.com/ng-alain/delon/commit/a0150e7520376064469cfa5ae0e3394635620022)) +## 17.0.1 (2023-11-26) + +* fix(cli): fix node version (#1719) ([df40bb3](https://github.com/ng-alain/delon/commit/df40bb3)), closes [#1719](https://github.com/ng-alain/delon/issues/1719) + + + +## 17.0.0 (2023-11-26) + +* chore: add `typescript.referencesCodeLens.enabled` (#1679) ([4d2d25c](https://github.com/ng-alain/delon/commit/4d2d25c)), closes [#1679](https://github.com/ng-alain/delon/issues/1679) +* chore: add data theme (#1684) ([d364ec1](https://github.com/ng-alain/delon/commit/d364ec1)), closes [#1684](https://github.com/ng-alain/delon/issues/1684) +* chore: bump zorro 17.0.1 (#1712) ([4d9f353](https://github.com/ng-alain/delon/commit/4d9f353)), closes [#1712](https://github.com/ng-alain/delon/issues/1712) +* chore: fix `provideMockConfig` (#1703) ([ae1cc30](https://github.com/ng-alain/delon/commit/ae1cc30)), closes [#1703](https://github.com/ng-alain/delon/issues/1703) +* chore: fix interceptor (#1710) ([54f4ea3](https://github.com/ng-alain/delon/commit/54f4ea3)), closes [#1710](https://github.com/ng-alain/delon/issues/1710) +* chore: fix ngDevMode (#1685) ([2803dc8](https://github.com/ng-alain/delon/commit/2803dc8)), closes [#1685](https://github.com/ng-alain/delon/issues/1685) +* chore: perf mock interceptor (#1713) ([fa4dc94](https://github.com/ng-alain/delon/commit/fa4dc94)), closes [#1713](https://github.com/ng-alain/delon/issues/1713) +* chore: remove Hotjar script (#1683) ([66e612d](https://github.com/ng-alain/delon/commit/66e612d)), closes [#1683](https://github.com/ng-alain/delon/issues/1683) +* chore: support standalone of site (#1696) ([1dce440](https://github.com/ng-alain/delon/commit/1dce440)), closes [#1696](https://github.com/ng-alain/delon/issues/1696) +* chore: type of widget registry items (#1715) ([7045cec](https://github.com/ng-alain/delon/commit/7045cec)), closes [#1715](https://github.com/ng-alain/delon/issues/1715) +* chore(cli): fix `ng update` (#1714) ([a6c860e](https://github.com/ng-alain/delon/commit/a6c860e)), closes [#1714](https://github.com/ng-alain/delon/issues/1714) +* chore(theme): add defaultLang (#1705) ([dbc122e](https://github.com/ng-alain/delon/commit/dbc122e)), closes [#1705](https://github.com/ng-alain/delon/issues/1705) +* chore(theme): add icon (#1706) ([fcbb0a7](https://github.com/ng-alain/delon/commit/fcbb0a7)), closes [#1706](https://github.com/ng-alain/delon/issues/1706) +* docs: unified NG-ALAIN text mark (#1677) ([491a342](https://github.com/ng-alain/delon/commit/491a342)), closes [#1677](https://github.com/ng-alain/delon/issues/1677) +* docs: upgrade angualr 17 (#1717) ([0b423c5](https://github.com/ng-alain/delon/commit/0b423c5)), closes [#1717](https://github.com/ng-alain/delon/issues/1717) +* perf: add `provideDelonMockConfig` (#1695) ([683ab23](https://github.com/ng-alain/delon/commit/683ab23)), closes [#1695](https://github.com/ng-alain/delon/issues/1695) +* perf(*): support standalone (#1694) ([d6e75a0](https://github.com/ng-alain/delon/commit/d6e75a0)), closes [#1694](https://github.com/ng-alain/delon/issues/1694) +* perf(*): use control flow (#1716) ([bee524f](https://github.com/ng-alain/delon/commit/bee524f)), closes [#1716](https://github.com/ng-alain/delon/issues/1716) +* perf(theme): remove forRoot and forChild (#1688) ([d1ed91e](https://github.com/ng-alain/delon/commit/d1ed91e)), closes [#1688](https://github.com/ng-alain/delon/issues/1688) +* chore(theme:preloader): support ssr (#1699) ([501566b](https://github.com/ng-alain/delon/commit/501566b)), closes [#1699](https://github.com/ng-alain/delon/issues/1699) +* feat(abc:cell): add `provideCellWidgets` (#1700) ([7ea0daf](https://github.com/ng-alain/delon/commit/7ea0daf)), closes [#1700](https://github.com/ng-alain/delon/issues/1700) +* feat(abc:reuse-tab): add provide (#1707) ([2f85357](https://github.com/ng-alain/delon/commit/2f85357)), closes [#1707](https://github.com/ng-alain/delon/issues/1707) +* feat(abc:st): add `provideSTWidgets` (#1701) ([065316a](https://github.com/ng-alain/delon/commit/065316a)), closes [#1701](https://github.com/ng-alain/delon/issues/1701) +* feat(theme:_httpclient): add `timestampSecond` (#1670) ([051b087](https://github.com/ng-alain/delon/commit/051b087)), closes [#1670](https://github.com/ng-alain/delon/issues/1670) +* feat(theme:pipe:date): add global config (#1711) ([b3b93fa](https://github.com/ng-alain/delon/commit/b3b93fa)), closes [#1711](https://github.com/ng-alain/delon/issues/1711) +* perf(cli:update): automatically add @_mock path (#1675) ([d014b54](https://github.com/ng-alain/delon/commit/d014b54)), closes [#1675](https://github.com/ng-alain/delon/issues/1675) +* perf(theme:preloader): fix loading order issues (#1691) ([f09c324](https://github.com/ng-alain/delon/commit/f09c324)), closes [#1691](https://github.com/ng-alain/delon/issues/1691) +* style(*): `clean` instead of `rational` of style (#1674) ([d9ea22f](https://github.com/ng-alain/delon/commit/d9ea22f)), closes [#1674](https://github.com/ng-alain/delon/issues/1674) +* style(*): bump prettier to `3.1.0` (#1708) ([7f9e861](https://github.com/ng-alain/delon/commit/7f9e861)), closes [#1708](https://github.com/ng-alain/delon/issues/1708) +* style(cli): fix prefer-self-closing-tags error (#1709) ([1abe2d5](https://github.com/ng-alain/delon/commit/1abe2d5)), closes [#1709](https://github.com/ng-alain/delon/issues/1709) +* feat: add `provideAlainConfig` (#1689) ([b9e0fad](https://github.com/ng-alain/delon/commit/b9e0fad)), closes [#1689](https://github.com/ng-alain/delon/issues/1689) +* feat(acl): remove `forRoot` (#1690) ([4472d48](https://github.com/ng-alain/delon/commit/4472d48)), closes [#1690](https://github.com/ng-alain/delon/issues/1690) +* feat(auth): add provide (#1704) ([c0c731b](https://github.com/ng-alain/delon/commit/c0c731b)), closes [#1704](https://github.com/ng-alain/delon/issues/1704) +* feat(cli): support multiple projects (#1664) ([e5476e2](https://github.com/ng-alain/delon/commit/e5476e2)), closes [#1664](https://github.com/ng-alain/delon/issues/1664) +* feat(form): add `provideSFConfig` (#1702) ([2404802](https://github.com/ng-alain/delon/commit/2404802)), closes [#1702](https://github.com/ng-alain/delon/issues/1702) +* feat(mock): add `mockInterceptor` (#1698) ([da051b2](https://github.com/ng-alain/delon/commit/da051b2)), closes [#1698](https://github.com/ng-alain/delon/issues/1698) +* feat(mock): support asynchronous of response (#1686) ([d7980db](https://github.com/ng-alain/delon/commit/d7980db)), closes [#1686](https://github.com/ng-alain/delon/issues/1686) +* feat(testing): add `delay` function (#1682) ([f83ea57](https://github.com/ng-alain/delon/commit/f83ea57)), closes [#1682](https://github.com/ng-alain/delon/issues/1682) +* feat(theme): add `provideAlain` (#1697) ([4311426](https://github.com/ng-alain/delon/commit/4311426)), closes [#1697](https://github.com/ng-alain/delon/issues/1697) +* fix(theme): corrected menu can't scrolling (#1692) ([2993297](https://github.com/ng-alain/delon/commit/2993297)), closes [#1692](https://github.com/ng-alain/delon/issues/1692) +* build: remove `networkEnv` plugin instead of `nnrm` (#1680) ([b7dbc68](https://github.com/ng-alain/delon/commit/b7dbc68)), closes [#1680](https://github.com/ng-alain/delon/issues/1680) +* build: support pnpm and update yarn to `4` (#1678) ([b904b9a](https://github.com/ng-alain/delon/commit/b904b9a)), closes [#1678](https://github.com/ng-alain/delon/issues/1678) +* build: update angualr 17 (#1687) ([a8fc961](https://github.com/ng-alain/delon/commit/a8fc961)), closes [#1687](https://github.com/ng-alain/delon/issues/1687) +* refactor(form): refining low-frequency widgets (#1668) ([8ab0e82](https://github.com/ng-alain/delon/commit/8ab0e82)), closes [#1668](https://github.com/ng-alain/delon/issues/1668) +* ci: update node version (#1681) ([acae2c3](https://github.com/ng-alain/delon/commit/acae2c3)), closes [#1681](https://github.com/ng-alain/delon/issues/1681) diff --git a/README.md b/README.md index f7f56178bf..5710239440 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Delon
- Delon is a set of essential modules for [ng-alain](https://ng-alain.com). + Delon is a set of essential modules for [NG-ALAIN](https://ng-alain.com). [![Build Status](https://dev.azure.com/ng-alain/delon/_apis/build/status/delon-CI?branchName=master)](https://dev.azure.com/ng-alain/delon/_build/latest?definitionId=1&branchName=master) [![Codecov](https://img.shields.io/codecov/c/github/ng-alain/delon.svg?style=flat-square)](https://codecov.io/gh/ng-alain/delon) diff --git a/_mock/user.ts b/_mock/user.ts index 60f76717c5..5bc6eb138d 100644 --- a/_mock/user.ts +++ b/_mock/user.ts @@ -1,9 +1,7 @@ -import { MockStatusError, MockRequest } from '@delon/mock'; +import { MockStatusError, MockRequest, r } from '@delon/mock'; import type { NzSafeAny } from 'ng-zorro-antd/core/types'; // import * as Mock from 'mockjs'; -const r = (min: number, max: number): number => Math.floor(Math.random() * (max - min + 1) + min); - export const USERS = { // 支持值为 Object 和 Array 'GET /users': (req: MockRequest) => { diff --git a/_screenshot/data.webp b/_screenshot/data.webp new file mode 100644 index 0000000000..5f8992d5a2 Binary files /dev/null and b/_screenshot/data.webp differ diff --git a/angular.json b/angular.json index 66810daee0..a961eabf28 100644 --- a/angular.json +++ b/angular.json @@ -21,13 +21,17 @@ "prefix": "app", "architect": { "build": { - "builder": "@angular-devkit/build-angular:browser", + "builder": "@angular-devkit/build-angular:application", "options": { - "outputPath": "src/dist/browser", + "outputPath": "src/dist", "index": "src/index.html", - "main": "src/main.ts", - "tsConfig": "src/tsconfig.json", - "polyfills": "src/polyfills.ts", + "browser": "src/main.ts", + "tsConfig": "src/tsconfig.site.json", + "polyfills": [ + "@webcomponents/custom-elements", + "@webcomponents/custom-elements/src/native-shim", + "zone.js" + ], "assets": [ "src/assets", "src/manifest.json", @@ -78,7 +82,14 @@ "date-fns", "extend", "aos" - ] + ], + "server": "src/main.server.ts", + "prerender": { + "routesFile": "scripts/site/route-paths.txt" + }, + "ssr": { + "entry": "src/server.ts" + } }, "configurations": { "production": { @@ -89,16 +100,12 @@ } ], "outputHashing": "all", - "serviceWorker": true, - "ngswConfigPath": "ngsw-config.json" + "serviceWorker": "ngsw-config.json" }, "development": { - "buildOptimizer": false, "optimization": false, - "vendorChunk": true, "extractLicenses": false, - "sourceMap": true, - "namedChunks": true + "sourceMap": true } }, "defaultConfiguration": "production" @@ -107,58 +114,13 @@ "builder": "@angular-devkit/build-angular:dev-server", "configurations": { "production": { - "browserTarget": "site:build:production" + "buildTarget": "site:build:production" }, "development": { - "browserTarget": "site:build:development" + "buildTarget": "site:build:development" } }, "defaultConfiguration": "development" - }, - "server": { - "builder": "@angular-devkit/build-angular:server", - "options": { - "outputPath": "src/dist/server", - "main": "./src/server.ts", - "tsConfig": "src/tsconfig.server.json" - }, - "configurations": { - "production": { - "outputHashing": "media", - "fileReplacements": [ - { - "replace": "src/environments/environment.ts", - "with": "src/environments/environment.prod.ts" - } - ], - "sourceMap": false, - "optimization": true - } - } - }, - "serve-ssr": { - "builder": "@nguniversal/builders:ssr-dev-server", - "options": { - "browserTarget": "site:build", - "serverTarget": "site:server" - }, - "configurations": { - "production": { - "browserTarget": "site:build:production", - "serverTarget": "site:server:production" - } - } - }, - "prerender": { - "builder": "@nguniversal/builders:prerender", - "options": { - "browserTarget": "site:build:production", - "serverTarget": "site:server:production", - "routesFile": "scripts/site/route-paths.txt" - }, - "configurations": { - "production": {} - } } } }, diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 6f9514cf73..81cd450d63 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -40,7 +40,7 @@ stages: ACCESS_REPO: $(ACCESS_REPO) ACCESS_TOKEN: $(ACCESS_TOKEN) - script: | - yarn run site:build:ssr + yarn run site:build displayName: 'Build site' - script: | export DEPLOY_DOMAIN=https://preview-${SYSTEM_PULLREQUEST_PULLREQUESTNUMBER}-ng-alain-delon.surge.sh diff --git a/docs/architecture.en-US.md b/docs/architecture.en-US.md index f0962ab4c6..44afa16295 100644 --- a/docs/architecture.en-US.md +++ b/docs/architecture.en-US.md @@ -65,25 +65,22 @@ Schematic diagram of directory structure: ├── _mock # Mock Data rule ├── src │   ├── app -│   │   ├── core # Core module +│   │   ├── core # Core │   │   │   ├── i18n │   │   │   ├── net │   │   │   │   └── default.interceptor.ts # Default HTTP interceptor │   │   │   ├── services │   │   │   │   └── startup.service.ts # Initialize project configuration -│   │   │   └── core.module.ts # Core module file +│   │   │   └── index.ts # Core index.ts │   │   ├── layout # Core layout │   │   ├── routes │   │   │   ├── ** # Business directory -│   │   │   ├── routes.module.ts # Service routing module -│   │   │   └── routes-routing.module.ts # Service routes registration +│   │   │   └── routes.ts # Service routes registration │   │   ├── shared # Shared module -│   │   │   ├── shared-delon.module.ts # @Delon/* import of secondary shared modules -│   │   │   ├── shared-zorro.module.ts # NG-ZORRO import of secondary shared modules -│   │   │   └── shared.module.ts # Shared module file +│   │   │   ├── shared-imports.ts # A collection of frequently shared components +│   │   │   └── index.ts # Shared index.ts │   │   ├── app.component.ts # Root component -│   │   └── app.module.ts # Root module -│   │   └── global-config.module.ts # @delon & ng-zorro global config +│   │   └── app.config.ts # Global config │   ├── assets # Local static resource │   ├── environments # Environment variable configuration │   ├── styles # Style directory @@ -96,9 +93,9 @@ The following is a description and use of each directory and file. The Mock data rules directory, if you create a project via [Command Line Tools](/cli), you can specify the `--mock` parameter to determine if the Mock function is required. -**src/app/core/core.module.ts** +**src/app/core/index.ts** -The core module will only be imported once. Therefore, core service classes (eg, messages, data access, etc.) that are required for the entire ** business module should exist here. +Some core business services (for example: messaging, data access, etc.) **src/app/core/i18n** @@ -108,7 +105,7 @@ The core module will only be imported once. Therefore, core service classes (eg, The default interceptor, where you can handle request parameters, request exceptions, business exceptions, and so on. -**src/app/core/services/startup.service.ts** +**src/app/core/startup/startup.service.ts** Useful when you need to execute some remote data (eg application information, user information, etc.) before Angular launches. @@ -122,13 +119,13 @@ Layout file code, refer to the page structure section. Business module, all your business code will be here. -**src/app/shared/shared.module.ts** +**src/app/shared/index.ts** -The shared module means that some third-party modules, custom components, and custom instructions that you need to use for the entire business module should exist here. In addition, for @delon & NG-ZORRO, two shared secondary module imports, `shared-delon.module.ts` and` shared-zorro.module.ts`. +A collection of some frequently shared components, The means that some third-party modules, custom components, and custom instructions that you need to use for the entire business module should exist here. In addition, for @delon & NG-ZORRO, two shared secondary module imports, `shared-delon.module.ts` and` shared-zorro.module.ts`. -**src/app/global-config.module.ts** +**src/app/app.config.ts** -Global configuration for @delon & NG-ZORRO. +Global configuration for project. **src/environments** diff --git a/docs/architecture.zh-CN.md b/docs/architecture.zh-CN.md index 82dff43793..bba6f58e38 100644 --- a/docs/architecture.zh-CN.md +++ b/docs/architecture.zh-CN.md @@ -34,25 +34,22 @@ NG-ALAIN 目标是提供更多通用性业务模块,让开发者更加专注 ├── angular.json # Angular 项目配置文件 ├── src │   ├── app -│   │   ├── core # 核心模块 +│   │   ├── core # 核心 │   │   │   ├── i18n │   │   │   ├── net │   │   │   │   └── default.interceptor.ts # 默认HTTP拦截器 │   │   │   ├── services │   │   │   │   └── startup.service.ts # 初始化项目配置 -│   │   │   └── core.module.ts # 核心模块文件 +│   │   │   └── index.ts # 核心导出 │   │   ├── layout # 通用布局 │   │   ├── routes │   │   │   ├── ** # 业务目录 -│   │   │   ├── routes.module.ts # 业务路由模块 -│   │   │   └── routes-routing.module.ts # 业务路由注册口 -│   │   ├── shared # 共享模块 -│   │   │   ├── shared-delon.module.ts # @Delon/* 次级共享模块导入 -│   │   │   ├── shared-zorro.module.ts # NG-ZORRO 次级共享模块导入 -│   │   │   └── shared.module.ts # 共享模块文件 +│   │   │   └── routes.ts # 业务路由 +│   │   ├── shared # 共享 +│   │   │   ├── shared-imports.ts # 一些高频率共享组件的集合 +│   │   │   └── index.ts # 共享导出 │   │   ├── app.component.ts # 根组件 -│   │   └── app.module.ts # 根模块 -│   │   └── global-config.module.ts # @delon & ng-zorro 全局配置项 +│   │   └── app.config.ts # 全局配置项 │   ├── assets # 本地静态资源 │   ├── environments # 环境变量配置 │   ├── styles # 样式目录 @@ -65,12 +62,12 @@ NG-ALAIN 目标是提供更多通用性业务模块,让开发者更加专注 |----|----| | **angular.json** | Angular 工作区及项目的配置文件,参考[Angular文档](https://angular.cn/guide/workspace-config) | | **_mock** | Mock 数据规则目录,若你通过 [命令行工具](/cli) 创建项目时可以指定 `--mock` 参数决定是否需要 Mock 功能 | -| **src/app/core/core.module.ts** | 核心模块,只会导入一次。因此,针对整个**业务模块都需要**使用的纯服务类(例如:消息、数据访问等) | +| **src/app/core/index.ts** | 一些核心业务服务(例如:消息、数据访问等) | | **src/app/core/i18n** | [国际化](/docs/i18n)数据加载及处理相关类,若你通过 [命令行工具](/cli) 创建项目时可以指定 `-di` 参数决定是否需要国际化支持 | | **src/app/core/net** | 默认拦截器,你可以在这里统一处理请求参数、请求异常、业务异常等动作 | -| **src/app/core/services/startup.service.ts** | 当你需要在 Angular 启动前执行一些远程数据(例如:应用信息、用户信息等)时非常有用 | +| **src/app/core/startup/startup.service.ts** | 当你需要在 Angular 启动前执行一些远程数据(例如:应用信息、用户信息等)时非常有用 | | **src/app/layout** | 布局目录,包含基础布局、空白布局、用户登录布局 | | **src/app/routes** | 业务模块,你的所有业务代码都将在这里 | -| **src/app/shared/shared.module.ts** | 共享模块,指当你需要针对整个**业务模块都需要**使用的一些第三方模块、自定义组件、自定义指令,都应该存在这里。除此之外,针对 @delon & NG-ZORRO 分别构建了 `shared-delon.module.ts`、`shared-zorro.module.ts` 两种次级共享模块的导入。 | -| **src/app/global-config.module.ts** | 针对 @delon & NG-ZORRO 的全局配置项 | +| **src/app/shared/index.ts** | 一些高频率共享组件的集合,指当你需要针对整个**业务模块都需要**使用的一些第三方模块、自定义组件、自定义指令,都应该存在这里。除此之外,针对 @delon & NG-ZORRO 分别构建了 `shared-delon.module.ts`、`shared-zorro.module.ts` 两种次级共享模块的导入。 | +| **src/app/app.config.ts** | 项目全局配置项 | | **src/environments** | 应用环境变量,包含以下值:`SERVER_URL` 所有HTTP请求的前缀;`production` 是否生产环境;`useHash` 路由是否useHash模式 | diff --git a/docs/changelog.en-US.md b/docs/changelog.en-US.md index 1e1502077e..bed499a456 100644 --- a/docs/changelog.en-US.md +++ b/docs/changelog.en-US.md @@ -14,127 +14,40 @@ NG-ALAIN strictly follows [Semantic Versioning 2.0.0](http://semver.org/lang/zh- --- -## [16.4.2](https://github.com/ng-alain/delon/compare/16.4.1...16.4.2) (2023-10-26) +## 17.0.1 (2023-11-26) -### Performance Improvements +* fix(cli): fix node 20 version (#1719) ([df40bb3](https://github.com/ng-alain/delon/commit/df40bb3)), closes [#1719](https://github.com/ng-alain/delon/issues/1719) -* **theme:modal:** perf code ([#1671](https://github.com/ng-alain/delon/issues/1671)) ([341ff56](https://github.com/ng-alain/delon/commit/341ff56dc67d65d5a545f124a51938366c7722dc)) -* **abc:qr:** will be removed in 18.0.0 ([#1667](https://github.com/ng-alain/delon/issues/1667)) ([5a7bdd4](https://github.com/ng-alain/delon/commit/5a7bdd42226960c8140397b1a1a377fb87ca725e)) +## 17.0.0 (2023-11-26) +### Breaking Changes -## [16.4.1](https://github.com/ng-alain/delon/compare/16.4.0...16.4.1) (2023-10-20) - -### Bug Fixes - -* temp downgrade `ng-zorro-antd` to `16.2.0` ([#1663](https://github.com/ng-alain/delon/issues/1663)) ([d3da2b7](https://github.com/ng-alain/delon/commit/d3da2b753c1ebed589ba51e9f184270508eb0ff9)) - - -# [16.4.0](https://github.com/ng-alain/delon/compare/16.3.0...16.4.0) (2023-10-19) - -### Bug Fixes - -* **abc:onboarding:** correct dark style ([#1650](https://github.com/ng-alain/delon/issues/1650)) ([7a21933](https://github.com/ng-alain/delon/commit/7a219338660bfbbe18f5ee35ccd1caaabf6e40a4)) -* **abc:st:** correct width misalignment in export excel ([#1655](https://github.com/ng-alain/delon/issues/1655)) ([76db16c](https://github.com/ng-alain/delon/commit/76db16c526057138e470244ccef1cd67e853ccc9)) -* **cli:** missing `fileReplacements` in Angular16 ([#1658](https://github.com/ng-alain/delon/issues/1658)) ([c5b46ee](https://github.com/ng-alain/delon/commit/c5b46ee50a3741dba0f0731f2f2326e9fd47b6cd)) -* **form:array:** fix invalid ui in `ui` schema ([#1657](https://github.com/ng-alain/delon/issues/1657)) ([6b3c711](https://github.com/ng-alain/delon/commit/6b3c711f2f56cbf93f2078b6d83a751b9bdd8bde)) -* **form:select:** correct trigger onSearch in reset when set value ([#1660](https://github.com/ng-alain/delon/issues/1660)) ([fa234cd](https://github.com/ng-alain/delon/commit/fa234cd2e7a127e4df79a82b6965a03220497143)) -* **form:** fix `visibleIf` to correctly trigger `reset` ([#1653](https://github.com/ng-alain/delon/issues/1653)) ([d80f8fb](https://github.com/ng-alain/delon/commit/d80f8fba87659be8099962817cbd17422a5ae249)) -* **form:** fix render UI can't be inherit ([#1661](https://github.com/ng-alain/delon/issues/1661)) ([ee96aaa](https://github.com/ng-alain/delon/commit/ee96aaac047dfa990aa0ffc2d94808939c3311c5)) -* **fix(form:widget:object):** fix missing optional of card type ([#1661](https://github.com/ng-alain/delon/issues/1661)) ([ee96aaa](https://github.com/ng-alain/delon/commit/ee96aaac047dfa990aa0ffc2d94808939c3311c5)) - -### Features - -* **abc:onboarding:** add `key` ([#1652](https://github.com/ng-alain/delon/issues/1652)) ([5edaa97](https://github.com/ng-alain/delon/commit/5edaa970f508c402d94843bb8260a5d72bdb5870)) -* **abc:theme:** add `.tag-wrap-list-spacing` class ([#1647](https://github.com/ng-alain/delon/issues/1647)) ([5da4ecb](https://github.com/ng-alain/delon/commit/5da4ecb766c9195609899dbaa543b5eefad82f01)) -* **cli:** add provide function ([#1654](https://github.com/ng-alain/delon/issues/1654)) ([c8779f4](https://github.com/ng-alain/delon/commit/c8779f41234364bf8690dcf9c9aa5d90c48eadcd)) - - -# [16.3.0](https://github.com/ng-alain/delon/compare/16.2.1...16.3.0) (2023-09-01) - -### SCAFFOLDING - -* enabled `bindToComponentInputs` [#2409](https://github.com/ng-alain/ng-alain/pull/2409/files). - -### Bug Fixes - -* **abc:pdf:** fix ignore dependency `pdfjs-dist` (If you [use local path](https://ng-alain.com/components/pdf) to load the libary, you need to install the dependency yourself. `pdfjs-dist` depends on `canvas`, which may be slow to install dependencies due to environmental factors) ([#1641](https://github.com/ng-alain/delon/issues/1641)) ([#1641](https://github.com/ng-alain/delon/issues/1641)) ([b987bab](https://github.com/ng-alain/delon/commit/b987baba6035eb60872c4ee48198568df140869c)) -* **form:select:** fix ignore reset option data when set `onSearch` ([#1644](https://github.com/ng-alain/delon/issues/1644)) ([1f8def7](https://github.com/ng-alain/delon/commit/1f8def70856c091ed677cbd47aed7ca230a2aa79)) -* **theme:http:** fix missing `content` of `HttpOptions` ([#1640](https://github.com/ng-alain/delon/issues/1640)) ([28eeceb](https://github.com/ng-alain/delon/commit/28eecebd7ab71a1b9a8345c0af1ebe22fd3bc1a6)) - -### Features - -* **abc:cell:** add `cell` component ([#1530](https://github.com/ng-alain/delon/issues/1530)) ([26023ca](https://github.com/ng-alain/delon/commit/26023cac7a91cae5383cfffd26d44fba6a95fb9f)) -* **abc:page-header:** add `titleSub` property ([#1643](https://github.com/ng-alain/delon/issues/1643)) ([79e229f](https://github.com/ng-alain/delon/commit/79e229f5c1b509dd463c48e4a82b361e5d923920)) -* **abc:st:** add `tooltip` in tag or badge ([#1634](https://github.com/ng-alain/delon/issues/1634)) ([0e9006e](https://github.com/ng-alain/delon/commit/0e9006e5b9fd30092b5a808f9b3d8012fd3a060c)) -* **abc:sv:** add `bordered` property ([#1628](https://github.com/ng-alain/delon/issues/1628)) ([ccfa5e1](https://github.com/ng-alain/delon/commit/ccfa5e1d6f5cf1d3f9bc5360bc2e373604ae22a2)) -* **cli:** add `bindToComponentInputs` of `ng add` ([#1630](https://github.com/ng-alain/delon/issues/1630)) ([9717d9d](https://github.com/ng-alain/delon/commit/9717d9dd4ee1d5ab1526616a99da7b70e8664bd2)) -* **theme:drawer:** add `closeAll`, `openDrawers` method ([#1627](https://github.com/ng-alain/delon/issues/1627)) ([bab3d0c](https://github.com/ng-alain/delon/commit/bab3d0c3c648d933784c4623b2714ac227219c5c)) -* **theme:modal:** add size support percentage ([#1626](https://github.com/ng-alain/delon/issues/1626)) ([8b52a08](https://github.com/ng-alain/delon/commit/8b52a08d82378a42e06c316757e19e5434e109dc)) - - -## [16.2.1](https://github.com/ng-alain/delon/compare/16.2.0...16.2.1) (2023-08-06) - -### Bug Fixes - -* **abc:reuse-tab:** fix missing export cache ([#1633](https://github.com/ng-alain/delon/issues/1633)) ([2c7def7](https://github.com/ng-alain/delon/commit/2c7def75a5b219a58319ab129407f4058010fc44)) -* **auth:cookie:** fix cookie expires ([#1636](https://github.com/ng-alain/delon/issues/1636)) ([eca7bcb](https://github.com/ng-alain/delon/commit/eca7bcb2e7ba43b3a4b3bb4ab3cd17a7d762a967)) -* **theme:table:** fix table image spacing ([#1629](https://github.com/ng-alain/delon/issues/1629)) ([994e2be](https://github.com/ng-alain/delon/commit/994e2be90354a55a538ed1b55c413b8ce8cde872)) -* **theme:title:** fix ignore empty title ([#1638](https://github.com/ng-alain/delon/issues/1638)) ([c7bf339](https://github.com/ng-alain/delon/commit/c7bf339ee417a3b238cdb7dc18cccd1fe99a6c88)) - - -# [16.2.0](https://github.com/ng-alain/delon/compare/16.1.1...16.2.0) (2023-07-21) - -### SCAFFOLDING - -* Breaking changes router guard [#2407](https://github.com/ng-alain/ng-alain/pull/2407/files). -* Code style [#2405](https://github.com/ng-alain/ng-alain/pull/2405/files#diff-a3f38f2cae79a3819f93ff1a9d4cd281cbe8f95696e14a29864f08796d3dc568). - -### Bug Fixes - -* **abc:onboarding:** fix `ComponentFactoryResolver` ([#1624](https://github.com/ng-alain/delon/issues/1624)) ([ae065c2](https://github.com/ng-alain/delon/commit/ae065c21e9ba1ea0d56bae9ceb1e44b7bbb9b0fb)) -* **chat:timeline:** fix `y2` to be optional ([#1622](https://github.com/ng-alain/delon/issues/1622)) ([b565ddf](https://github.com/ng-alain/delon/commit/b565ddfdd7872a43f9fd3b3a1fd33d739f08074c)) -* **cli:** remove `skipTests` from generating module ([#1616](https://github.com/ng-alain/delon/issues/1616)) ([0da83f8](https://github.com/ng-alain/delon/commit/0da83f83b90ea5a367d35c6761554d7ebc07bfd0)) -* fix misalignment of `col` placeholders in `se`, `sv`, `sg` ([#1617](https://github.com/ng-alain/delon/issues/1617)) ([83b08c9](https://github.com/ng-alain/delon/commit/83b08c95ba803cf29e0f10bb354ae4f9170b2295)) -* **theme:modal:** removed `nzComponentParams` ([#1615](https://github.com/ng-alain/delon/issues/1615)) ([45863a1](https://github.com/ng-alain/delon/commit/45863a1d62e5751416321cb1d591faf820bb82d3)) - -### Features - -* **abc:st:** add `onCell`, support colSpan and rowSpan merging ([#1613](https://github.com/ng-alain/delon/issues/1613)) ([9ab109e](https://github.com/ng-alain/delon/commit/9ab109e8f99fb1bd4e5b4e99b0b814bf34f0b4ac)) -* **abc:st:** button support function method of `icon`, `className` ([#1618](https://github.com/ng-alain/delon/issues/1618)) ([6bf27da](https://github.com/ng-alain/delon/commit/6bf27dac696818ff78b0ee955333e308597c968b)) -* **theme:layout-default:** add `fetching` property ([#1614](https://github.com/ng-alain/delon/issues/1614)) ([8446da6](https://github.com/ng-alain/delon/commit/8446da6fdd10d07f2e917d91830d95e1c81d2622)) - -### BREAKING CHANGE - -* **acl:** refactor `ACLGuard` to `aclCanMatch`, `aclCanActivate`, `aclCanActivateChild` -* **auth:simple** refactor `SimpleGuard` to `authSimpleCanMatch`, `authSimpleCanActivate`, `authSimpleCanActivateChild` -* **auth:jtw** refactor `JWTGuard` to `authJWTCanMatch`, `authJWTCanActivate`, `authJWTCanActivateChild` -* **i18n** refactor `AlainI18NGuard` to `alainI18nCanMatch`, `alainI18nCanActivate`, `alainI18nCanActivateChild` - - -# [16.1.1](https://github.com/ng-alain/delon/compare/16.0.1...16.1.0) (2023-07-16) - -### Bug Fixes - -* **cli:** remove `stylelint-config-prettier` ([#1606](https://github.com/ng-alain/delon/issues/1606)) ([2ecc28b](https://github.com/ng-alain/delon/commit/2ecc28b53773d9b5215ebd720be4ead55d78c705)) -* **theme:** fix ant-btn preserve white spaces when is link type ([#1605](https://github.com/ng-alain/delon/issues/1605)) ([0fdd15d](https://github.com/ng-alain/delon/commit/0fdd15dd0922bf6570d38fabd1afc1ca755b87a2)) +* refactor(form): refining low-frequency widgets (#1668) ([8ab0e82](https://github.com/ng-alain/delon/commit/8ab0e82)), closes [#1668](https://github.com/ng-alain/delon/issues/1668) +* build: remove `networkEnv` plugin instead of [nnrm](https://github.com/YunYouJun/nnrm/blob/main/README.md) (#1680) ([b7dbc68](https://github.com/ng-alain/delon/commit/b7dbc68)), closes [#1680](https://github.com/ng-alain/delon/issues/1680) +* feat(acl): remove `forRoot` (#1690) ([4472d48](https://github.com/ng-alain/delon/commit/4472d48)), closes [#1690](https://github.com/ng-alain/delon/issues/1690) ### Features -* **abc:reuse-tab:** support custom cache data ([#1609](https://github.com/ng-alain/delon/issues/1609)) ([11599d9](https://github.com/ng-alain/delon/commit/11599d9566712c707146e4ac299ec6efc2d82b01)) -* **theme:modal:** support drag ([#1607](https://github.com/ng-alain/delon/issues/1607)) ([3cd73f7](https://github.com/ng-alain/delon/commit/3cd73f7f86a76a7ea450f839e9ad359f6afd0da4)) - - -## [16.0.1](https://github.com/ng-alain/delon/compare/16.0.0...16.0.1) (2023-06-08) - -* fix `ng-zorro-antd` dependency - -# [16.0.0](https://github.com/ng-alain/delon/compare/15.2.1...16.0.0) (2023-06-07) +* feat(cli): support use `ng add ng-alain` in multiple projects (#1664) ([e5476e2](https://github.com/ng-alain/delon/commit/e5476e2)), closes [#1664](https://github.com/ng-alain/delon/issues/1664) +* feat(theme): add `provideAlain` (#1697) ([4311426](https://github.com/ng-alain/delon/commit/4311426)), closes [#1697](https://github.com/ng-alain/delon/issues/1697) +* feat(abc:cell): add `provideCellWidgets` (#1700) ([7ea0daf](https://github.com/ng-alain/delon/commit/7ea0daf)), closes [#1700](https://github.com/ng-alain/delon/issues/1700) +* feat(abc:reuse-tab): add `provideReuseTabConfig` (#1707) ([2f85357](https://github.com/ng-alain/delon/commit/2f85357)), closes [#1707](https://github.com/ng-alain/delon/issues/1707) +* feat(abc:st): add `provideSTWidgets` (#1701) ([065316a](https://github.com/ng-alain/delon/commit/065316a)), closes [#1701](https://github.com/ng-alain/delon/issues/1701) +* feat(theme:_httpclient): add `timestampSecond` (#1670) ([051b087](https://github.com/ng-alain/delon/commit/051b087)), closes [#1670](https://github.com/ng-alain/delon/issues/1670) +* feat(theme:pipe:date): add global config (#1711) ([b3b93fa](https://github.com/ng-alain/delon/commit/b3b93fa)), closes [#1711](https://github.com/ng-alain/delon/issues/1711) +* feat: add `provideAlainConfig` (#1689) ([b9e0fad](https://github.com/ng-alain/delon/commit/b9e0fad)), closes [#1689](https://github.com/ng-alain/delon/issues/1689) +* feat(auth): add `provideAuth` (#1704) ([c0c731b](https://github.com/ng-alain/delon/commit/c0c731b)), closes [#1704](https://github.com/ng-alain/delon/issues/1704) +* feat(form): add `provideSFConfig` (#1702) ([2404802](https://github.com/ng-alain/delon/commit/2404802)), closes [#1702](https://github.com/ng-alain/delon/issues/1702) +* feat(mock): add `mockInterceptor` (#1698) ([da051b2](https://github.com/ng-alain/delon/commit/da051b2)), closes [#1698](https://github.com/ng-alain/delon/issues/1698) +* feat(mock): support asynchronous (`Promise`, `Observable`) of response (#1686) ([d7980db](https://github.com/ng-alain/delon/commit/d7980db)), closes [#1686](https://github.com/ng-alain/delon/issues/1686) +* feat(mock): add `provideDelonMockConfig` (#1695) ([683ab23](https://github.com/ng-alain/delon/commit/683ab23)), closes [#1695](https://github.com/ng-alain/delon/issues/1695) +* feat(testing): add `delay` function (#1682) ([f83ea57](https://github.com/ng-alain/delon/commit/f83ea57)), closes [#1682](https://github.com/ng-alain/delon/issues/1682) +* build: support pnpm and update yarn to `4` (#1678) ([b904b9a](https://github.com/ng-alain/delon/commit/b904b9a)), closes [#1678](https://github.com/ng-alain/delon/issues/1678) ### Bug Fixes -* **abc:st:** fix error row class in fixed column title cell ([#1598](https://github.com/ng-alain/delon/issues/1598)) ([d2bf211](https://github.com/ng-alain/delon/commit/d2bf211a35df8bcbee165b54bcda4b2dcf69c6f0)) -* **form:** fix inheriting references to other of ui ([#1600](https://github.com/ng-alain/delon/issues/1600)) ([a0150e7](https://github.com/ng-alain/delon/commit/a0150e7520376064469cfa5ae0e3394635620022)) - +* fix(theme:preloader): fix loading order issues (#1691) ([f09c324](https://github.com/ng-alain/delon/commit/f09c324)), closes [#1691](https://github.com/ng-alain/delon/issues/1691) +* fix(cli:update): automatically add @_mock path (#1675) ([d014b54](https://github.com/ng-alain/delon/commit/d014b54)), closes [#1675](https://github.com/ng-alain/delon/issues/1675) ## Old Versions diff --git a/docs/changelog.zh-CN.md b/docs/changelog.zh-CN.md index e8acd37533..7da191d855 100644 --- a/docs/changelog.zh-CN.md +++ b/docs/changelog.zh-CN.md @@ -14,127 +14,41 @@ NG-ALAIN 严格遵循 [Semantic Versioning 2.0.0](http://semver.org/lang/zh-CN/) --- -## [16.4.2](https://github.com/ng-alain/delon/compare/16.4.1...16.4.2) (2023-10-26) +## 17.0.1 (2023-11-26) -### Performance Improvements +* fix(cli): 修复不支持 node 20 版本 (#1719) ([df40bb3](https://github.com/ng-alain/delon/commit/df40bb3)), closes [#1719](https://github.com/ng-alain/delon/issues/1719) -* **theme:modal:** perf code ([#1671](https://github.com/ng-alain/delon/issues/1671)) ([341ff56](https://github.com/ng-alain/delon/commit/341ff56dc67d65d5a545f124a51938366c7722dc)) -* **abc:qr:** will be removed in 18.0.0 ([#1667](https://github.com/ng-alain/delon/issues/1667)) ([5a7bdd4](https://github.com/ng-alain/delon/commit/5a7bdd42226960c8140397b1a1a377fb87ca725e)) +## 17.0.0 (2023-11-26) -## [16.4.1](https://github.com/ng-alain/delon/compare/16.4.0...16.4.1) (2023-10-20) +### Breaking Changes -### Bug Fixes - -* temp downgrade `ng-zorro-antd` to `16.2.0` ([#1663](https://github.com/ng-alain/delon/issues/1663)) ([d3da2b7](https://github.com/ng-alain/delon/commit/d3da2b753c1ebed589ba51e9f184270508eb0ff9)) - - -# [16.4.0](https://github.com/ng-alain/delon/compare/16.3.0...16.4.0) (2023-10-19) - -### Bug Fixes - -* **abc:onboarding:** 纠正暗黑风格 ([#1650](https://github.com/ng-alain/delon/issues/1650)) ([7a21933](https://github.com/ng-alain/delon/commit/7a219338660bfbbe18f5ee35ccd1caaabf6e40a4)) -* **abc:st:** 纠正导出 Excel 中的宽度未对齐问题 ([#1655](https://github.com/ng-alain/delon/issues/1655)) ([76db16c](https://github.com/ng-alain/delon/commit/76db16c526057138e470244ccef1cd67e853ccc9)) -* **cli:** 修复 Angular 16 下 `ng new ng-alain` 缺失 `fileReplacements` ([#1658](https://github.com/ng-alain/delon/issues/1658)) ([c5b46ee](https://github.com/ng-alain/delon/commit/c5b46ee50a3741dba0f0731f2f2326e9fd47b6cd)) -* **form:array:** 修复 `ui` 属性无法合并 ui ([#1657](https://github.com/ng-alain/delon/issues/1657)) ([6b3c711](https://github.com/ng-alain/delon/commit/6b3c711f2f56cbf93f2078b6d83a751b9bdd8bde)) -* **form:select:** 纠正有默认值时初始化时无法触发 `onSearch` ([#1660](https://github.com/ng-alain/delon/issues/1660)) ([fa234cd](https://github.com/ng-alain/delon/commit/fa234cd2e7a127e4df79a82b6965a03220497143)) -* **form:** 修复设置 `visibleIf` 触发时也会触发 `reset` 方法 ([#1653](https://github.com/ng-alain/delon/issues/1653)) ([d80f8fb](https://github.com/ng-alain/delon/commit/d80f8fba87659be8099962817cbd17422a5ae249)) -* **form:** 修复渲染UI不应被继承([#1661](https://github.com/ng-alain/delon/issues/1661)) ([ee96aaa](https://github.com/ng-alain/delon/commit/ee96aaac047dfa990aa0ffc2d94808939c3311c5)) -* **fix(form:widget:object):** 修复 `card` 样式缺失 `optional` ([#1661](https://github.com/ng-alain/delon/issues/1661)) ([ee96aaa](https://github.com/ng-alain/delon/commit/ee96aaac047dfa990aa0ffc2d94808939c3311c5)) - -### Features - -* **abc:onboarding:** 新增 `key` 用于自动判断是否发生版本变化 ([#1652](https://github.com/ng-alain/delon/issues/1652)) ([5edaa97](https://github.com/ng-alain/delon/commit/5edaa970f508c402d94843bb8260a5d72bdb5870)) -* **abc:theme:** 纠正 `ng-tag` 列表换行间距问题 ([#1647](https://github.com/ng-alain/delon/issues/1647)) ([5da4ecb](https://github.com/ng-alain/delon/commit/5da4ecb766c9195609899dbaa543b5eefad82f01)) -* **cli:** add provide function ([#1654](https://github.com/ng-alain/delon/issues/1654)) ([c8779f4](https://github.com/ng-alain/delon/commit/c8779f41234364bf8690dcf9c9aa5d90c48eadcd)) - - -# [16.3.0](https://github.com/ng-alain/delon/compare/16.2.1...16.3.0) (2023-09-01) - -### SCAFFOLDING - -* 启用 `bindToComponentInputs` [#2409](https://github.com/ng-alain/ng-alain/pull/2409/files). - -### Bug Fixes - -* **abc:pdf:** 忽略依赖 `pdfjs-dist`(若使用[本地](https://ng-alain.com/components/pdf)加载模式,需要自行安装该依赖,`pdfjs-dist` 依赖了 `canvas` 可能会因为环境因素安装依赖很慢) ([#1641](https://github.com/ng-alain/delon/issues/1641)) ([b987bab](https://github.com/ng-alain/delon/commit/b987baba6035eb60872c4ee48198568df140869c)) -* **form:select:** 当设置 `onSearch` 时重置数据时会忽略更新预选数据 ([#1644](https://github.com/ng-alain/delon/issues/1644)) ([1f8def7](https://github.com/ng-alain/delon/commit/1f8def70856c091ed677cbd47aed7ca230a2aa79)) -* **theme:http:** 纠正 `HttpOptions` 缺少 `content` ([#1640](https://github.com/ng-alain/delon/issues/1640)) ([28eeceb](https://github.com/ng-alain/delon/commit/28eecebd7ab71a1b9a8345c0af1ebe22fd3bc1a6)) +* refactor(form): 重构低频率小部件为可选导入 (#1668) ([8ab0e82](https://github.com/ng-alain/delon/commit/8ab0e82)), closes [#1668](https://github.com/ng-alain/delon/issues/1668) +* build: 移除 `networkEnv` 插件,使用 [nnrm](https://github.com/YunYouJun/nnrm/blob/main/README.zh-CN.md) 替代 (#1680) ([b7dbc68](https://github.com/ng-alain/delon/commit/b7dbc68)), closes [#1680](https://github.com/ng-alain/delon/issues/1680) +* feat(acl): 移除 `forRoot` (#1690) ([4472d48](https://github.com/ng-alain/delon/commit/4472d48)), closes [#1690](https://github.com/ng-alain/delon/issues/1690) ### Features -* **abc:cell:** 新增 `cell` 单元格数据组件 ([#1530](https://github.com/ng-alain/delon/issues/1530)) ([26023ca](https://github.com/ng-alain/delon/commit/26023cac7a91cae5383cfffd26d44fba6a95fb9f)) -* **abc:page-header:** 新增 `titleSub` 子标题属性 ([#1643](https://github.com/ng-alain/delon/issues/1643)) ([79e229f](https://github.com/ng-alain/delon/commit/79e229f5c1b509dd463c48e4a82b361e5d923920)) -* **abc:st:** 标签或徽标支持 `tooltip` ([#1634](https://github.com/ng-alain/delon/issues/1634)) ([0e9006e](https://github.com/ng-alain/delon/commit/0e9006e5b9fd30092b5a808f9b3d8012fd3a060c)) -* **abc:sv:** 新增 `bordered` 是否显示边框 ([#1628](https://github.com/ng-alain/delon/issues/1628)) ([ccfa5e1](https://github.com/ng-alain/delon/commit/ccfa5e1d6f5cf1d3f9bc5360bc2e373604ae22a2)) -* **cli:** `ng add` 开启 `bindToComponentInputs` ([#1630](https://github.com/ng-alain/delon/issues/1630)) ([9717d9d](https://github.com/ng-alain/delon/commit/9717d9dd4ee1d5ab1526616a99da7b70e8664bd2)) -* **theme:drawer:** 新增 `closeAll`, `openDrawers` 方法 ([#1627](https://github.com/ng-alain/delon/issues/1627)) ([bab3d0c](https://github.com/ng-alain/delon/commit/bab3d0c3c648d933784c4623b2714ac227219c5c)) -* **theme:modal:** 支持百分比大小 ([#1626](https://github.com/ng-alain/delon/issues/1626)) ([8b52a08](https://github.com/ng-alain/delon/commit/8b52a08d82378a42e06c316757e19e5434e109dc)) - - -## [16.2.1](https://github.com/ng-alain/delon/compare/16.2.0...16.2.1) (2023-08-06) +* feat(cli): 支持多重项目下使用 `ng add ng-alain` (#1664) ([e5476e2](https://github.com/ng-alain/delon/commit/e5476e2)), closes [#1664](https://github.com/ng-alain/delon/issues/1664) +* feat(theme): 新增 `provideAlain` (#1697) ([4311426](https://github.com/ng-alain/delon/commit/4311426)), closes [#1697](https://github.com/ng-alain/delon/issues/1697) +* feat(abc:cell): 新增 `provideCellWidgets` (#1700) ([7ea0daf](https://github.com/ng-alain/delon/commit/7ea0daf)), closes [#1700](https://github.com/ng-alain/delon/issues/1700) +* feat(abc:reuse-tab): 新增 `provideReuseTabConfig` (#1707) ([2f85357](https://github.com/ng-alain/delon/commit/2f85357)), closes [#1707](https://github.com/ng-alain/delon/issues/1707) +* feat(abc:st): 新增 `provideSTWidgets` (#1701) ([065316a](https://github.com/ng-alain/delon/commit/065316a)), closes [#1701](https://github.com/ng-alain/delon/issues/1701) +* feat(theme:_httpclient): 新增 `timestampSecond` 支持 10 位时间戳 (#1670) ([051b087](https://github.com/ng-alain/delon/commit/051b087)), closes [#1670](https://github.com/ng-alain/delon/issues/1670) +* feat(theme:pipe:date): 支持全局配置格式化字符串 (#1711) ([b3b93fa](https://github.com/ng-alain/delon/commit/b3b93fa)), closes [#1711](https://github.com/ng-alain/delon/issues/1711) +* feat: 新增 `provideAlainConfig` (#1689) ([b9e0fad](https://github.com/ng-alain/delon/commit/b9e0fad)), closes [#1689](https://github.com/ng-alain/delon/issues/1689) +* feat(auth): 新增 `provideAuth` (#1704) ([c0c731b](https://github.com/ng-alain/delon/commit/c0c731b)), closes [#1704](https://github.com/ng-alain/delon/issues/1704) +* feat(form): 新增 `provideSFConfig` (#1702) ([2404802](https://github.com/ng-alain/delon/commit/2404802)), closes [#1702](https://github.com/ng-alain/delon/issues/1702) +* feat(mock): 新增 `mockInterceptor` (#1698) ([da051b2](https://github.com/ng-alain/delon/commit/da051b2)), closes [#1698](https://github.com/ng-alain/delon/issues/1698) +* feat(mock): 支持异步返回,例如 `Promise`、`Observable` (#1686) ([d7980db](https://github.com/ng-alain/delon/commit/d7980db)), closes [#1686](https://github.com/ng-alain/delon/issues/1686) +* feat(mock): 新增 `provideDelonMockConfig` (#1695) ([683ab23](https://github.com/ng-alain/delon/commit/683ab23)), closes [#1695](https://github.com/ng-alain/delon/issues/1695) +* feat(testing): 新增 `delay` function (#1682) ([f83ea57](https://github.com/ng-alain/delon/commit/f83ea57)), closes [#1682](https://github.com/ng-alain/delon/issues/1682) +* build: support pnpm and update yarn to `4` (#1678) ([b904b9a](https://github.com/ng-alain/delon/commit/b904b9a)), closes [#1678](https://github.com/ng-alain/delon/issues/1678) ### Bug Fixes -* **abc:reuse-tab:** 修复缺少导出缓存相关 ([#1633](https://github.com/ng-alain/delon/issues/1633)) ([2c7def7](https://github.com/ng-alain/delon/commit/2c7def75a5b219a58319ab129407f4058010fc44)) -* **auth:cookie:** 修复过期值应与 expires 同步 ([#1636](https://github.com/ng-alain/delon/issues/1636)) ([eca7bcb](https://github.com/ng-alain/delon/commit/eca7bcb2e7ba43b3a4b3bb4ab3cd17a7d762a967)) -* **theme:table:** 修复单图像的间距问题 ([#1629](https://github.com/ng-alain/delon/issues/1629)) ([994e2be](https://github.com/ng-alain/delon/commit/994e2be90354a55a538ed1b55c413b8ce8cde872)) -* **theme:title:** 修复应忽略空标题 ([#1638](https://github.com/ng-alain/delon/issues/1638)) ([c7bf339](https://github.com/ng-alain/delon/commit/c7bf339ee417a3b238cdb7dc18cccd1fe99a6c88)) - - -# [16.2.0](https://github.com/ng-alain/delon/compare/16.1.1...16.2.0) (2023-07-21) - -### SCAFFOLDING - -* 路由守卫的破坏性变更 [#2407](https://github.com/ng-alain/ng-alain/pull/2407/files). -* 代码风格变更 [#2405](https://github.com/ng-alain/ng-alain/pull/2405/files#diff-a3f38f2cae79a3819f93ff1a9d4cd281cbe8f95696e14a29864f08796d3dc568). - -### Bug Fixes - -* **abc:onboarding:** 修复使用过期方法 `ComponentFactoryResolver` ([#1624](https://github.com/ng-alain/delon/issues/1624)) ([ae065c2](https://github.com/ng-alain/delon/commit/ae065c21e9ba1ea0d56bae9ceb1e44b7bbb9b0fb)) -* **chat:timeline:** 修复 `y2` 指标数据为可选 ([#1622](https://github.com/ng-alain/delon/issues/1622)) ([b565ddf](https://github.com/ng-alain/delon/commit/b565ddfdd7872a43f9fd3b3a1fd33d739f08074c)) -* **cli:** 移除生成模块时包含无效参数 `skipTests` ([#1616](https://github.com/ng-alain/delon/issues/1616)) ([0da83f8](https://github.com/ng-alain/delon/commit/0da83f83b90ea5a367d35c6761554d7ebc07bfd0)) -* 修复 `col` 占位符不对齐问题,涉及 `se`, `sv`, `sg` 组件 ([#1617](https://github.com/ng-alain/delon/issues/1617)) ([83b08c9](https://github.com/ng-alain/delon/commit/83b08c95ba803cf29e0f10bb354ae4f9170b2295)) -* **theme:modal:** 修复过期参数 `nzComponentParams` ([#1615](https://github.com/ng-alain/delon/issues/1615)) ([45863a1](https://github.com/ng-alain/delon/commit/45863a1d62e5751416321cb1d591faf820bb82d3)) - -### Features - -* **abc:st:** 新增 `onCell` 方法支持合并列或行 ([#1613](https://github.com/ng-alain/delon/issues/1613)) ([9ab109e](https://github.com/ng-alain/delon/commit/9ab109e8f99fb1bd4e5b4e99b0b814bf34f0b4ac)) -* **abc:st:** 新增 `icon`, `className` 方法支持动态调整 ([#1618](https://github.com/ng-alain/delon/issues/1618)) ([6bf27da](https://github.com/ng-alain/delon/commit/6bf27dac696818ff78b0ee955333e308597c968b)) -* **theme:layout-default:** 新增 `fetchingStrictly`, `fetching` 属性用于自主受控顶部加载动画状态 ([#1614](https://github.com/ng-alain/delon/issues/1614)) ([8446da6](https://github.com/ng-alain/delon/commit/8446da6fdd10d07f2e917d91830d95e1c81d2622)) - -### BREAKING CHANGE - -* **acl:** 重构 `ACLGuard` 为 `aclCanMatch`, `aclCanActivate`, `aclCanActivateChild` -* **auth:simple** 重构 `SimpleGuard` 为 `authSimpleCanMatch`, `authSimpleCanActivate`, `authSimpleCanActivateChild` -* **auth:jtw** 重构 `JWTGuard` 为 `authJWTCanMatch`, `authJWTCanActivate`, `authJWTCanActivateChild` -* **i18n** 重构 `AlainI18NGuard` 为 `alainI18nCanMatch`, `alainI18nCanActivate`, `alainI18nCanActivateChild` - - -# [16.1.1](https://github.com/ng-alain/delon/compare/16.0.1...16.1.0) (2023-07-16) - -### Bug Fixes - -* **cli:** 移除过期库 `stylelint-config-prettier` ([#1606](https://github.com/ng-alain/delon/issues/1606)) ([2ecc28b](https://github.com/ng-alain/delon/commit/2ecc28b53773d9b5215ebd720be4ead55d78c705)) -* **theme:** 修复忽略两个 `nzType="link"` 按错时间距 ([#1605](https://github.com/ng-alain/delon/issues/1605)) ([0fdd15d](https://github.com/ng-alain/delon/commit/0fdd15dd0922bf6570d38fabd1afc1ca755b87a2)) - -### Features - -* **abc:reuse-tab:** 新增可自定义缓存存储 ([#1609](https://github.com/ng-alain/delon/issues/1609)) ([11599d9](https://github.com/ng-alain/delon/commit/11599d9566712c707146e4ac299ec6efc2d82b01)) -* **theme:modal:** 新增支持拖拽参数 `drag` ([#1607](https://github.com/ng-alain/delon/issues/1607)) ([3cd73f7](https://github.com/ng-alain/delon/commit/3cd73f7f86a76a7ea450f839e9ad359f6afd0da4)) - - -## [16.0.1](https://github.com/ng-alain/delon/compare/16.0.0...16.0.1) (2023-06-08) - -* 修复 `ng-zorro-antd` 错位版本依赖 - -# [16.0.0](https://github.com/ng-alain/delon/compare/15.2.1...16.0.0) (2023-06-07) - -### Bug Fixes - -* **abc:st:** 修复固定列样式错误 ([#1598](https://github.com/ng-alain/delon/issues/1598)) ([d2bf211](https://github.com/ng-alain/delon/commit/d2bf211a35df8bcbee165b54bcda4b2dcf69c6f0)) -* **form:** 修复ui由于继承关系导致重复引用 ([#1600](https://github.com/ng-alain/delon/issues/1600)) ([a0150e7](https://github.com/ng-alain/delon/commit/a0150e7520376064469cfa5ae0e3394635620022)) - +* fix(theme:preloader): 修复启动动画存在懒加载时空白问题 (#1691) ([f09c324](https://github.com/ng-alain/delon/commit/f09c324)), closes [#1691](https://github.com/ng-alain/delon/issues/1691) +* fix(cli:update): 修复自动追加 `@_mock` 路径 (#1675) ([d014b54](https://github.com/ng-alain/delon/commit/d014b54)), closes [#1675](https://github.com/ng-alain/delon/issues/1675) ## 历史版本 diff --git a/docs/faq.en-US.md b/docs/faq.en-US.md index 78d418aada..005899ed1c 100644 --- a/docs/faq.en-US.md +++ b/docs/faq.en-US.md @@ -10,10 +10,6 @@ Please check the FAQ below before asking questions. ## Basic -### Can't Bind to since it isn't a known property of - -When you have multiple lazy modules, you want each submodule to use the component library (for example: `NgZorroAntdModule`, `NgxTinymceModule`) should be exported in `ShareModule`, please refer to [module registration guidelines](/docs/Module). - ### Expression Changed After It Has Been Checked Error Common mistakes under Angular, [this article](https://blog.angularindepth.com/everything-you-need-to-know-about-the-expressionchangedafterithasbeencheckederror-error-e3fd9ce7dbb4) will help you understand why. diff --git a/docs/faq.zh-CN.md b/docs/faq.zh-CN.md index bb59cc8b3f..b141560c4f 100644 --- a/docs/faq.zh-CN.md +++ b/docs/faq.zh-CN.md @@ -10,10 +10,6 @@ type: Other ## 基础 -### Can't Bind to since it isn't a known property of - -当你有多个懒模块时,希望每个子模块都需要使用组件库时(例如:`NgZorroAntdModule`、`NgxTinymceModule`)应在 `ShareModule` 中 export,请参考[模块注册指导原则](/docs/module)。 - ### Expression Changed After It Has Been Checked Error 错误 Angular 下常见错误,[这篇文章](https://blog.angularindepth.com/everything-you-need-to-know-about-the-expressionchangedafterithasbeencheckederror-error-e3fd9ce7dbb4) 会帮助你理解原因。 diff --git a/docs/getting-started.en-US.md b/docs/getting-started.en-US.md index 3bf09c6d25..623aaf0a25 100644 --- a/docs/getting-started.en-US.md +++ b/docs/getting-started.en-US.md @@ -39,6 +39,18 @@ npm start # Or use HMR mode by: npm run hmr ``` +**Multiple projects** + +```bash +yarn global add @angular/cli +ng new my-workspace --no-create-application --package-manager yarn +cd my-workspace +ng g application mgr --style less --routing +ng add ng-alain +yarn mgr:start +# Or use HMR mode by: yarn run mgr:hmr +``` + > Please refer to [Schematics](/cli) for more details. ### Clone the Git Repository @@ -103,9 +115,9 @@ This will automatically open [http://localhost:4200](http://localhost:4200). If - Server-side Rendering - [Electron](https://electron.atom.io/) -| [IE / Edge](https://godban.github.io/browsers-support-badges//)
IE / Edge | [Firefox](https://godban.github.io/browsers-support-badges/)
Firefox | [Chrome](https://godban.github.io/browsers-support-badges/)
Chrome | [Safari](https://godban.github.io/browsers-support-badges/)
Safari | [Opera](https://godban.github.io/browsers-support-badges/)
Opera | [Electron](https://godban.github.io/browsers-support-badges/)
Electron | -| --- | --- | --- | --- | --- | --- | -| Edge | last 2 versions | last 2 versions | last 2 versions | last 2 versions | last 2 versions | +| [Edge](http://godban.github.io/browsers-support-badges/)
Edge | [Firefox](http://godban.github.io/browsers-support-badges/)
Firefox | [Chrome](http://godban.github.io/browsers-support-badges/)
Chrome | [Safari](http://godban.github.io/browsers-support-badges/)
Safari | [Opera](http://godban.github.io/browsers-support-badges/)
Opera | [Electron](http://godban.github.io/browsers-support-badges/)
Electron | +| --------- | --------- | --------- | --------- | --------- | --------- | +| last 2 versions | last 2 versions | last 2 versions | last 2 versions | last 2 versions | last 2 versions ## Contributing diff --git a/docs/getting-started.zh-CN.md b/docs/getting-started.zh-CN.md index dde9de1294..9a554383cd 100644 --- a/docs/getting-started.zh-CN.md +++ b/docs/getting-started.zh-CN.md @@ -43,6 +43,11 @@ NG-ALAIN 必须先创建一个全新的 Angular 项目,可以通过终端窗 ```bash ng new my-project --style less --routing +cd my-project +# 或多重项目 +ng new my-workspace --no-create-application +cd my-workspace +ng g application mgr --style less --routing ``` > 如果你想了解 `--style`、`--routing` 参数,请参考 [ng new](https://angular.io/cli/new#options) 文档。 @@ -50,10 +55,11 @@ ng new my-project --style less --routing 接下来只需要将 NG-ALAIN 添加到 `my-project` 项目中即可,在 `my-project` 目录下通过终端窗口中运行: ```bash -cd my-project ng add ng-alain ``` +> 若多重项目时,需要提供具体的项目名称。 + NG-ALAIN 会询问是否需要一些额外的插件,一开始完全可以一路回车,这些插件都是可插拔,后期可以自行添加与移除。 > 以上只会生成干净的项目,可以直接用于生产环境中。你可能在[预览](https://ng-alain.gitee.io/)上看到许多示例页,它们全都可以在 [Github](https://github.com/ng-alain/ng-alain) 查看到源代码,当然也可以通过 Git 克隆代码的形式获得: @@ -82,9 +88,9 @@ npm start - 支持服务端渲染 - [Electron](https://electron.atom.io/) -| [IE / Edge](https://godban.github.io/browsers-support-badges//)
IE / Edge | [Firefox](https://godban.github.io/browsers-support-badges/)
Firefox | [Chrome](https://godban.github.io/browsers-support-badges/)
Chrome | [Safari](https://godban.github.io/browsers-support-badges/)
Safari | [Opera](https://godban.github.io/browsers-support-badges/)
Opera | [Electron](https://godban.github.io/browsers-support-badges/)
Electron | -| --- | --- | --- | --- | --- | --- | -| Edge | last 2 versions | last 2 versions | last 2 versions | last 2 versions | last 2 versions | +| [Edge](http://godban.github.io/browsers-support-badges/)
Edge | [Firefox](http://godban.github.io/browsers-support-badges/)
Firefox | [Chrome](http://godban.github.io/browsers-support-badges/)
Chrome | [Safari](http://godban.github.io/browsers-support-badges/)
Safari | [Opera](http://godban.github.io/browsers-support-badges/)
Opera | [Electron](http://godban.github.io/browsers-support-badges/)
Electron | +| --------- | --------- | --------- | --------- | --------- | --------- | +| last 2 versions | last 2 versions | last 2 versions | last 2 versions | last 2 versions | last 2 versions ## 如何贡献 diff --git a/docs/global-config.en-US.md b/docs/global-config.en-US.md index 2dee1385b1..c0b30d6fce 100644 --- a/docs/global-config.en-US.md +++ b/docs/global-config.en-US.md @@ -10,22 +10,31 @@ We add support of **global configuration** to many components. You can define th ## How to Use? -If you want to provide default configurations to some components, you should provide an object that implements the interface **AlainConfig** with the injection token **ALAIN_CONFIG**, in the root module (in another word, to the root injector). Like this: +If you want to provide default configurations to some components, please use `provideAlain` function. object providing implements interface `AlainProvideOptions` For example: ```typescript // global-config.module.ts -import { AlainConfig, ALAIN_CONFIG } from '@delon/util/config'; +import { AlainConfig, AlainProvideLang } from '@delon/util/config'; +import { ICONS } from '../style-icons'; +import { ICONS_AUTO } from '../style-icons-auto'; + +const defaultLang: AlainProvideLang = { + abbr: 'zh-CN', + ng: ngLang, + zorro: zorroLang, + date: dateLang, + delon: delonLang +}; const alainConfig: AlainConfig = { st: { ps: 3 }, }; -@NgModule({ +export const appConfig: ApplicationConfig = { providers: [ - { provide: ALAIN_CONFIG, useValue: alainConfig }, - ], -}) -export class GlobalConfigModule {} + provideAlain({ config: alainConfig, defaultLang, icons: [...ICONS_AUTO, ...ICONS] }) + ] +}; ``` These global configuration would be injected into a service named `AlainConfigService` and gets stored. diff --git a/docs/global-config.zh-CN.md b/docs/global-config.zh-CN.md index 626eea5689..1cd350ee32 100644 --- a/docs/global-config.zh-CN.md +++ b/docs/global-config.zh-CN.md @@ -10,22 +10,31 @@ type: Dev ## 如何使用 -想要为某些组件提供默认配置项,请在根注入器中根据注入令牌 `ALAIN_CONFIG` 提供一个符合 `AlainConfig` 接口的对象,例如: +想要为某些组件提供默认配置项,可以使用 `provideAlain` 函数,转入一个符合 `AlainProvideOptions` 接口的对象,例如: ```typescript // global-config.module.ts -import { AlainConfig, ALAIN_CONFIG } from '@delon/util/config'; +import { AlainConfig, AlainProvideLang } from '@delon/util/config'; +import { ICONS } from '../style-icons'; +import { ICONS_AUTO } from '../style-icons-auto'; + +const defaultLang: AlainProvideLang = { + abbr: 'zh-CN', + ng: ngLang, + zorro: zorroLang, + date: dateLang, + delon: delonLang +}; const alainConfig: AlainConfig = { st: { ps: 3 }, }; -@NgModule({ +export const appConfig: ApplicationConfig = { providers: [ - { provide: ALAIN_CONFIG, useValue: alainConfig }, - ], -}) -export class GlobalConfigModule {} + provideAlain({ config: alainConfig, defaultLang, icons: [...ICONS_AUTO, ...ICONS] }) + ] +}; ``` 这些全局配置项将会被注入 `AlainConfigService` 当中并保存。 diff --git a/docs/how-to-start.en-US.md b/docs/how-to-start.en-US.md index a84bd382ea..f84666e775 100644 --- a/docs/how-to-start.en-US.md +++ b/docs/how-to-start.en-US.md @@ -31,13 +31,14 @@ When running an app via `ng serve`, a complete Angular startup process would loo ### 1) APP_INITIALIZER -From a mid and back-office perspective, NG-ALAIN always believes that a network request is required to get some application information (eg menu data, user data, etc.) before Angular starts.[startup.service.ts](https://github.com/ng-alain/ng-alain/blob/master/src/app/core/startup/startup.service.ts);It returns a `Promise` object, which always needs to be called: `resolve()` to ensure that Angular starts normally. +Angular provides a DI (dependency injection) token `APP_INITIALIZER` that allows the application to perform some data that will affect the rendering results when it starts, such as: language data, menu data, user information data, dictionary data, etc., and must return an `Observable` Asynchronous, asynchronous means that you can do a lot of interesting things, such as data coming from a remote location. `APP_INITIALIZER` will only be executed once, you only need to register it in the `ApplicationConfig` module. -> Network requests may encounter a 403 error because the scaffolding uses the user authentication module by default and always assumes that all requests must be a valid user authorization. For more documentation see: -> - [Interact with server](/docs/server) -> - [Auth User Authentication](/auth) +The NG-ALAIN scaffolding provides a sample code on how to load basic data before starting rendering after starting Angular [startup.service.ts](https://github.com/ng-alain/ng-alain/blob/ master/src/app/core/startup/startup.service.ts). -After obtaining the application information, you need to assign some values ​​to the built-in services of the scaffolding, including: +1. Provide unified registration `provideStartup` function, which only needs to be registered in `app.config.ts` +2. Provide the `load()` function and ensure that **regardless of whether the request is successful or not** must return an `Observable` for Angular to render normally, otherwise Angular will not be able to start. + +> Note: NG-ALAIN provides authorization services. If the requested data interface cannot be authorized, you can add `ALLOW_ANONYMOUS` to mark it. **Application Information** @@ -99,18 +100,21 @@ It is recommended to load the internationalization package first before starting ### 2) Business routing -Scaffolding top-level routing begins with [routes-routing.module.ts](https://github.com/ng-alain/ng-alain/blob/master/src/app/routes/routes-routing.module.ts) Its structure is as follows: +Scaffolding top-level routing begins with [routes.ts](https://github.com/ng-alain/ng-alain/blob/master/src/app/routes/routes.ts) Its structure is as follows: ```ts const routes: Routes = [ { path: '', - component: LayoutDefaultComponent, + component: LayoutBasicComponent, children: [ { path: '', redirectTo: 'dashboard', pathMatch: 'full' }, - { path: 'dashboard', component: DashboardComponent, data: { title: 'Dashboard' } }, + { + path: 'dashboard', + loadChildren: () => import('./dashboard/routes').then(m => m.routes) + }, // business submodule - // { path: 'trade', loadChildren: './trade/trade.module#TradeModule' } + // { path: 'trade', loadChildren: () => import('./trade/routes').then(m => m.routes) }, ] }, // Blank layout @@ -120,37 +124,29 @@ const routes: Routes = [ children: [ ] }, - // passport - { - path: 'passport', - component: LayoutPassportComponent, - children: [ - { path: 'login', component: UserLoginComponent }, - { path: 'register', component: UserRegisterComponent }, - { path: 'register-result', component: UserRegisterResultComponent } - ] - }, - // Single page does not wrap Layout - { path: 'callback/:type', component: CallbackComponent }, - { path: '403', component: Exception403Component }, - { path: '404', component: Exception404Component }, - { path: '500', component: Exception500Component }, + { path: '', loadChildren: () => import('./passport/routes').then(m => m.routes) }, + { path: 'exception', loadChildren: () => import('./exception/routes').then(m => m.routes) }, + // All missed routes will jump to the `exception/404` page { path: '**', redirectTo: 'dashboard' } ]; ``` -Above we used the `LayoutDefaultComponent` base layout in the business module. User authorization uses `LayoutPassportComponent` user authorization layout and the full screen layout. +> The above mentioned `LayoutBasicComponent` basic layout is used in the business module, user authorization uses `LayoutPassportComponent` user authorization layout and `LayoutBlankComponent` blank layout. The above three layouts can be used in [layout](https://github.com/ ng-alain/ng-alain/tree/master/src/app/layout) directory. -It is recommended that all submodules be loaded using a lazy module, such as the `TradeModule` order module, which organizes the code structure more efficiently. +> NG-ALAIN also provides some [commercial themes](https://e.ng-alain.com/) to chooses. -### Under what circumstances do you not use lazy loading? - -Angular startup from the top-level component. When a lazy module is encountered, it will initiate a script request. At this time, the dashboard or login page will be blank due to network requests, which is not good for the experience. +For example, when a user accesses the `/dashboard` route, they will first go through `LayoutBasicComponent` -> `DashboardComponent`, and eventually form a huge component tree to represent a specific page. NG-ALAIN scaffolding helps you complete most of the work, and a newbie only needs to care about how to implement the `DashboardComponent` business component. ### Routing permission control The routing URL may be affected by the browser's own historical memory, so that users may access the unprivileged route. If you want a better experience, you need to configure the `canActivate` option on the route. When the user has no permission, it will utomatically jump to the relevant page. see the [ACL Routing Guard](/acl/guard) section for details. +### Intercept network requests + +Network requests are a very frequent task. If you want to use network request actions elegantly within business components, it is essential to centrally handle server-side URL prefixes, exception handling, token refresh and other operations. NG-ALAIN scaffolding Provide a [net](https://github.com/ng-alain/ng-alain/tree/master/src/app/core/net) file. It uses the token `HttpInterceptorFn` to act as an interceptor. + +For details, please refer to [default.interceptor.ts](https://github.com/ng-alain/ng-alain/blob/master/src/app/core/net/default.interceptor. ts) file. + ## IDE A developer must first sharpen his tools if he is to do his work well, NG-ALAIN recommended to use the [Visual Studio Code](https://code.visualstudio.com/) IDE, because ng-alain adds some extra features to VSCode to better help you. Development. diff --git a/docs/how-to-start.zh-CN.md b/docs/how-to-start.zh-CN.md index 4b78a8279a..b1fbf66cff 100644 --- a/docs/how-to-start.zh-CN.md +++ b/docs/how-to-start.zh-CN.md @@ -31,48 +31,16 @@ NG-ALAIN 技术栈基于 Typescript、Angular、图表G2 和 NG-ZORRO,在开 ### 初始化项目数据 -Angular 提供一个DI(依赖注入)令牌 `APP_INITIALIZER` 让应用启动时可以做一些会影响渲染结果的数据,比如:语言数据、菜单数据、用户信息数据、字典数据等,并且必须返回一个 `Promise` 异步函数,异步意味者可以做很多有趣的事,比如数据来自远程。`APP_INITIALIZER` 只会执行一次,只需要在 `AppModule` 模块注册它就行了。 +Angular 提供一个DI(依赖注入)令牌 `APP_INITIALIZER` 让应用启动时可以做一些会影响渲染结果的数据,比如:语言数据、菜单数据、用户信息数据、字典数据等,并且必须返回一个 `Observable` 异步,异步意味者可以做很多有趣的事,比如数据来自远程。`APP_INITIALIZER` 只会执行一次,只需要在 `ApplicationConfig` 模块注册它就行了。 -```ts -export function StartupServiceFactory(startupService: StartupService): () => Promise { - return () => startupService.load(); -} - -@NgModule({ - declarations: [AppComponent], - imports: [BrowserModule] - providers: [{ - StartupService, - { - provide: APP_INITIALIZER, - useFactory: StartupServiceFactory, - deps: [StartupService], - multi: true, - }, - }], - bootstrap: [AppComponent], -}) -export class AppModule {} -``` +NG-ALAIN 脚手架提供了一个如何在启动 Angular 后先加载基础数据以后才会开始渲染的样板代码 [startup.service.ts](https://github.com/ng-alain/ng-alain/blob/master/src/app/core/startup/startup.service.ts)。 -而 `StartupService` 如下: +1. 提供统一注册 `provideStartup` 函数,只需要在 `app.config.ts` 注册就能生效 +2. 提供 `load()` 函数,并确保**无论请求是否成功**都必须返回一个 `Observable` 以供Angular正常渲染,否则会导致Angular无法启动 -```ts -@Injectable() -export class StartupService { - constructor(private httpClient: HttpClient) {} - - load(): Promise { - return new Promise((resolve) => { - this.httpClient.get(``).subscribe(() => { - resolve(); - }); - }); - } -} -``` +> 注:NG-ALAIN 提供授权服务,若在请求数据接口无法授权时,可加 `ALLOW_ANONYMOUS` 来标记 -哪怕 Http 请求失败,这里也必须执行 `resolve()`,否则应用就无法启动。而 NG-ALAIN 提供的 [startup.service.ts](https://github.com/ng-alain/ng-alain/blob/master/src/app/core/startup/startup.service.ts) 内容更加丰富一点,对于完整的中后台而言,大多数项目中以下这些信息都可以必备的: + NG-ALAIN 提供的 [startup.service.ts](https://github.com/ng-alain/ng-alain/blob/master/src/app/core/startup/startup.service.ts) 内容更加丰富一点,对于完整的中后台而言,大多数项目中以下这些信息都可以必备的: | 数据类型 | 描述 | |------|----| @@ -86,7 +54,7 @@ export class StartupService { ### 业务路由 -当 Angular 项目正式启动后会进入渲染动作,根据当前的路由地址来决定一个页面如何渲染,从最顶层路由 [routes-routing.module.ts](https://github.com/ng-alain/ng-alain/blob/master/src/app/routes/routes-routing.module.ts) 开始一层层寻找,其结构如下: +当 Angular 项目正式启动后会进入渲染动作,根据当前的路由地址来决定一个页面如何渲染,从最顶层路由 [routes.ts](https://github.com/ng-alain/ng-alain/blob/master/src/app/routes/routes.ts) 开始一层层寻找,其结构如下: ```ts const routes: Routes = [ @@ -95,9 +63,12 @@ const routes: Routes = [ component: LayoutBasicComponent, children: [ { path: '', redirectTo: 'dashboard', pathMatch: 'full' }, - { path: 'dashboard', component: DashboardComponent, data: { title: '仪表盘' } }, + { + path: 'dashboard', + loadChildren: () => import('./dashboard/routes').then(m => m.routes) + }, // 业务子模块 - // { path: 'trade', loadChildren: './trade/trade.module#TradeModule' } + // { path: 'trade', loadChildren: () => import('./trade/routes').then(m => m.routes) }, ] }, // 空白布局 @@ -107,17 +78,8 @@ const routes: Routes = [ children: [ ] }, - // passport - { - path: 'passport', - component: LayoutPassportComponent, - children: [ - { path: 'login', component: UserLoginComponent }, - ] - }, - // 单页不包裹Layout - { path: 'passport/callback/:type', component: CallbackComponent }, - { path: 'exception', loadChildren: () => import('./exception/exception.module').then((m) => m.ExceptionModule) }, + { path: '', loadChildren: () => import('./passport/routes').then(m => m.routes) }, + { path: 'exception', loadChildren: () => import('./exception/routes').then(m => m.routes) }, // 未命中路由全部跳转至 `exception/404` 页面上 { path: '**', redirectTo: 'exception/404' }, ]; @@ -125,11 +87,9 @@ const routes: Routes = [ > 上述在业务模块中使用了 `LayoutBasicComponent` 基础布局、用户授权使用了 `LayoutPassportComponent` 用户授权布局以及 `LayoutBlankComponent` 空白布局,以上三种布局都可以在 [layout](https://github.com/ng-alain/ng-alain/tree/master/src/app/layout) 目录下找得到。 -例如当用户访问 `/dashboard` 路由时,会先经过 `LayoutBasicComponent` -> `DashboardComponent`,最终换形成一个庞大的组件树来表示一个具体的页面。NG-ALAIN 脚手架帮助你完成大多数工作,而一个新入门的人更多只需要关心 `DashboardComponent` 业务组件该如何实现。 - -**什么情况下不使用懒加载?** +> NG-ALAIN 也提供一些[商用主题](https://e.ng-alain.com/)可供选择。 -Angular 启动是从顶层组件开始向下渲染,当遇到懒模块时会先发起脚本请求,此时会因为网络请求导致仪表盘或登录页短暂的空白,这对体验并不好。 +例如当用户访问 `/dashboard` 路由时,会先经过 `LayoutBasicComponent` -> `DashboardComponent`,最终换形成一个庞大的组件树来表示一个具体的页面。NG-ALAIN 脚手架帮助你完成大多数工作,而一个新入门的人更多只需要关心 `DashboardComponent` 业务组件该如何实现。 ### 用户认证与授权 @@ -180,10 +140,9 @@ const routes: Routes = [ ### 拦截网络请求 -网络请求是一项非常频繁的工作,如果想优雅的在业务组件内使用网络请求动作的话,那么将服务端URL前缀、异常处理、Token 刷新等操作集中处理是必不可少的,NG-ALAIN 脚手架提供一个 [default.interceptor.ts](https://github.com/ng-alain/ng-alain/blob/master/src/app/core/net/default.interceptor.ts) 文件。它会利用令牌 `HTTP_INTERCEPTORS` 起到一种拦截器的效果。 - -有关以上集中处理的动作细节,请参考 [default.interceptor.ts](https://github.com/ng-alain/ng-alain/blob/master/src/app/core/net/default.interceptor.ts) 文件。 +网络请求是一项非常频繁的工作,如果想优雅的在业务组件内使用网络请求动作的话,那么将服务端URL前缀、异常处理、Token 刷新等操作集中处理是必不可少的,NG-ALAIN 脚手架提供一个 [net](https://github.com/ng-alain/ng-alain/tree/master/src/app/core/net) 文件。它会利用令牌 `HttpInterceptorFn` 起到一种拦截器的效果。 +有关更多细节,请参考 [default.interceptor.ts](https://github.com/ng-alain/ng-alain/blob/master/src/app/core/net/default.interceptor.ts) 文件。 ## IDE diff --git a/docs/i18n.en-US.md b/docs/i18n.en-US.md index e6f0220fb2..649fc86e47 100644 --- a/docs/i18n.en-US.md +++ b/docs/i18n.en-US.md @@ -28,10 +28,9 @@ registerLocaleData(zh); ```ts import { en_US, provideNzI18n } from 'ng-zorro-antd/i18n'; -@NgModule({ +export const appConfig: ApplicationConfig = { providers: [provideNzI18n(en_US)] -}) -export class App1Module {} +}; ``` Of course, you can also use runtime changes: @@ -75,19 +74,16 @@ switchLanguage() { ## ALAIN_I18N_TOKEN -`@delon/*` class library has many data interface properties with the _i18n_ typeface (for example: `page-header`, `st` column description, `Menu` menu data, etc.) when you want the data for these components. When the interface can dynamically switch automatically according to the Key value in the current language, you also need to define a self-implementation service interface for `ALAIN_I18N_TOKEN` (for example: [I18NService](https://github.com/ng-alain/ng-alain/blob) /master/src/app/core/i18n/i18n.service.ts)) and register under the root module. +`@delon/*` class library has many data interface properties with the _i18n_ typeface (for example: `page-header`, `st` column description, `Menu` menu data, etc.) when you want the data for these components. When the interface can dynamically switch automatically according to the Key value in the current language, you also need to define a self-implementation service interface for `ALAIN_I18N_TOKEN` (for example: [I18NService](https://github.com/ng-alain/ng-alain/blob) /master/src/app/core/i18n/i18n.service.ts)) and register under the `app.config.ts` file. ```ts -import { ALAIN_I18N_TOKEN } from '@delon/theme'; import { I18NService } from '@core'; -@NgModule({ - ... +export const appConfig: ApplicationConfig = { providers: [ - { provide: ALAIN_I18N_TOKEN, useClass: I18NService, multi: false } + provideAlain({ config: alainConfig, defaultLang, i18nClass: I18NService }), ] -}) -export class AppModule {} +}; ``` ### i18n pipe @@ -140,59 +136,54 @@ In order to make language uniformity, NG-ALAIN provides a simple unified configu #### Chinese Version ```ts -// #region i18n import { default as ngLang } from '@angular/common/locales/zh'; import { provideNzI18n, zh_CN as zorroLang } from 'ng-zorro-antd/i18n'; import { DELON_LOCALE, zh_CN as delonLang } from '@delon/theme'; -const LANG = { +import { zhCN as dateLang } from 'date-fns/locale'; +import { I18NService } from '@core'; + +const defaultLang: AlainProvideLang = { abbr: 'zh', ng: ngLang, zorro: zorroLang, + date: dateLang, delon: delonLang, }; -// register angular -import { registerLocaleData } from '@angular/common'; -registerLocaleData(LANG.ng, LANG.abbr); -const LANG_PROVIDES = [ - { provide: LOCALE_ID, useValue: LANG.abbr }, - provideNzI18n(LANG.zorro), - { provide: DELON_LOCALE, useValue: LANG.delon }, -]; -// #endregion -@NgModule({ - providers: [...LANG_PROVIDES], -}) -export class AppModule {} +export const appConfig: ApplicationConfig = { + providers: [ + provideAlain({ config: alainConfig, defaultLang, i18nClass: I18NService }), + ] +}; ``` #### English version ```ts -// #region i18n import { default as ngLang } from '@angular/common/locales/en'; import { provideNzI18n, en_US as zorroLang } from 'ng-zorro-antd/i18n'; import { DELON_LOCALE, en_US as delonLang } from '@delon/theme'; +import { en_US as dateLang } from 'date-fns/locale'; const LANG = { abbr: 'en', ng: ngLang, zorro: zorroLang, delon: delonLang, }; -// register angular -import { registerLocaleData } from '@angular/common'; -registerLocaleData(LANG.ng, LANG.abbr); -const LANG_PROVIDES = [ - { provide: LOCALE_ID, useValue: LANG.abbr }, - provideNzI18n(LANG.zorro), - { provide: DELON_LOCALE, useValue: LANG.delon }, -]; -// #endregion -@NgModule({ - providers: [...LANG_PROVIDES], -}) -export class AppModule {} +const defaultLang: AlainProvideLang = { + abbr: 'en', + ng: ngLang, + zorro: zorroLang, + date: dateLang, + delon: delonLang, +}; + +export const appConfig: ApplicationConfig = { + providers: [ + provideAlain({ config: alainConfig, defaultLang, i18nClass: I18NService }), + ] +}; ``` ### Command Line diff --git a/docs/i18n.zh-CN.md b/docs/i18n.zh-CN.md index 561e3259f9..d5abe45a42 100644 --- a/docs/i18n.zh-CN.md +++ b/docs/i18n.zh-CN.md @@ -28,10 +28,9 @@ NG-ZORRO 国际化默认是中文版,例如默认为英文版本: ```ts import { en_US, provideNzI18n } from 'ng-zorro-antd/i18n'; -@NgModule({ +export const appConfig: ApplicationConfig = { providers: [provideNzI18n(en_US)] -}) -export class App1Module {} +}; ``` 当然,也可以使用运行时更改: @@ -75,19 +74,16 @@ switchLanguage() { ## ALAIN_I18N_TOKEN -`@delon/*` 类库有许多带有 _i18n_ 字样的数据接口属性(例如:`page-header`、`st` 列描述、`Menu` 菜单数据等等),当你希望这些组件的数据接口能动态根据 Key 值按当前语言自动切换时,你还需要对 `ALAIN_I18N_TOKEN` 定义一个自实现服务接口(例如:[I18NService](https://github.com/ng-alain/ng-alain/blob/master/src/app/core/i18n/i18n.service.ts)),并在根模块下注册。 +`@delon/*` 类库有许多带有 _i18n_ 字样的数据接口属性(例如:`page-header`、`st` 列描述、`Menu` 菜单数据等等),当你希望这些组件的数据接口能动态根据 Key 值按当前语言自动切换时,你还需要对 `ALAIN_I18N_TOKEN` 定义一个自实现服务接口(例如:[I18NService](https://github.com/ng-alain/ng-alain/blob/master/src/app/core/i18n/i18n.service.ts)),并在 `app.config.ts` 下注册。 ```ts -import { ALAIN_I18N_TOKEN } from '@delon/theme'; import { I18NService } from '@core'; -@NgModule({ - ... +export const appConfig: ApplicationConfig = { providers: [ - { provide: ALAIN_I18N_TOKEN, useClass: I18NService, multi: false } + provideAlain({ config: alainConfig, defaultLang, i18nClass: I18NService }), ] -}) -export class AppModule {} +}; ``` ### i18n管道 @@ -140,59 +136,54 @@ export class I18nTestComponent { #### 中文版 ```ts -// #region i18n import { default as ngLang } from '@angular/common/locales/zh'; import { provideNzI18n, zh_CN as zorroLang } from 'ng-zorro-antd/i18n'; import { DELON_LOCALE, zh_CN as delonLang } from '@delon/theme'; -const LANG = { +import { zhCN as dateLang } from 'date-fns/locale'; +import { I18NService } from '@core'; + +const defaultLang: AlainProvideLang = { abbr: 'zh', ng: ngLang, zorro: zorroLang, + date: dateLang, delon: delonLang, }; -// register angular -import { registerLocaleData } from '@angular/common'; -registerLocaleData(LANG.ng, LANG.abbr); -const LANG_PROVIDES = [ - { provide: LOCALE_ID, useValue: LANG.abbr }, - provideNzI18n(LANG.zorro), - { provide: DELON_LOCALE, useValue: LANG.delon }, -]; -// #endregion -@NgModule({ - providers: [...LANG_PROVIDES], -}) -export class AppModule {} +export const appConfig: ApplicationConfig = { + providers: [ + provideAlain({ config: alainConfig, defaultLang, i18nClass: I18NService }), + ] +}; ``` #### 英文版 ```ts -// #region i18n import { default as ngLang } from '@angular/common/locales/en'; import { provideNzI18n, en_US as zorroLang } from 'ng-zorro-antd/i18n'; import { DELON_LOCALE, en_US as delonLang } from '@delon/theme'; +import { en_US as dateLang } from 'date-fns/locale'; const LANG = { abbr: 'en', ng: ngLang, zorro: zorroLang, delon: delonLang, }; -// register angular -import { registerLocaleData } from '@angular/common'; -registerLocaleData(LANG.ng, LANG.abbr); -const LANG_PROVIDES = [ - { provide: LOCALE_ID, useValue: LANG.abbr }, - provideNzI18n(LANG.zorro), - { provide: DELON_LOCALE, useValue: LANG.delon }, -]; -// #endregion -@NgModule({ - providers: [...LANG_PROVIDES], -}) -export class AppModule {} +const defaultLang: AlainProvideLang = { + abbr: 'en', + ng: ngLang, + zorro: zorroLang, + date: dateLang, + delon: delonLang, +}; + +export const appConfig: ApplicationConfig = { + providers: [ + provideAlain({ config: alainConfig, defaultLang, i18nClass: I18NService }), + ] +}; ``` ### 命令行 diff --git a/docs/module.en-US.md b/docs/module.en-US.md index 5faa7bbe9d..0e8c327795 100644 --- a/docs/module.en-US.md +++ b/docs/module.en-US.md @@ -36,7 +36,6 @@ NG-ZORRO, @delon/abc, @delon/chart, etc. have changed from all import to on-dema + Angular Module: `BrowserModule`, `BrowserAnimationsModule`, `HttpClientModule` + `AlainThemeModule` Theme system -+ `DelonMockModule` Mock data + `AlainAuthModule` User authentication module + `AlainACLModule` Privilege module + Internationalization module diff --git a/docs/module.zh-CN.md b/docs/module.zh-CN.md index aa3a3562b8..9c2f01dbcb 100644 --- a/docs/module.zh-CN.md +++ b/docs/module.zh-CN.md @@ -36,7 +36,6 @@ NG-ZORRO、@delon/abc、@delon/chart 等从版本11开始由一次性导入改 + Angular 模块:`BrowserModule`、`BrowserAnimationsModule`、`HttpClientModule` + `AlainThemeModule` 主题系统 -+ `DelonMockModule` Mock数据 + `AlainAuthModule` 用户认证模块 + `AlainACLModule` 权限模块 + 国际化模块 diff --git a/docs/new-component.en-US.md b/docs/new-component.en-US.md index 9ef4eff165..061e81e036 100644 --- a/docs/new-component.en-US.md +++ b/docs/new-component.en-US.md @@ -47,7 +47,9 @@ import { Component, Input } from '@angular/core'; template: `
-
{{ desc }}
+ @if (desc) { +
{{ desc }}
+ }
`, styleUrls: [ './index.less' ] diff --git a/docs/new-component.zh-CN.md b/docs/new-component.zh-CN.md index ca2b899a21..5c77b94da6 100644 --- a/docs/new-component.zh-CN.md +++ b/docs/new-component.zh-CN.md @@ -47,7 +47,9 @@ import { Component, Input } from '@angular/core'; template: `
-
{{ desc }}
+ @if (desc) { +
{{ desc }}
+ }
`, styleUrls: [ './index.less' ] diff --git a/docs/ssr.md b/docs/ssr.md index 74a1ef962a..5b1c4f3391 100644 --- a/docs/ssr.md +++ b/docs/ssr.md @@ -77,7 +77,7 @@ export class AuthStorageStore implements IStore { ```diff const alainProvides = [ - { provide: ALAIN_CONFIG, useValue: alainConfig }, + provideAlainConfig(alainConfig) + { provide: DA_STORE_TOKEN, useClass: AuthStorageStore }, ]; ``` diff --git a/docs/upgrade-v16.en-US.md b/docs/upgrade-v16.en-US.md deleted file mode 100644 index 4037d2b3df..0000000000 --- a/docs/upgrade-v16.en-US.md +++ /dev/null @@ -1,25 +0,0 @@ ---- -order: 1000 -type: Basic -title: Upgrade to version 16.0 -hot: true ---- - -> This guide applies to the current version ng-alain >= `15`; -> If you encounter problems during the upgrade process, feel free to comment here. -> If you find any errors in this guide, please point out -> Or you have encountered a new problem and solved it, welcome to comment here. - -## Before upgrade - -1. Make sure `Node.js` >= `16.10.0`. -2. Create a new branch, or use other methods to back up the current project. -3. Delete the `package-lock.json` or `yarn.lock` file. - -### 1.Upgrade dependencies - -- Upgrade Angular to 15.x version, Run `ng update @angular/core@16 @angular/cli@16 @angular-eslint/schematics@16 ng-zorro-antd@16 ng-alain@16`. -- _Run `ng update @angular/cdk@16`, if you have used `@angular/cdk`._ -- **If any warning messages appear in the console, follow the prompts to modify the corresponding code.** - -> NG-ALAIN scaffolding upgrade all change files, please refer to: [#2394](https://github.com/ng-alain/ng-alain/pull/2394/files). diff --git a/docs/upgrade-v16.zh-CN.md b/docs/upgrade-v16.zh-CN.md deleted file mode 100644 index 9103fed256..0000000000 --- a/docs/upgrade-v16.zh-CN.md +++ /dev/null @@ -1,27 +0,0 @@ ---- -order: 1000 -type: Basic -title: 升级到 16.0 版本 -hot: true ---- - -> 本指南适用于当前版本 ng-alain >= `15` ; -> 如果在升级过程中遇到问题,欢迎提出。提问前请阅读 [如何向开源社区提问题](https://github.com/seajs/seajs/issues/545) -> 如果发现本指南存在遗漏/错误,请指出! -> 或者你遇到了新的问题并解决了,欢迎补充! - -## 开始之前 - -1. 首先确保你 `Node.js` >= `16.10.0` -2. 创建新的分支,或者使用其他方式备份当前项目 -3. 删除项目下 `package-lock.json` 或 `yarn.lock` 文件 - -## 升级步骤 - -### 升级相关依赖 - -- 将项目升级到 Angular 15 运行 `ng update @angular/core@16 @angular/cli@16 @angular-eslint/schematics@16 ng-zorro-antd@16 ng-alain@16`。 -- _如果你有单独使用 `@angular/cdk` 请执行 `ng update @angular/cdk@16`_ -- **如果控制台出现警告消息请按提示修改对应代码** - -> NG-ALAIN脚手架升级全部变更文件,请参考:[#2394](https://github.com/ng-alain/ng-alain/pull/2394/files)。 diff --git a/docs/upgrade-v17.en-US.md b/docs/upgrade-v17.en-US.md new file mode 100644 index 0000000000..5b2c28a9cb --- /dev/null +++ b/docs/upgrade-v17.en-US.md @@ -0,0 +1,52 @@ +--- +order: 1000 +type: Basic +title: Upgrade to version 17.0 +hot: true +--- + +> This guide applies to the current version ng-alain >= `16`; +> If you encounter problems during the upgrade process, feel free to comment here. +> If you find any errors in this guide, please point out +> Or you have encountered a new problem and solved it, welcome to comment here. + + +## Before upgrade + +1. Make sure `Node.js` >= `18.13.0`. +2. Create a new branch, or use other methods to back up the current project. +3. Delete the `package-lock.json` or `yarn.lock` file. + +### 1.Upgrade dependencies + +- Upgrade Angular to 16.x version, Run `ng update @angular/core@17 @angular/cli@17 @angular-eslint/schematics@17 ng-zorro-antd@17 ng-alain@17`. +- _Run `ng update @angular/cdk@17`, if you have used `@angular/cdk`._ +- **If any warning messages appear in the console, follow the prompts to modify the corresponding code.** + +> NG-ALAIN scaffolding upgrade all change files, please refer to: [#2394](https://github.com/ng-alain/ng-alain/pull/2394/files). + +## Optional + +The optional part of the work is centered around Standalone. Currently, NG-ALAIN will reserve some DEMO pages using the NgModule writing method. [#2442](https://github.com/ng-alain/ng-alain/pull/2442/files) contains all changes in this PR. + +### Upgrade to Standalone + +- Use `bootstrapApplication` instead of `platformBrowserDynamic` +- Use `app.config.ts` instead of `global-config.module.ts`, `app.module.ts` + +The `layout`, `dashboard`, `passport` have been rewritten using standalone and can be modified according to [#2442](https://github.com/ng-alain/ng-alain/pull/2442/files) changes. + +### DI configuration + +- NG-ALAIN removes all DI configuration modes such as `Module. forRoot`. Of course, for compatibility, the `NgModule` writing method is still retained. +- All DI configurations are maintained by `provide*`, see `app.config.ts` for more details +- If you use a third party but does not provide a similar `provide` writing method, you need to transition through `importProvidersFrom` + +### HTTP interceptor + +- Angular 17 has strict requirements for the order of interceptors to be visible. This upgrade has all been modified to the `HttpInterceptorFn` writing method (see `provideHttpClient` for details) +- If you upgrade from NG-ALAIN 16, `withInterceptorsFromDi()` will be imported by default to be compatible with the `HTTP_INTERCEPTORS` writing method, but it is recommended to upgrade to the new writing method as much as possible, please refer to [#2442](https://github.com/ng-alain/ng-alain/pull/2442/files) + +### About `@delon/form` + +- Convert all widgets with low frequency into optional imports. All widgets will be automatically imported during this upgrade to ensure compatibility. diff --git a/docs/upgrade-v17.zh-CN.md b/docs/upgrade-v17.zh-CN.md new file mode 100644 index 0000000000..3a049cca09 --- /dev/null +++ b/docs/upgrade-v17.zh-CN.md @@ -0,0 +1,53 @@ +--- +order: 1000 +type: Basic +title: 升级到 17.0 版本 +hot: true +--- + +> 本指南适用于当前版本 ng-alain >= `16` ; +> 如果在升级过程中遇到问题,欢迎提出。提问前请阅读 [如何向开源社区提问题](https://github.com/seajs/seajs/issues/545) +> 如果发现本指南存在遗漏/错误,请指出! +> 或者你遇到了新的问题并解决了,欢迎补充! + +## 开始之前 + +1. 首先确保你 `Node.js` >= `18.13.0` +2. 创建新的分支,或者使用其他方式备份当前项目 +3. 删除项目下 `package-lock.json` 或 `yarn.lock` 文件 + +## 升级步骤 + +### 升级相关依赖 + +- 将项目升级到 Angular 16 运行 `ng update @angular/core@17 @angular/cli@17 @angular-eslint/schematics@17 ng-zorro-antd@17 ng-alain@17`。 +- _如果你有单独使用 `@angular/cdk` 请执行 `ng update @angular/cdk@17`_ +- **如果控制台出现警告消息请按提示修改对应代码** + +> NG-ALAIN脚手架升级全部变更文件,请参考:[#2394](https://github.com/ng-alain/ng-alain/pull/2394/files)。 + +## 可选部分 + +可选部分的主要工作都是围绕 Standalone 展开,目前 NG-ALAIN 会保留部分DEMO页使用 NgModule 写法。[#2442](https://github.com/ng-alain/ng-alain/pull/2442/files) 包含着这部分变动的所有内容。 + +### 升级为 Standalone + +- 使用 `bootstrapApplication` 替代 `platformBrowserDynamic` +- 使用 `app.config.ts` 替代 `global-config.module.ts`, `app.module.ts` + +其中 `layout`, `dashboard`, `passport` 目录采用 standalone 写法,可根据 [#2442](https://github.com/ng-alain/ng-alain/pull/2442/files) 变动自行修改。 + +### DI配置 + +- NG-ALAIN 移除掉所有 `Module. forRoot` 之类的 DI 配置模式,当然为了兼容,依然保留 `NgModule` 写法 +- 所有DI配置都是 `provide*` 来维护,更多细节见 `app.config.ts` +- 如果您使用第三方但不提供类似 `provide` 写法时,只需要借助 `importProvidersFrom` 过渡 + +### HTTP拦截器 + +- Angular 17 对拦截器顺序可见有严格的要求,本次升级已经全部修改为 `HttpInterceptorFn` 写法(细节见 `provideHttpClient`) +- 若从 NG-ALAIN 16 升级默认为会导入 `withInterceptorsFromDi()` 用于兼容 `HTTP_INTERCEPTORS` 写法,但建议尽可能升级新的写法,可参考 [#2442](https://github.com/ng-alain/ng-alain/pull/2442/files) + +### 关于 `@delon/form` + +- 将小部件频率较低的全部转化为可选导入,本次升级时会自动导入所有小部件来保证兼容 diff --git a/package.json b/package.json index e2a21a1b1b..c44c55a8de 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "delon", - "version": "16.4.2", + "version": "17.0.1", "description": "Delon is a set of essential modules for NG-ALAIN.", "keywords": [ "delon", @@ -21,7 +21,7 @@ }, "homepage": "https://ng-alain.com", "engines": { - "node": "^18.10.0" + "node": "^18.13.0" }, "scripts": { "ng-high-memory": "node --max_old_space_size=8000 ./node_modules/@angular/cli/bin/ng", @@ -40,7 +40,6 @@ "analyze:view": "source-map-explorer src/dist/browser/*.js", "site:gen": "node scripts/site/main init && ng-alain-plugin-theme -t=themeCss && ng-alain-plugin-theme -t=colorLess", "site:build": "npm run site:gen && ng b site && npm run site:ngsw && npm run site:minify && npm run site:sitemap && npm run site:helper", - "site:build:ssr": "npm run site:gen && npm run ssr:prerender && npm run site:ngsw && npm run site:minify && npm run site:sitemap && npm run site:helper", "site:helper": "bash scripts/ci/helper.sh", "site:minify": "node scripts/build/minify.js", "site:sitemap": "node scripts/build/sitemap.js", @@ -49,26 +48,25 @@ "pre-publish": "node scripts/publish/publish.js && node scripts/publish/publish-scaffold.js", "publish:next": "bash scripts/publish/publish.sh -next", "publish": "bash scripts/publish/publish.sh", - "sync-scaffold-version": "node scripts/publish/sync-scaffold-version.js", - "ssr": "npm run ng-high-memory run site:serve-ssr", - "ssr:serve": "node src/dist/server/main.js", - "ssr:prerender": "npm run ng-high-memory run site:prerender" + "sync-scaffold-version": "node scripts/publish/sync-scaffold-version.js" }, "dependencies": { - "@angular/animations": "^16.2.0", - "@angular/common": "^16.2.0", - "@angular/compiler": "^16.2.0", - "@angular/core": "^16.2.0", - "@angular/forms": "^16.2.0", - "@angular/platform-browser": "^16.2.0", - "@angular/platform-browser-dynamic": "^16.2.0", - "@angular/router": "^16.2.0", + "@angular/animations": "^17.0.0", + "@angular/common": "^17.0.0", + "@angular/compiler": "^17.0.0", + "@angular/core": "^17.0.0", + "@angular/forms": "^17.0.0", + "@angular/platform-browser": "^17.0.0", + "@angular/platform-browser-dynamic": "^17.0.0", + "@angular/router": "^17.0.0", "rxjs": "~7.8.0", "tslib": "^2.3.0", - "zone.js": "~0.13.0", - "@angular/service-worker": "^16.2.0", - "@angular/platform-server": "^16.2.0", - "@angular/elements": "^16.2.0", + "zone.js": "~0.14.2", + "@angular/service-worker": "^17.0.0", + "@angular/platform-server": "^17.0.0", + "@angular/ssr": "^17.0.0", + "express": "^4.18.2", + "@angular/elements": "^17.0.0", "@antv/data-set": "^0.11.8", "@antv/g2": "^5.1.6", "echarts": "^5.4.3", @@ -78,55 +76,53 @@ "ajv-formats": "^2.1.1", "extend": "^3.0.2", "file-saver": "^2.0.5", - "ng-github-button": "^16.0.0", - "ng-zorro-antd": "^16.2.2", - "ngx-color": "~9.0.0", - "ngx-countdown": "^16.0.0", - "ngx-highlight-js": "^16.1.0", - "ngx-tinymce": "^16.0.0", + "ng-github-button": "^17.0.0", + "ng-zorro-antd": "^17.0.1", + "@angular/cdk": "^17.0.0", + "ngx-countdown": "^17.0.0", + "ngx-highlight-js": "^17.0.0", + "ngx-tinymce": "^17.0.0", "qrious": "^4.0.2", "@webcomponents/custom-elements": "^1.6.0", "aos": "^3.0.0-beta.6", - "@ng-util/monaco-editor": "^16.0.0", - "@nguniversal/express-engine": "^16.2.0", - "express": "^4.18.2", + "@ng-util/monaco-editor": "^17.0.0", "isutf8": "^4.0.0", - "@github/hotkey": "^2.0.1" + "@github/hotkey": "^2.3.0", + "ng-antd-color-picker": "^0.0.2" }, "devDependencies": { - "@angular-devkit/build-angular": "^16.2.0", - "@angular/cli": "~16.2.0", - "@angular/compiler-cli": "^16.2.0", - "@types/jasmine": "~4.3.0", - "jasmine-core": "~4.6.0", + "@angular-devkit/build-angular": "^17.0.0", + "@angular/cli": "^17.0.0", + "@angular/compiler-cli": "^17.0.0", + "@types/jasmine": "~5.1.0", + "jasmine-core": "~5.1.0", "karma": "~6.4.0", "karma-chrome-launcher": "~3.2.0", "karma-coverage": "~2.2.0", "karma-jasmine": "~5.1.0", "karma-jasmine-html-reporter": "~2.1.0", - "typescript": "~5.1.3", - "codelyzer": "^6.0.2", + "typescript": "~5.2.2", + "jasmine": "^5.1.0", "jasmine-spec-reporter": "^7.0.0", - "protractor": "~7.0.0", "ts-node": "~10.9.1", - "@angular/language-service": "^16.2.0", - "@types/jasminewd2": "~2.0.10", + "@angular/language-service": "^17.0.0", + "@types/jasminewd2": "~2.0.13", "karma-junit-reporter": "^2.0.1", "karma-spec-reporter": "0.0.36", "karma-viewport": "^1.0.9", - "@types/aos": "^3.0.4", - "@types/file-saver": "^2.0.5", - "@types/deep-extend": "^0.6.0", - "@types/extend": "^3.0.1", - "@types/mockjs": "^1.0.7", + "@types/aos": "^3.0.7", + "@types/file-saver": "^2.0.7", + "@types/deep-extend": "^0.6.2", + "@types/extend": "^3.0.4", + "@types/mockjs": "^1.0.10", "@types/parse5": "^7.0.0", "karma-summary-reporter": "^3.1.1", "karma-parallel": "^0.3.1", "chalk": "^5.3.0", "codecov": "^3.8.3", - "conventional-changelog-cli": "^4.0.0", + "conventional-changelog-cli": "^4.1.0", "fs-extra": "^11.1.1", - "@types/fs-extra": "^11.0.1", + "@types/fs-extra": "^11.0.4", "husky": "^8.0.3", "jsonml.js": "^0.1.0", "klaw-sync": "^6.0.0", @@ -136,12 +132,12 @@ "mark-twain": "^2.0.3", "mockjs": "^1.1.0", "mustache": "^4.2.0", - "ng-packagr": "^16.2.1", + "ng-packagr": "^17.0.0", "parse5": "^7.1.2", - "prettier": "^3.0.2", + "prettier": "^3.1.0", "readline-sync": "^1.4.10", "stream": "0.0.2", - "stylelint": "^15.10.3", + "stylelint": "^15.11.0", "stylelint-config-standard": "^34.0.0", "stylelint-declaration-block-no-ignored-properties": "^2.7.0", "stylelint-config-clean-order": "^5.2.0", @@ -151,32 +147,30 @@ "plyr": "^3.7.8", "screenfull": "^6.0.2", "less-bundle-promise": "^1.0.11", - "ng-alain-codelyzer": "^0.0.1", "ng-alain-sts": "^0.0.2", - "ng-alain-plugin-theme": "^15.0.1", + "ng-alain-plugin-theme": "^16.0.0", "tsconfig-paths": "^4.2.0", - "@nguniversal/builders": "^16.2.0", "@types/express": "^4.17.17", + "@types/node": "^18.18.0", "html-minifier-terser": "^7.2.0", "terser": "^5.19.2", - "@commitlint/cli": "^17.7.1", - "@commitlint/config-angular": "^17.7.0", - "@angular-eslint/builder": "~16.1.1", - "@angular-eslint/eslint-plugin": "~16.1.1", - "@angular-eslint/eslint-plugin-template": "~16.1.1", - "@angular-eslint/schematics": "~16.1.1", - "@angular-eslint/template-parser": "~16.1.1", - "@typescript-eslint/eslint-plugin": "~6.4.1", - "@typescript-eslint/parser": "~6.4.1", - "eslint": "^8.48.0", + "@commitlint/cli": "^17.8.1", + "@commitlint/config-angular": "^17.8.1", + "@angular-eslint/builder": "^17.0.0", + "@angular-eslint/eslint-plugin": "^17.0.0", + "@angular-eslint/eslint-plugin-template": "^17.0.0", + "@angular-eslint/schematics": "^17.0.0", + "@angular-eslint/template-parser": "^17.0.0", + "@typescript-eslint/eslint-plugin": "^6.10.0", + "@typescript-eslint/parser": "^6.10.0", + "eslint": "^8.53.0", "eslint-config-prettier": "~9.0.0", - "eslint-plugin-import": "~2.28.1", - "eslint-plugin-jsdoc": "~46.5.0", + "eslint-plugin-import": "~2.29.0", + "eslint-plugin-jsdoc": "~46.5.1", "eslint-plugin-prefer-arrow": "~1.2.3", - "eslint-plugin-prettier": "~5.0.0", - "eslint-plugin-deprecation": "~1.5.0", + "eslint-plugin-prettier": "~5.0.1", + "eslint-plugin-deprecation": "~2.0.0", "lint-staged": "^14.0.1", - "raw-loader": "^4.0.2", "swagger-typescript-api": "^12.0.4", "sitemap": "^7.1.1" }, diff --git a/packages/abc/auto-focus/auto-focus.directive.spec.ts b/packages/abc/auto-focus/auto-focus.directive.spec.ts index 9c898ed0ef..814282c85c 100644 --- a/packages/abc/auto-focus/auto-focus.directive.spec.ts +++ b/packages/abc/auto-focus/auto-focus.directive.spec.ts @@ -42,9 +42,11 @@ describe('abc: auto-focus', () => { @Component({ template: ` -
- -
+ @if (showInput) { +
+ +
+ } ` }) class TestComponent { diff --git a/packages/abc/auto-focus/demo/simple.md b/packages/abc/auto-focus/demo/simple.md index 422015c499..97ef3a8ba3 100644 --- a/packages/abc/auto-focus/demo/simple.md +++ b/packages/abc/auto-focus/demo/simple.md @@ -20,9 +20,11 @@ import { Component } from '@angular/core'; selector: 'app-demo', template: ` -
- -
+ @if (showInput) { +
+ +
+ } `, }) export class DemoComponent { diff --git a/packages/abc/avatar-list/avatar-list.component.html b/packages/abc/avatar-list/avatar-list.component.html index 0e724cbbeb..f39eacc01e 100644 --- a/packages/abc/avatar-list/avatar-list.component.html +++ b/packages/abc/avatar-list/avatar-list.component.html @@ -1,17 +1,23 @@
    -
  • - - -
  • -
  • - -
  • + @for (i of items; track $index) { +
  • + @if (i.tips) { + + } @else { + + } +
  • + } + @if (exceedCount > 0) { +
  • + +
  • + }
diff --git a/packages/abc/cell/cell-host.directive.ts b/packages/abc/cell/cell-host.directive.ts index 1b2069cc23..3b4d140c9d 100644 --- a/packages/abc/cell/cell-host.directive.ts +++ b/packages/abc/cell/cell-host.directive.ts @@ -6,7 +6,8 @@ import { CellService } from './cell.service'; import { CellWidgetData } from './cell.types'; @Directive({ - selector: '[cell-widget-host]' + selector: '[cell-widget-host]', + standalone: true }) export class CellHostDirective implements OnInit { @Input() data!: CellWidgetData; diff --git a/packages/abc/cell/cell.component.ts b/packages/abc/cell/cell.component.ts index 6f99e839f8..3c2e6293d8 100644 --- a/packages/abc/cell/cell.component.ts +++ b/packages/abc/cell/cell.component.ts @@ -1,3 +1,4 @@ +import { NgTemplateOutlet } from '@angular/common'; import { ChangeDetectionStrategy, ChangeDetectorRef, @@ -13,6 +14,7 @@ import { SimpleChange, ViewEncapsulation } from '@angular/core'; +import { FormsModule } from '@angular/forms'; import type { SafeValue } from '@angular/platform-browser'; import { Router } from '@angular/router'; import { Subscription } from 'rxjs'; @@ -20,9 +22,16 @@ import { Subscription } from 'rxjs'; import { updateHostClass } from '@delon/util/browser'; import { BooleanInput, InputBoolean } from '@delon/util/decorator'; import { WINDOW } from '@delon/util/token'; +import { NzBadgeModule } from 'ng-zorro-antd/badge'; +import { NzCheckboxModule } from 'ng-zorro-antd/checkbox'; import type { NzSafeAny } from 'ng-zorro-antd/core/types'; +import { NzIconModule } from 'ng-zorro-antd/icon'; import { NzImage, NzImageService } from 'ng-zorro-antd/image'; +import { NzRadioModule } from 'ng-zorro-antd/radio'; +import { NzTagModule } from 'ng-zorro-antd/tag'; +import { NzToolTipModule } from 'ng-zorro-antd/tooltip'; +import { CellHostDirective } from './cell-host.directive'; import { CellService } from './cell.service'; import type { CellDefaultText, CellOptions, CellTextResult, CellValue, CellWidgetData } from './cell.types'; @@ -30,69 +39,90 @@ import type { CellDefaultText, CellOptions, CellTextResult, CellValue, CellWidge selector: 'cell, [cell]', template: ` - - - - - - - - - - - - - - - - {{ _unit }} - - + @switch (safeOpt.type) { + @case ('checkbox') { + + } + @case ('radio') { + + } + @case ('link') { + + } + @case ('tag') { + + + + } + @case ('badge') { + + } + @case ('widget') { + + } + @case ('img') { + @for (i of $any(_text); track $index) { + + } + } + @default { + @if (isText) { + + } @else { + + } + @if (_unit) { + {{ _unit }} + } + } + } - {{ safeOpt.default?.text }} - - + @if (showDefault) { + {{ safeOpt.default?.text }} + } @else { + @if (safeOpt.tooltip) { + + + + } @else { - - + } + } - + @if (loading) { + + } @else { + + } `, exportAs: 'cell', preserveWhitespaces: false, changeDetection: ChangeDetectionStrategy.OnPush, - encapsulation: ViewEncapsulation.None + encapsulation: ViewEncapsulation.None, + standalone: true, + imports: [ + FormsModule, + NgTemplateOutlet, + NzCheckboxModule, + NzRadioModule, + NzIconModule, + NzTagModule, + NzBadgeModule, + NzToolTipModule, + CellHostDirective + ] }) export class CellComponent implements OnChanges, OnDestroy { static ngAcceptInputType_loading: BooleanInput; @@ -151,7 +181,7 @@ export class CellComponent implements OnChanges, OnDestroy { private setClass(): void { const { el, renderer } = this; - const { renderType, size } = this.safeOpt; + const { renderType, size, type } = this.safeOpt; updateHostClass(el.nativeElement, renderer, { [`cell`]: true, [`cell__${renderType}`]: renderType != null, @@ -160,7 +190,7 @@ export class CellComponent implements OnChanges, OnDestroy { [`cell__has-default`]: this.showDefault, [`cell__disabled`]: this.disabled }); - el.nativeElement.dataset.type = this.safeOpt.type; + el.nativeElement.setAttribute('data-type', `${type}`); } ngOnChanges(changes: { [p in keyof CellComponent]?: SimpleChange }): void { diff --git a/packages/abc/cell/cell.module.ts b/packages/abc/cell/cell.module.ts index 67f835083d..7d4590c983 100644 --- a/packages/abc/cell/cell.module.ts +++ b/packages/abc/cell/cell.module.ts @@ -25,9 +25,10 @@ const COMPS = [CellComponent]; NzTagModule, NzToolTipModule, NzIconModule, - NzImageModule + NzImageModule, + ...COMPS, + CellHostDirective ], - declarations: [...COMPS, CellHostDirective], exports: COMPS }) export class CellModule {} diff --git a/packages/abc/cell/cell.spec.ts b/packages/abc/cell/cell.spec.ts index 53aba8f159..c61a163a0b 100644 --- a/packages/abc/cell/cell.spec.ts +++ b/packages/abc/cell/cell.spec.ts @@ -16,6 +16,7 @@ import { CellComponent } from './cell.component'; import { CellModule } from './cell.module'; import { CellService } from './cell.service'; import { CellFuValue, CellOptions, CellWidgetData } from './cell.types'; +import { provideCellWidgets } from './provide'; const DATE = new Date(2022, 0, 1, 1, 2, 3); @@ -292,6 +293,19 @@ describe('abc: cell', () => { }); }); + describe('[widget]', () => { + it('via provideCellWidgets', () => { + TestBed.configureTestingModule({ + imports: [CellModule, NoopAnimationsModule], + declarations: [TestComponent, TestWidget], + providers: [provideCellWidgets({ KEY: TestWidget.KEY, type: TestWidget })] + }); + ({ fixture, dl, context } = createTestContext(TestComponent)); + page = new PageObject(); + page.update('1', { widget: { key: TestWidget.KEY, data: 'new data' } }).check('1-new data'); + }); + }); + class PageObject { update(value: unknown, options?: CellOptions): this { context.value = value; diff --git a/packages/abc/cell/demo/simple.md b/packages/abc/cell/demo/simple.md index c634c4ddeb..85f00a4fb9 100644 --- a/packages/abc/cell/demo/simple.md +++ b/packages/abc/cell/demo/simple.md @@ -26,7 +26,9 @@ import { CellBadge, CellFuValue, CellOptions, CellRenderType } from '@delon/abc/ selector: 'app-demo', template: `
-
{{ i | json }} =>
+ @for (i of baseList; track $index) { +
{{ i | json }} =>
+ }
date-fn =>
mega =>
mask =>
@@ -107,10 +109,12 @@ import { CellBadge, CellFuValue, CellOptions, CellRenderType } from '@delon/abc/ default =>
-
- {{ i }} => - -
+ @for (i of typeList; track $index) { +
+ {{ i }} => + +
+ }
size => , , @@ -128,7 +132,9 @@ import { CellBadge, CellFuValue, CellOptions, CellRenderType } from '@delon/abc/
Async => - Again + @if (!asyncLoading) { + Again + }
Unit =>
Text Unit =>
diff --git a/packages/abc/cell/index.en-US.md b/packages/abc/cell/index.en-US.md index 47063de66d..d5efedd24a 100644 --- a/packages/abc/cell/index.en-US.md +++ b/packages/abc/cell/index.en-US.md @@ -62,7 +62,7 @@ Cell formatting is supported for multiple data types, and supports widget mode. - `enum` Enum - `widget` Custom widget -**Custom widget** +## Custom widget Just implement the `CellWidgetInstance` interface, for example: @@ -95,28 +95,14 @@ export class CellTestWidget implements CellWidgetInstance { `data` is a fixed parameter, including `value`, `options` configuration items. -Secondly, you also need to call `CellService.registerWidget` to register the widget; usually a new module will be built separately, for example: +Finally, register the widget through `provideCellWidgets` under `app.config.ts`, for example: ```ts -import { NgModule } from '@angular/core'; - -import { CellService } from '@delon/abc/cell'; - -import { CellTestWidget } from './test'; -import { SharedModule } from '../shared.module'; - -export const CELL_WIDGET_COMPONENTS = [CellTestWidget]; - -@NgModule({ - declarations: CELL_WIDGET_COMPONENTS, - imports: [SharedModule], - exports: CELL_WIDGET_COMPONENTS -}) -export class CellWidgetModule { - constructor(srv: CellService) { - srv.registerWidget(CellTestWidget.KEY, CellTestWidget); - } +export const appConfig: ApplicationConfig = { + providers: [ + provideCellWidgets( + { KEY: CellTestWidget.KEY, type: CellTestWidget } + ), + ] } ``` - -Finally, just register `CellWidgetModule` under the root module. diff --git a/packages/abc/cell/index.ts b/packages/abc/cell/index.ts index 397ff56e11..e16b303798 100644 --- a/packages/abc/cell/index.ts +++ b/packages/abc/cell/index.ts @@ -3,3 +3,4 @@ export * from './cell-host.directive'; export * from './cell.module'; export * from './cell.service'; export * from './cell.types'; +export * from './provide'; diff --git a/packages/abc/cell/index.zh-CN.md b/packages/abc/cell/index.zh-CN.md index 5767686247..49bf73a562 100644 --- a/packages/abc/cell/index.zh-CN.md +++ b/packages/abc/cell/index.zh-CN.md @@ -62,7 +62,7 @@ module: import { CellModule } from '@delon/abc/cell'; - `enum` 枚举转换 - `widget` 自定义小部件 -**自定义小部件** +## 自定义小部件 实现 `CellWidgetInstance` 接口即可,例如: @@ -95,28 +95,14 @@ export class CellTestWidget implements CellWidgetInstance { 其中 `data` 为固定参数,包含 `value`、`options` 配置项。 -其次,还需要调用 `CellService.registerWidget` 注册小部件;通常会单独构建一个新的模块,例如: +最后在 `app.config.ts` 下通过 `provideCellWidgets` 注册小部件,例如: ```ts -import { NgModule } from '@angular/core'; - -import { CellService } from '@delon/abc/cell'; - -import { CellTestWidget } from './test'; -import { SharedModule } from '../shared.module'; - -export const CELL_WIDGET_COMPONENTS = [CellTestWidget]; - -@NgModule({ - declarations: CELL_WIDGET_COMPONENTS, - imports: [SharedModule], - exports: CELL_WIDGET_COMPONENTS -}) -export class CellWidgetModule { - constructor(srv: CellService) { - srv.registerWidget(CellTestWidget.KEY, CellTestWidget); - } +export const appConfig: ApplicationConfig = { + providers: [ + provideCellWidgets( + { KEY: CellTestWidget.KEY, type: CellTestWidget } + ), + ] } ``` - -最后,将 `CellWidgetModule` 注册到根模块下即可。 diff --git a/packages/abc/cell/provide.ts b/packages/abc/cell/provide.ts new file mode 100644 index 0000000000..1fc429c6c5 --- /dev/null +++ b/packages/abc/cell/provide.ts @@ -0,0 +1,26 @@ +import { ENVIRONMENT_INITIALIZER, EnvironmentProviders, inject, makeEnvironmentProviders } from '@angular/core'; + +import type { NzSafeAny } from 'ng-zorro-antd/core/types'; + +import { CellService } from './cell.service'; + +export interface CellWidgetProvideConfig { + KEY: string; + type: NzSafeAny; +} + +/** + * Just only using Standalone widgets + */ +export function provideCellWidgets(...widgets: CellWidgetProvideConfig[]): EnvironmentProviders { + return makeEnvironmentProviders([ + { + provide: ENVIRONMENT_INITIALIZER, + multi: true, + useValue: () => { + const srv = inject(CellService); + widgets.forEach(widget => srv.registerWidget(widget.KEY, widget.type)); + } + } + ]); +} diff --git a/packages/abc/count-down/count-down.component.ts b/packages/abc/count-down/count-down.component.ts index 233ac2bc9a..fcac7c55aa 100644 --- a/packages/abc/count-down/count-down.component.ts +++ b/packages/abc/count-down/count-down.component.ts @@ -14,7 +14,9 @@ import { CountdownComponent, CountdownConfig, CountdownEvent } from 'ngx-countdo @Component({ selector: 'count-down', exportAs: 'countDown', - template: ``, + template: `@if (config) { + + }`, preserveWhitespaces: false, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None diff --git a/packages/abc/count-down/count-down.spec.ts b/packages/abc/count-down/count-down.spec.ts index d746d318e8..491e1d5082 100644 --- a/packages/abc/count-down/count-down.spec.ts +++ b/packages/abc/count-down/count-down.spec.ts @@ -50,12 +50,16 @@ describe('abc: count-down', () => { @Component({ template: ` -
- -
-
- -
+ @if (config) { +
+ +
+ } + @if (target) { +
+ +
+ } ` }) class TestComponent { diff --git a/packages/abc/date-picker/range-shortcut.component.ts b/packages/abc/date-picker/range-shortcut.component.ts index 8b997df8cf..414131d656 100644 --- a/packages/abc/date-picker/range-shortcut.component.ts +++ b/packages/abc/date-picker/range-shortcut.component.ts @@ -6,12 +6,9 @@ import { AlainDateRangePickerShortcutItem } from '@delon/util/config'; selector: '', template: ` - + @for (i of list; track $index) { + + } ` }) diff --git a/packages/abc/date-picker/range.directive.ts b/packages/abc/date-picker/range.directive.ts index c82c3e1e3a..f1458ddbd4 100644 --- a/packages/abc/date-picker/range.directive.ts +++ b/packages/abc/date-picker/range.directive.ts @@ -72,10 +72,12 @@ export class RangePickerDirective implements OnDestroy, AfterViewInit { @Host() @Optional() private nativeComp: NzRangePickerComponent, private vcr: ViewContainerRef ) { - assert( - !!nativeComp, - `It should be attached to nz-range-picker component, for example: ''` - ); + if (typeof ngDevMode === 'undefined' || ngDevMode) { + assert( + !!nativeComp, + `It should be attached to nz-range-picker component, for example: ''` + ); + } const cog = configSrv.merge('dataRange', { nzFormat: 'yyyy-MM-dd', nzAllowClear: true, diff --git a/packages/abc/down-file/demo/simple.md b/packages/abc/down-file/demo/simple.md index f8dd3807e8..3ba952a35b 100644 --- a/packages/abc/down-file/demo/simple.md +++ b/packages/abc/down-file/demo/simple.md @@ -19,17 +19,18 @@ import { Component } from '@angular/core'; @Component({ selector: 'app-demo', template: ` - + @for (i of fileTypes; track $index) { + + } `, }) export class DemoComponent { diff --git a/packages/abc/down-file/down-file.spec.ts b/packages/abc/down-file/down-file.spec.ts index 9df116ac8e..2349f68ecd 100644 --- a/packages/abc/down-file/down-file.spec.ts +++ b/packages/abc/down-file/down-file.spec.ts @@ -204,21 +204,22 @@ describe('abc: down-file', () => { @Component({ template: ` - + @for (i of fileTypes; track $index) { + + } ` }) class TestComponent { diff --git a/packages/abc/ellipsis/ellipsis.component.html b/packages/abc/ellipsis/ellipsis.component.html index dfdc9fa0b5..90c453c456 100644 --- a/packages/abc/ellipsis/ellipsis.component.html +++ b/packages/abc/ellipsis/ellipsis.component.html @@ -1,35 +1,42 @@
- + @if (tooltip) { + + +
+
+ } @else { -
-
+ }
- - - - +@switch (type) { + @case ('default') { + + } + @case ('length') { + {{ text }} - - - + } + @case ('line-clamp') { +
-
-
-
- - {{ linsWord }} -
-
- {{ text }} + } + @case ('line') { +
+
+ + {{ linsWord }} +
+
+ {{ text }} +
-
- + } +} diff --git a/packages/abc/exception/exception.component.html b/packages/abc/exception/exception.component.html index 711fe8a2e4..9896894978 100644 --- a/packages/abc/exception/exception.component.html +++ b/packages/abc/exception/exception.component.html @@ -8,8 +8,10 @@

- + @if (!hasCon) { + + }
diff --git a/packages/abc/footer-toolbar/footer-toolbar.component.html b/packages/abc/footer-toolbar/footer-toolbar.component.html index 8ebe9798f4..a3c8accd89 100644 --- a/packages/abc/footer-toolbar/footer-toolbar.component.html +++ b/packages/abc/footer-toolbar/footer-toolbar.component.html @@ -2,6 +2,8 @@ {{ extra }}
diff --git a/packages/abc/global-footer/global-footer.component.html b/packages/abc/global-footer/global-footer.component.html index b7cd71f70d..2e7e0fbb83 100644 --- a/packages/abc/global-footer/global-footer.component.html +++ b/packages/abc/global-footer/global-footer.component.html @@ -1,9 +1,15 @@ - +@if (links.length > 0 || items.length > 0) { + +} diff --git a/packages/abc/hotkey/hotkey.directive.ts b/packages/abc/hotkey/hotkey.directive.ts index 1ca9981d46..914f4d78d4 100644 --- a/packages/abc/hotkey/hotkey.directive.ts +++ b/packages/abc/hotkey/hotkey.directive.ts @@ -3,7 +3,7 @@ import { Directive, ElementRef, Input, NgZone, OnDestroy } from '@angular/core'; import { install, uninstall } from '@github/hotkey'; -@Directive({ selector: '[hotkey]' }) +@Directive({ selector: '[hotkey]', standalone: true }) export class HotkeyDirective implements OnDestroy { /** * Specify [hotkey format](https://github.com/github/hotkey#hotkey-string-format) diff --git a/packages/abc/hotkey/hotkey.module.ts b/packages/abc/hotkey/hotkey.module.ts index f63cfc3b1e..2aa068362e 100644 --- a/packages/abc/hotkey/hotkey.module.ts +++ b/packages/abc/hotkey/hotkey.module.ts @@ -5,7 +5,7 @@ import { HotkeyDirective } from './hotkey.directive'; const DIRECTIVES = [HotkeyDirective]; @NgModule({ - declarations: DIRECTIVES, + imports: DIRECTIVES, exports: DIRECTIVES }) export class HotkeyModule {} diff --git a/packages/abc/let/demo/async.md b/packages/abc/let/demo/async.md index a8d7f57fb4..21ddb9bc2f 100644 --- a/packages/abc/let/demo/async.md +++ b/packages/abc/let/demo/async.md @@ -23,13 +23,13 @@ import { NzSafeAny } from 'ng-zorro-antd/core/types'; @Component({ selector: 'app-demo', template: ` - + @if (timer$ !== null) {

Timer value: {{ time }}

Timer value: {{ time }}

Timer value: {{ time }}

-
+ } `, changeDetection: ChangeDetectionStrategy.OnPush }) diff --git a/packages/abc/let/let.directive.ts b/packages/abc/let/let.directive.ts index a9037f179b..24e72a0794 100644 --- a/packages/abc/let/let.directive.ts +++ b/packages/abc/let/let.directive.ts @@ -14,7 +14,7 @@ export class LetContext { } } -@Directive({ selector: '[let]' }) +@Directive({ selector: '[let]', standalone: true }) export class LetDirective { @Input({ required: true }) let!: T; diff --git a/packages/abc/let/let.module.ts b/packages/abc/let/let.module.ts index 2aa1707e90..b9199e5f04 100644 --- a/packages/abc/let/let.module.ts +++ b/packages/abc/let/let.module.ts @@ -5,7 +5,7 @@ import { LetDirective } from './let.directive'; const DIRECTIVES = [LetDirective]; @NgModule({ - declarations: DIRECTIVES, + imports: DIRECTIVES, exports: DIRECTIVES }) export class LetModule {} diff --git a/packages/abc/loading/demo/custom.md b/packages/abc/loading/demo/custom.md index bdba2b00dc..0a1171c027 100644 --- a/packages/abc/loading/demo/custom.md +++ b/packages/abc/loading/demo/custom.md @@ -20,7 +20,10 @@ import { LoadingCustom, LoadingService } from '@delon/abc/loading'; @Component({ selector: 'app-demo', - template: ` `, + template: ` + @for (i of customs; track $index) { + + }`, }) export class DemoComponent { customs: LoadingCustom[] = [ diff --git a/packages/abc/loading/loading.component.html b/packages/abc/loading/loading.component.html index 10a7170a14..2ac6e2a728 100644 --- a/packages/abc/loading/loading.component.html +++ b/packages/abc/loading/loading.component.html @@ -1,8 +1,18 @@ -
- - - -
-
-
-
{{ options.text }}
+@if (options.type! !== 'text') { +
+ @switch (options.type) { + @case ('spin') { + + } + @case ('icon') { + + } + @default { +
+ } + } +
+} +@if (options.text) { +
{{ options.text }}
+} diff --git a/packages/abc/lodop/demo/design.md b/packages/abc/lodop/demo/design.md index 4137ac9e76..5c146ab46c 100644 --- a/packages/abc/lodop/demo/design.md +++ b/packages/abc/lodop/demo/design.md @@ -21,37 +21,41 @@ import { NzMessageService } from 'ng-zorro-antd/message'; @Component({ selector: 'app-demo', template: ` - - 请先下载Lodop插件 - -
- - 标题 - - - - - - 费用 - - - - - - 打印内容 - - - - - - - - - - - - -
+ @if (error) { + + 请先下载Lodop插件 + + } + @if (lodop && !error) { +
+ + 标题 + + + + + + 费用 + + + + + + 打印内容 + + + + + + + + + + + + +
+ } `, }) export class DemoComponent { diff --git a/packages/abc/lodop/demo/print.md b/packages/abc/lodop/demo/print.md index 195a4c0f19..99baeb073a 100644 --- a/packages/abc/lodop/demo/print.md +++ b/packages/abc/lodop/demo/print.md @@ -21,62 +21,69 @@ import { NzMessageService } from 'ng-zorro-antd/message'; @Component({ selector: 'app-demo', template: ` - - - 请先下载Lodop插件, 安装后点击重试。 - - -
- - 打印服务器 - - -
- -
-
- -
-
-
-
- - 打印机 - - - - - - - - 纸张类型 - - - - - - - - 打印内容 - - - - - - - - - - -
+ @if (error) { + + + 请先下载Lodop插件, 安装后点击重试。 + + + } @else { +
+ + 打印服务器 + + +
+ +
+
+ +
+
+
+
+ + 打印机 + + + @for (name of pinters; track $index) { + + } + + + + + 纸张类型 + + + @for (name of papers; track $index) { + + } + + + + + 打印内容 + + + + + + + + + + +
+ } `, }) export class DemoComponent { diff --git a/packages/abc/notice-icon/notice-icon-tab.component.html b/packages/abc/notice-icon/notice-icon-tab.component.html index d81f604bdb..c748288e18 100644 --- a/packages/abc/notice-icon/notice-icon-tab.component.html +++ b/packages/abc/notice-icon/notice-icon-tab.component.html @@ -1,11 +1,17 @@ -
- not found -

- - {{ data.emptyText || locale.emptyText }} - -

-
+@if (data.list?.length === 0) { +
+ @if (data.emptyImage) { + not found + } +

+ + {{ data.emptyText || locale.emptyText }} + +

+
+} @else { + +} @@ -15,17 +21,23 @@ {{ item.title }} -
- {{ item.extra }} -
+ @if (item.extra) { +
+ {{ item.extra }} +
+ }
-
- - {{ item.description }} - -
-
{{ item.datetime }}
+ @if (item.description) { +
+ + {{ item.description }} + +
+ } + @if (item.datetime) { +
{{ item.datetime }}
+ }
diff --git a/packages/abc/notice-icon/notice-icon.component.html b/packages/abc/notice-icon/notice-icon.component.html index 9a4fa89c07..f4d5bcf94b 100644 --- a/packages/abc/notice-icon/notice-icon.component.html +++ b/packages/abc/notice-icon/notice-icon.component.html @@ -3,27 +3,29 @@
-
+@if (data!.length <= 0) { -
-
- -
- - - - - - - - - +} @else { +
+ +
+ + + + @for (i of data; track $index) { + + + + } + + + +} diff --git a/packages/abc/observers/observer-size.ts b/packages/abc/observers/observer-size.ts index a094874bfd..3d81d96a9f 100644 --- a/packages/abc/observers/observer-size.ts +++ b/packages/abc/observers/observer-size.ts @@ -80,7 +80,8 @@ export class SizeObserver implements OnDestroy { @Directive({ selector: '[observeSize]', - exportAs: 'observeSize' + exportAs: 'observeSize', + standalone: true }) export class ObserverSize implements AfterViewInit, OnDestroy { private _sub$: Subscription | null = null; @@ -117,6 +118,6 @@ export class ObserverSize implements AfterViewInit, OnDestroy { @NgModule({ exports: [ObserverSize], - declarations: [ObserverSize] + imports: [ObserverSize] }) export class ObserversModule {} diff --git a/packages/abc/onboarding/onboarding.component.html b/packages/abc/onboarding/onboarding.component.html index 96717c30d4..be3aab5b2c 100644 --- a/packages/abc/onboarding/onboarding.component.html +++ b/packages/abc/onboarding/onboarding.component.html @@ -1,62 +1,54 @@ -
-
- - -
-
- - + @if (footer) { + + } diff --git a/packages/chart/chart-echarts/echarts.component.ts b/packages/chart/chart-echarts/echarts.component.ts index eaad848828..e0fa424aee 100644 --- a/packages/chart/chart-echarts/echarts.component.ts +++ b/packages/chart/chart-echarts/echarts.component.ts @@ -34,7 +34,9 @@ import { selector: 'chart-echarts, [chart-echarts]', exportAs: 'chartECharts', template: ` - + @if (!loaded) { + + }
`, host: { diff --git a/packages/chart/custom/custom.component.ts b/packages/chart/custom/custom.component.ts index 99345d131f..d3c54fa3dd 100644 --- a/packages/chart/custom/custom.component.ts +++ b/packages/chart/custom/custom.component.ts @@ -16,8 +16,10 @@ import { InputNumber, NumberInput } from '@delon/util/decorator'; selector: 'g2,g2-custom', exportAs: 'g2Custom', template: ` - - + @if (!loaded) { + + } + `, host: { '[style.height.px]': 'height' diff --git a/packages/chart/docs/demo/resizable.md b/packages/chart/docs/demo/resizable.md index 15600195cc..a5dffbd4a4 100644 --- a/packages/chart/docs/demo/resizable.md +++ b/packages/chart/docs/demo/resizable.md @@ -14,10 +14,11 @@ type: example Use [nz-resizable](https://ng.ant.design/experimental/resizable/en) to build a resizable container. ```ts -import { Component, ViewChild } from '@angular/core'; -import { G2BarClickItem, G2BarComponent, G2BarData } from '@delon/chart/bar'; +import { Component, ViewChild, inject } from '@angular/core'; + +import { G2BarClickItem, G2BarComponent, G2BarData, G2BarModule } from '@delon/chart/bar'; import { NzMessageService } from 'ng-zorro-antd/message'; -import { NzResizeEvent } from 'ng-zorro-antd/resizable'; +import { NzResizableModule, NzResizeEvent } from 'ng-zorro-antd/resizable'; @Component({ selector: 'app-demo', @@ -30,21 +31,23 @@ import { NzResizeEvent } from 'ng-zorro-antd/resizable'; (nzResize)="onResize($event)" style="background: #eee;border: 1px solid #ddd; padding: 16px;" > - - + +
`, + standalone: true, + imports: [NzResizableModule, G2BarModule] }) export class DemoComponent { + private msg = inject(NzMessageService); @ViewChild('bar') private readonly barComp!: G2BarComponent; width = 400; height = 200; private id = -1; - constructor(private msg: NzMessageService) {} salesData: G2BarData[] = new Array(12).fill({}).map((_i, idx) => ({ x: `${idx + 1}月`, y: Math.floor(Math.random() * 1000) + 200, - color: idx > 5 ? '#f50' : undefined, + color: idx > 5 ? '#f50' : undefined })); handleClick(data: G2BarClickItem): void { diff --git a/packages/chart/docs/getting-started.en-US.md b/packages/chart/docs/getting-started.en-US.md index 12329bd056..51538d8651 100644 --- a/packages/chart/docs/getting-started.en-US.md +++ b/packages/chart/docs/getting-started.en-US.md @@ -29,7 +29,7 @@ export class DelonModule { return { ngModule: DelonModule, providers: [ - { provide: ALAIN_CONFIG, useValue: alainConfig } + provideAlainConfig(alainConfig) ] }; } @@ -105,9 +105,9 @@ export class DelonModule { return { ngModule: DelonModule, providers: [ - { provide: ALAIN_CONFIG, useValue: alainConfig } + provideAlainConfig(alainConfig) ] }; } } -``` \ No newline at end of file +``` diff --git a/packages/chart/docs/getting-started.zh-CN.md b/packages/chart/docs/getting-started.zh-CN.md index ba34699d4c..9ee80c98a4 100644 --- a/packages/chart/docs/getting-started.zh-CN.md +++ b/packages/chart/docs/getting-started.zh-CN.md @@ -41,7 +41,7 @@ export class DelonModule { return { ngModule: DelonModule, providers: [ - { provide: ALAIN_CONFIG, useValue: alainConfig } + provideAlainConfig(alainConfig) ] }; } @@ -117,7 +117,7 @@ export class DelonModule { return { ngModule: DelonModule, providers: [ - { provide: ALAIN_CONFIG, useValue: alainConfig } + provideAlainConfig(alainConfig) ] }; } diff --git a/packages/chart/gauge/gauge.component.ts b/packages/chart/gauge/gauge.component.ts index 558eb08d5c..4dd49c78e3 100644 --- a/packages/chart/gauge/gauge.component.ts +++ b/packages/chart/gauge/gauge.component.ts @@ -9,7 +9,9 @@ import type { NzSafeAny } from 'ng-zorro-antd/core/types'; @Component({ selector: 'g2-gauge', exportAs: 'g2Gauge', - template: ``, + template: `@if (!loaded) { + + }`, host: { '[class.g2-gauge]': 'true' }, diff --git a/packages/chart/number-info/number-info.component.html b/packages/chart/number-info/number-info.component.html index d0054ca5fa..08db60486c 100644 --- a/packages/chart/number-info/number-info.component.html +++ b/packages/chart/number-info/number-info.component.html @@ -1,16 +1,26 @@ -
- {{ title }} -
-
- {{ subTitle }} -
+@if (title) { +
+ {{ title }} +
+} +@if (subTitle) { +
+ {{ subTitle }} +
+}
{{ total }} - {{ suffix }} - - - {{ subTotal }} - + @if (suffix) { + {{ suffix }} + } + @if (status || subTotal) { + + {{ subTotal }} + @if (status) { + + } + + }
diff --git a/packages/chart/pie/pie.component.html b/packages/chart/pie/pie.component.html index b877d452d7..b0e7dfb50d 100644 --- a/packages/chart/pie/pie.component.html +++ b/packages/chart/pie/pie.component.html @@ -1,25 +1,37 @@ - +@if (!loaded) { + +}
-
-

- -
-
-

-
- -
-
+ @if (subTitle || total) { +
+ @if (subTitle) { +

+ +
+
+

+ } + @if (total) { +
+ +
+
+
+ }
-
+ }
-
    -
  • - - {{ item.x }} - - {{ item.percent }}% - -
  • -
+@if (hasLegend && legendData?.length) { +
    + @for (item of legendData; track $index) { +
  • + + {{ item.x }} + + {{ item.percent }}% + +
  • + } +
+} diff --git a/packages/chart/radar/radar.component.html b/packages/chart/radar/radar.component.html index 492c5d8db8..3a056bb0af 100644 --- a/packages/chart/radar/radar.component.html +++ b/packages/chart/radar/radar.component.html @@ -1,18 +1,18 @@ - +@if (!loaded) { + +}

{{ title }}

-
-
- - {{ i.name }} -
{{ i.value }}
+@if (hasLegend) { +
+ @for (i of legendData; track $index) { +
+ + {{ i.name }} +
{{ i.value }}
+
+ }
-
+} diff --git a/packages/chart/single-bar/demo/basic.md b/packages/chart/single-bar/demo/basic.md index fc826dca91..10ec47b015 100644 --- a/packages/chart/single-bar/demo/basic.md +++ b/packages/chart/single-bar/demo/basic.md @@ -23,15 +23,17 @@ import { Component, ViewEncapsulation } from '@angular/core'; - - {{ idx + 1 }} - - - - - - - + @for (i of list; track $index) { + + {{ $index + 1 }} + + + + + + + + } `, diff --git a/packages/chart/tag-cloud/tag-cloud.component.ts b/packages/chart/tag-cloud/tag-cloud.component.ts index c45db08d73..ac2a2b55a0 100644 --- a/packages/chart/tag-cloud/tag-cloud.component.ts +++ b/packages/chart/tag-cloud/tag-cloud.component.ts @@ -21,7 +21,9 @@ export interface G2TagCloudClickItem { @Component({ selector: 'g2-tag-cloud', exportAs: 'g2TagCloud', - template: ``, + template: `@if (!loaded) { + + }`, preserveWhitespaces: false, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None diff --git a/packages/chart/timeline/demo/max-axis.md b/packages/chart/timeline/demo/max-axis.md index ae56e21237..9a4b80aec8 100644 --- a/packages/chart/timeline/demo/max-axis.md +++ b/packages/chart/timeline/demo/max-axis.md @@ -16,7 +16,9 @@ import { G2TimelineData, G2TimelineMap } from '@delon/chart/timeline'; selector: 'app-demo', template: ` - + @for (i of axisList; track $index) { + + } ` }) diff --git a/packages/chart/timeline/timeline.component.ts b/packages/chart/timeline/timeline.component.ts index 1c94b85abe..d6f3328a87 100644 --- a/packages/chart/timeline/timeline.component.ts +++ b/packages/chart/timeline/timeline.component.ts @@ -67,7 +67,9 @@ export interface G2TimelineClickItem {

{{ title }}

- + @if (!loaded) { + + }
`, preserveWhitespaces: false, diff --git a/packages/chart/trend/trend.component.ts b/packages/chart/trend/trend.component.ts index 2bce55077a..13c85f6a7b 100644 --- a/packages/chart/trend/trend.component.ts +++ b/packages/chart/trend/trend.component.ts @@ -7,7 +7,9 @@ import { BooleanInput, InputBoolean } from '@delon/util/decorator'; exportAs: 'trend', template: ` - + @if (flag) { + + } `, host: { '[class.trend]': 'true', diff --git a/packages/chart/water-wave/water-wave.component.html b/packages/chart/water-wave/water-wave.component.html index d52f3d709a..43b53b1b64 100644 --- a/packages/chart/water-wave/water-wave.component.html +++ b/packages/chart/water-wave/water-wave.component.html @@ -2,8 +2,10 @@
- - {{ title }} - + @if (title) { + + {{ title }} + + }

{{ percent }}%

diff --git a/packages/form/docs/customize.en-US.md b/packages/form/docs/customize.en-US.md index a93e13f1fe..c864a75238 100644 --- a/packages/form/docs/customize.en-US.md +++ b/packages/form/docs/customize.en-US.md @@ -6,7 +6,7 @@ type: Documents ## Foreword -`@delon/form` try our best to meet the needs of different environments, in addition to the built-in basic component widgets, you can further expand the requirements in two ways: +`@delon/form` try our best to meet the needs of different environments, in addition to the built-in basic widgets (Some require manual registration), you can further expand the requirements in two ways: ## Custom widget in sf @@ -18,22 +18,17 @@ Making a set of widgets for project can lead to faster development work. ### How to making widget -**Third-party library** - -By default `@delon/form` implements some common third-party library widgets, which are called third-party component widgets. This widget exists in [widgets-third](https://github.com/ng-alain /delon/tree/master/packages/form/widgets-third) directory; you can use directly. - -| Name | Description | Document | Source | -| ---- | ----------- | -------- | ------ | -| `markdown` | Markdown Editor | [Document](/form/markdown) | [Source](https://github.com/ng-alain/delon/tree/master/packages/form/widgets-third/markdown) | -| `tinymce` | Tinymce Editor | [Document](/form/tinymce) | [Source](https://github.com/ng-alain/delon/tree/master/packages/form/widgets-third/tinymce) | - **Create widgets** A widget is a component. You only need to inherit `ControlWidget` to create a widget. For example: ```ts -import { Component, OnInit } from '@angular/core'; -import { ControlWidget } from '@delon/form'; +import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; +import { ControlWidget, DelonFormModule, SFWidgetProvideConfig } from '@delon/form'; + +export function withTestWidget(): SFWidgetProvideConfig { + return { KEY: 'test', type: TestWidget }; +} @Component({ selector: 'sf-tinymce', @@ -47,11 +42,17 @@ import { ControlWidget } from '@delon/form'; [loading]="loading"> - ` + `, + preserveWhitespaces: false, + changeDetection: ChangeDetectionStrategy.OnPush, + standalone: true, + imports: [DelonFormModule] }) -export class TinymceWidget extends ControlWidget implements OnInit { - static readonly KEY = 'tinymce'; +class TestWidget extends ControlWidget implements OnInit { + /* KEY value for registering widgets */ + static readonly KEY = 'test'; + // It is recommended to use `ngOnInit` to obtain the parameters required by the component. config: any; loadingTip: string; @@ -60,6 +61,7 @@ export class TinymceWidget extends ControlWidget implements OnInit { this.config = this.ui.config || {}; } + // reset can better solve the problem of new data required during the form reset process reset(value: string) { } @@ -81,34 +83,32 @@ The widget is manually trigger changed detection during the rendering process. I ### Register -Define the widget component in the root module (includes: `declarations`), import `WidgetRegistry` in the module and register the custom widget. +Recommended to define a widget called `withXWidth` and return `SFWidgetProvideConfig` type, for example: ```ts -@NgModule({ - declarations: [ TinymceWidget ], - imports: [ - DelonFormModule.forRoot() - ] -}) -export class AppModule { - constructor(widgetRegistry: WidgetRegistry) { - widgetRegistry.register(TinymceWidget.KEY, TinymceWidget); - } +export function withTestWidget(): SFWidgetProvideConfig { + return { KEY: 'test', type: TestWidget }; } ``` -Of course, for more friendly maintenance, recommended to define a Json schema module in the Shared directory. Please refer to [ng-alain implementation](https://github.com/ng-alain/ng-alain/blob/master/ Src/app/shared/json-schema/json-schema.module.ts). +Finally, register via `provideSFConfig`: + +```ts +provideSFConfig({ widgets: [ withTestWidget() ] }) +``` + +For more friendly maintenance, recommended to define a project-specific collection in the `shared` directory. If you are interested, please refer to [NG-ALAIN implementation](https://github.com/ng-alain/ng-alain/blob/master/src/app/shared/json-schema/) or refer to [@delon/form/widgets/autocomplete](https://github.com/ng-alain/delon/tree/master/packages/form/widgets/autocomplete). ### Usage Just like other widgets, just specify the `widget` value, for example: ```json -"intro": { +"test": { "type": "string", "ui": { - "widget": "tinymce", - "loadingTip": "loading..." + "widget": "test", + "data": "widget parameters" } } ``` diff --git a/packages/form/docs/customize.zh-CN.md b/packages/form/docs/customize.zh-CN.md index 774b03ea0d..e36c38586c 100644 --- a/packages/form/docs/customize.zh-CN.md +++ b/packages/form/docs/customize.zh-CN.md @@ -6,7 +6,7 @@ type: Documents ## 写在前面 -`@delon/form` 尽可能满足不同需求,除现有内置的十几种基础组件小部件外,可以通过以下两种方式进一步扩展需求: +`@delon/form` 尽可能满足不同需求,除现有内置的十几种基础小部件(部分需要手动注册)外,可以通过以下两种方式进一步扩展需求: ## 自定义小部件 @@ -18,24 +18,17 @@ type: Documents ### 编写小部件 -**常见小部件库** - -默认情况下 @delon/form 实现了一些常见需求,但需要额外类库支持的,称它为第三方组件小部件,这一部分小部件存在于[widgets-third](https://github.com/ng-alain/delon/tree/master/packages/form/widgets-third)目录里;你可以直接复制使用。 - -这些组件包括: - -| 名称 | 描述 | 文档 | 源代码 | -| --- | ---- | ---- | ---- | -| `markdown` | Markdown 编辑器 | [文档](/form/markdown) | [源代码](https://github.com/ng-alain/delon/tree/master/packages/form/widgets-third/markdown) | -| `tinymce` | Tinymce 富文本框 | [文档](/form/tinymce) | [源代码](https://github.com/ng-alain/delon/tree/master/packages/form/widgets-third/tinymce) | - **自己创建小部件** -小部件就是一个组件,你只需要继承 `ControlWidget` 就相当于构建一个小部件,其结构如下: +小部件就是一个组件,你只需要继承 `ControlWidget` 就相当于构建一个小部件,例如: ```ts -import { Component, OnInit } from '@angular/core'; -import { ControlWidget } from '@delon/form'; +import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; +import { ControlWidget, DelonFormModule, SFWidgetProvideConfig } from '@delon/form'; + +export function withTestWidget(): SFWidgetProvideConfig { + return { KEY: 'test', type: TestWidget }; +} @Component({ selector: 'sf-tinymce', @@ -49,11 +42,15 @@ import { ControlWidget } from '@delon/form'; [loading]="loading"> - ` + `, + preserveWhitespaces: false, + changeDetection: ChangeDetectionStrategy.OnPush, + standalone: true, + imports: [DelonFormModule] }) -export class TinymceWidget extends ControlWidget implements OnInit { +class TestWidget extends ControlWidget implements OnInit { /* 用于注册小部件 KEY 值 */ - static readonly KEY = 'tinymce'; + static readonly KEY = 'test'; // 组件所需要的参数,建议使用 `ngOnInit` 获取 config: any; @@ -86,34 +83,32 @@ export class TinymceWidget extends ControlWidget implements OnInit { ### 注册小部件 -在根模块中定义(`declarations`)注册小部件组件,同时在模块中导入 `WidgetRegistry` 并注册自定义小部件。 +建议在小部件内定义一个名为 `withXWidth` 并返回 `SFWidgetProvideConfig`,例如: ```ts -@NgModule({ - declarations: [ TinymceWidget ], - imports: [ - DelonFormModule.forRoot() - ] -}) -export class AppModule { - constructor(widgetRegistry: WidgetRegistry) { - widgetRegistry.register(TinymceWidget.KEY, TinymceWidget); - } +export function withTestWidget(): SFWidgetProvideConfig { + return { KEY: 'test', type: TestWidget }; } ``` -当然为了更友好的维护,建议在Shared目录下定义一个专属 Json schema 模块,有兴趣可参考 [ng-alain实现](https://github.com/ng-alain/ng-alain/blob/master/src/app/shared/json-schema/json-schema.module.ts)。 +最后,通过 `provideSFConfig` 来注册: + +```ts +provideSFConfig({ widgets: [ withTestWidget() ] }) +``` + +当然为了更友好的维护,建议在Shared目录下定义项目专属的集合,有兴趣可参考 [ng-alain实现](https://github.com/ng-alain/ng-alain/blob/master/src/app/shared/json-schema/)或参考[@delon/form/widgets/autocomplete](https://github.com/ng-alain/delon/tree/master/packages/form/widgets/autocomplete)。 ### 使用自定义小部件 同其他小部件一样,只需要指定 `widget` 值,例如: ```json -"intro": { +"test": { "type": "string", "ui": { - "widget": "tinymce", - "loadingTip": "loading..." + "widget": "test", + "data": "widget parameters" } } ``` diff --git a/packages/form/public_api.ts b/packages/form/public_api.ts index a606da1581..cba1843545 100644 --- a/packages/form/public_api.ts +++ b/packages/form/public_api.ts @@ -1 +1 @@ -export * from './src/form'; +export * from './src/index'; diff --git a/packages/form/spec/base.spec.ts b/packages/form/spec/base.spec.ts index 17e3b67dd2..dfafacfc45 100644 --- a/packages/form/spec/base.spec.ts +++ b/packages/form/spec/base.spec.ts @@ -9,6 +9,7 @@ import { AlainThemeModule } from '@delon/theme'; import { deepCopy, deepGet } from '@delon/util/other'; import { NzSafeAny } from 'ng-zorro-antd/core/types'; +import { SFWidgetProvideConfig, provideSFConfig } from '../src'; import { SF_SEQ } from '../src/const'; import { SFButton } from '../src/interface'; import { FormProperty } from '../src/model/form.property'; @@ -47,9 +48,7 @@ export function builder(options?: { } { options = { detectChanges: true, ...options }; TestBed.configureTestingModule({ - imports: [NoopAnimationsModule, AlainThemeModule.forRoot(), DelonFormModule.forRoot()].concat( - options.imports || [] - ), + imports: [NoopAnimationsModule, AlainThemeModule, DelonFormModule.forRoot()].concat(options.imports || []), declarations: [TestFormComponent] }); if (options.template) { @@ -75,11 +74,18 @@ export function builder(options?: { }; } -export function configureSFTestSuite(): void { +export function configureSFTestSuite(options?: { imports?: NzSafeAny[]; widgets?: SFWidgetProvideConfig[] }): void { beforeEach(() => { TestBed.configureTestingModule({ - imports: [NoopAnimationsModule, AlainThemeModule.forRoot(), DelonFormModule.forRoot(), HttpClientTestingModule], - declarations: [TestFormComponent] + imports: [ + NoopAnimationsModule, + AlainThemeModule, + DelonFormModule, + HttpClientTestingModule, + ...(options?.imports ?? []) + ], + declarations: [TestFormComponent], + providers: [provideSFConfig({ widgets: options?.widgets })] }); }); } diff --git a/packages/form/spec/form.spec.ts b/packages/form/spec/form.spec.ts index 7660fd6652..487784485c 100644 --- a/packages/form/spec/form.spec.ts +++ b/packages/form/spec/form.spec.ts @@ -27,12 +27,9 @@ describe('form: component', () => { function genModule(options: { acl?: boolean; i18n?: boolean } = {}): void { options = { acl: false, i18n: false, ...options }; - const imports = [NoopAnimationsModule, DelonFormModule.forRoot(), AlainThemeModule.forRoot()]; - if (options.i18n) { - imports.push(AlainThemeModule.forRoot()); - } + const imports: NzSafeAny[] = [NoopAnimationsModule, DelonFormModule.forRoot(), AlainThemeModule]; if (options.acl) { - imports.push(DelonACLModule.forRoot()); + imports.push(DelonACLModule); } TestBed.configureTestingModule({ imports, diff --git a/packages/form/src/form.ts b/packages/form/src/index.ts similarity index 95% rename from packages/form/src/form.ts rename to packages/form/src/index.ts index 2c1378475e..73d5189514 100644 --- a/packages/form/src/form.ts +++ b/packages/form/src/index.ts @@ -16,6 +16,7 @@ export * from './model/index'; export * from './widget'; export * from './widgets/index'; export * from './widget.factory'; +export * from './provide'; // other export * from './validator.factory'; diff --git a/packages/form/src/module.ts b/packages/form/src/module.ts index b72cad44af..e67ba6a9cf 100644 --- a/packages/form/src/module.ts +++ b/packages/form/src/module.ts @@ -3,10 +3,8 @@ import { CommonModule } from '@angular/common'; import { ModuleWithProviders, NgModule, NgZone } from '@angular/core'; import { FormsModule } from '@angular/forms'; -import { NzAutocompleteModule } from 'ng-zorro-antd/auto-complete'; import { NzButtonModule } from 'ng-zorro-antd/button'; import { NzCardModule } from 'ng-zorro-antd/card'; -import { NzCascaderModule } from 'ng-zorro-antd/cascader'; import { NzCheckboxModule } from 'ng-zorro-antd/checkbox'; import { NzDatePickerModule } from 'ng-zorro-antd/date-picker'; import { NzFormModule } from 'ng-zorro-antd/form'; @@ -14,28 +12,18 @@ import { NzGridModule } from 'ng-zorro-antd/grid'; import { NzIconModule } from 'ng-zorro-antd/icon'; import { NzInputModule } from 'ng-zorro-antd/input'; import { NzInputNumberModule } from 'ng-zorro-antd/input-number'; -import { NzMentionModule } from 'ng-zorro-antd/mention'; import { NzModalModule } from 'ng-zorro-antd/modal'; import { NzRadioModule } from 'ng-zorro-antd/radio'; -import { NzRateModule } from 'ng-zorro-antd/rate'; import { NzSelectModule } from 'ng-zorro-antd/select'; -import { NzSliderModule } from 'ng-zorro-antd/slider'; import { NzSwitchModule } from 'ng-zorro-antd/switch'; -import { NzTagModule } from 'ng-zorro-antd/tag'; -import { NzTimePickerModule } from 'ng-zorro-antd/time-picker'; import { NzToolTipModule } from 'ng-zorro-antd/tooltip'; -import { NzTransferModule } from 'ng-zorro-antd/transfer'; -import { NzTreeSelectModule } from 'ng-zorro-antd/tree-select'; -import { NzUploadModule } from 'ng-zorro-antd/upload'; import { DelonLocaleModule } from '@delon/theme'; import { AlainConfigService } from '@delon/util/config'; const ZORROS = [ - NzAutocompleteModule, NzButtonModule, NzCardModule, - NzCascaderModule, NzCheckboxModule, NzDatePickerModule, NzFormModule, @@ -43,19 +31,11 @@ const ZORROS = [ NzIconModule, NzInputModule, NzInputNumberModule, - NzMentionModule, NzModalModule, NzRadioModule, - NzRateModule, NzSelectModule, - NzSliderModule, NzSwitchModule, - NzTagModule, - NzTimePickerModule, - NzToolTipModule, - NzTransferModule, - NzTreeSelectModule, - NzUploadModule + NzToolTipModule ]; import { SFFixedDirective } from './sf-fixed.directive'; @@ -71,28 +51,18 @@ const COMPONENTS = [SFComponent, SFItemComponent, SFItemWrapComponent, SFTemplat import { WidgetRegistry } from './widget.factory'; import { ArrayWidget } from './widgets/array/array.widget'; -import { AutoCompleteWidget } from './widgets/autocomplete/autocomplete.widget'; import { BooleanWidget } from './widgets/boolean/boolean.widget'; -import { CascaderWidget } from './widgets/cascader/cascader.widget'; import { CheckboxWidget } from './widgets/checkbox/checkbox.widget'; import { CustomWidget } from './widgets/custom/custom.widget'; import { DateWidget } from './widgets/date/date.widget'; -import { MentionWidget } from './widgets/mention/mention.widget'; import { NumberWidget } from './widgets/number/number.widget'; import { NzWidgetRegistry } from './widgets/nz-widget.registry'; import { ObjectWidget } from './widgets/object/object.widget'; import { RadioWidget } from './widgets/radio/radio.widget'; -import { RateWidget } from './widgets/rate/rate.widget'; import { SelectWidget } from './widgets/select/select.widget'; -import { SliderWidget } from './widgets/slider/slider.widget'; import { StringWidget } from './widgets/string/string.widget'; -import { TagWidget } from './widgets/tag/tag.widget'; import { TextWidget } from './widgets/text/text.widget'; import { TextareaWidget } from './widgets/textarea/textarea.widget'; -import { TimeWidget } from './widgets/time/time.widget'; -import { TransferWidget } from './widgets/transfer/transfer.widget'; -import { TreeSelectWidget } from './widgets/tree-select/tree-select.widget'; -import { UploadWidget } from './widgets/upload/upload.widget'; const WIDGETS = [ ObjectWidget, @@ -100,21 +70,11 @@ const WIDGETS = [ StringWidget, NumberWidget, DateWidget, - TimeWidget, RadioWidget, CheckboxWidget, BooleanWidget, TextareaWidget, SelectWidget, - TreeSelectWidget, - TagWidget, - UploadWidget, - TransferWidget, - SliderWidget, - RateWidget, - AutoCompleteWidget, - CascaderWidget, - MentionWidget, CustomWidget, TextWidget ]; diff --git a/packages/form/src/provide.ts b/packages/form/src/provide.ts new file mode 100644 index 0000000000..4de812db29 --- /dev/null +++ b/packages/form/src/provide.ts @@ -0,0 +1,45 @@ +import { + ENVIRONMENT_INITIALIZER, + EnvironmentProviders, + NgZone, + Provider, + inject, + makeEnvironmentProviders +} from '@angular/core'; + +import { AlainConfigService } from '@delon/util/config'; +import type { NzSafeAny } from 'ng-zorro-antd/core/types'; + +import { AjvSchemaValidatorFactory, SchemaValidatorFactory } from './validator.factory'; +import { WidgetRegistry } from './widget.factory'; +import { NzWidgetRegistry } from './widgets/nz-widget.registry'; + +export interface SFWidgetProvideConfig { + KEY: string; + type: NzSafeAny; +} + +/** + * Just only using Standalone widgets + */ +export function provideSFConfig(options?: { widgets?: SFWidgetProvideConfig[] }): EnvironmentProviders { + const provides: Array = [ + { + provide: SchemaValidatorFactory, + useClass: AjvSchemaValidatorFactory, + deps: [AlainConfigService, NgZone] + }, + { provide: WidgetRegistry, useClass: NzWidgetRegistry } + ]; + if (options?.widgets) { + provides.push({ + provide: ENVIRONMENT_INITIALIZER, + multi: true, + useValue: () => { + const srv = inject(WidgetRegistry); + options?.widgets?.forEach(widget => srv.register(widget.KEY, widget.type)); + } + }); + } + return makeEnvironmentProviders(provides); +} diff --git a/packages/form/src/sf-item-wrap.component.html b/packages/form/src/sf-item-wrap.component.html index c14725ee17..207f71feec 100644 --- a/packages/form/src/sf-item-wrap.component.html +++ b/packages/form/src/sf-item-wrap.component.html @@ -8,39 +8,50 @@ [class.ant-form-item-is-validating]="ui.feedback === 'validating'" [class.ant-form-item-has-feedback]="ui.feedback" > -
- -
+ @if (_showTitle) { +
+ @if (t) { + + } +
+ }
-
- -
+ } + @if (schema.description) { +
+ }
diff --git a/packages/form/src/sf.component.html b/packages/form/src/sf.component.html index 4f0632991e..b69d1457ce 100644 --- a/packages/form/src/sf.component.html +++ b/packages/form/src/sf.component.html @@ -2,74 +2,80 @@ - - -
-
-
- - - - + @if (button !== 'none') { + @if (_btn && _btn.render) { + +
+
+
+ @if (button) { + + @if (_btn.reset) { + + } + } @else { + + } +
-
- - + + } + } @else { + + }
- + @if (rootProperty) { + + } diff --git a/packages/form/src/widgets/array/array.widget.html b/packages/form/src/widgets/array/array.widget.html deleted file mode 100644 index 498f1fd33b..0000000000 --- a/packages/form/src/widgets/array/array.widget.html +++ /dev/null @@ -1,57 +0,0 @@ - -
- -
- -
-
-
-
-
- -
- - - - - - -
-
-
-
{{ error }}
-
-
-
-
diff --git a/packages/form/src/widgets/array/array.widget.ts b/packages/form/src/widgets/array/array.widget.ts index e727a80a5c..7802b06cbe 100644 --- a/packages/form/src/widgets/array/array.widget.ts +++ b/packages/form/src/widgets/array/array.widget.ts @@ -8,7 +8,68 @@ import { ArrayLayoutWidget } from '../../widget'; @Component({ selector: 'sf-array', - templateUrl: './array.widget.html', + template: ` + @if (schema.title) { +
+ +
+ +
+
+ } +
+
+
+ @for (i of $any(formProperty).properties; track $index) { + @if (i.visible && !i.ui.hidden) { +
+ + + @if (showRemove) { + + + + } + +
+ } + } +
+ @if (!ui.onlyVisual && showError) { +
{{ error }}
+ } + @if (schema.description) { +
+ } +
+
+
`, host: { '[class.sf__array]': 'true' }, preserveWhitespaces: false, encapsulation: ViewEncapsulation.None diff --git a/packages/form/src/widgets/array/index.en-US.md b/packages/form/src/widgets/array/index.en-US.md index 88566b5423..1129dadd3d 100644 --- a/packages/form/src/widgets/array/index.en-US.md +++ b/packages/form/src/widgets/array/index.en-US.md @@ -2,6 +2,7 @@ title: array subtitle: Array type: Widgets +order: 2 --- Create array object, it's only valid when `schema.type="array"`. diff --git a/packages/form/src/widgets/array/index.zh-CN.md b/packages/form/src/widgets/array/index.zh-CN.md index fd179a3a5d..f8e4f8d5b9 100644 --- a/packages/form/src/widgets/array/index.zh-CN.md +++ b/packages/form/src/widgets/array/index.zh-CN.md @@ -2,6 +2,7 @@ title: array subtitle: 数组 type: Widgets +order: 2 --- 创建对象数组,只对 `schema.type="array"` 时有效。 diff --git a/packages/form/src/widgets/autocomplete/autocomplete.widget.html b/packages/form/src/widgets/autocomplete/autocomplete.widget.html deleted file mode 100644 index 128281dcd5..0000000000 --- a/packages/form/src/widgets/autocomplete/autocomplete.widget.html +++ /dev/null @@ -1,27 +0,0 @@ - - - - {{ i.label }} - - diff --git a/packages/form/src/widgets/boolean/boolean.widget.html b/packages/form/src/widgets/boolean/boolean.widget.html deleted file mode 100644 index fd288eab8d..0000000000 --- a/packages/form/src/widgets/boolean/boolean.widget.html +++ /dev/null @@ -1,11 +0,0 @@ - - - diff --git a/packages/form/src/widgets/boolean/boolean.widget.ts b/packages/form/src/widgets/boolean/boolean.widget.ts index 46fdb51357..41d9cd552f 100644 --- a/packages/form/src/widgets/boolean/boolean.widget.ts +++ b/packages/form/src/widgets/boolean/boolean.widget.ts @@ -5,7 +5,24 @@ import { ControlUIWidget } from '../../widget'; @Component({ selector: 'sf-boolean', - templateUrl: './boolean.widget.html', + template: ` + + `, preserveWhitespaces: false, encapsulation: ViewEncapsulation.None }) diff --git a/packages/form/src/widgets/boolean/index.en-US.md b/packages/form/src/widgets/boolean/index.en-US.md index e229751769..821870a325 100644 --- a/packages/form/src/widgets/boolean/index.en-US.md +++ b/packages/form/src/widgets/boolean/index.en-US.md @@ -2,6 +2,7 @@ title: boolean subtitle: Switch type: Widgets +order: 4 --- Switching Selector. diff --git a/packages/form/src/widgets/boolean/index.zh-CN.md b/packages/form/src/widgets/boolean/index.zh-CN.md index 50b568ae47..81488a03e2 100644 --- a/packages/form/src/widgets/boolean/index.zh-CN.md +++ b/packages/form/src/widgets/boolean/index.zh-CN.md @@ -2,6 +2,7 @@ title: boolean subtitle: 开关 type: Widgets +order: 4 --- 开关选择器 diff --git a/packages/form/src/widgets/cascader/cascader.widget.html b/packages/form/src/widgets/cascader/cascader.widget.html deleted file mode 100644 index 25fee31981..0000000000 --- a/packages/form/src/widgets/cascader/cascader.widget.html +++ /dev/null @@ -1,28 +0,0 @@ - - - diff --git a/packages/form/src/widgets/checkbox/checkbox.widget.html b/packages/form/src/widgets/checkbox/checkbox.widget.html deleted file mode 100644 index 58d19cdd1d..0000000000 --- a/packages/form/src/widgets/checkbox/checkbox.widget.html +++ /dev/null @@ -1,61 +0,0 @@ - - - - - - - - - - - - - - -
-
- -
-
- -
-
-
-
-
-
diff --git a/packages/form/src/widgets/checkbox/checkbox.widget.ts b/packages/form/src/widgets/checkbox/checkbox.widget.ts index f9a36a9778..4c56abf51c 100644 --- a/packages/form/src/widgets/checkbox/checkbox.widget.ts +++ b/packages/form/src/widgets/checkbox/checkbox.widget.ts @@ -8,7 +8,75 @@ import { ControlUIWidget } from '../../widget'; @Component({ selector: 'sf-checkbox', - templateUrl: './checkbox.widget.html', + template: ` + @if (ui.checkAll) { + + } + + + @if (inited) { + @if (data.length === 0) { + + } @else { + @if (grid_span === 0) { + + + } @else { + +
+ @if (ui.checkAll) { +
+ +
+ } + @for (i of data; track $index) { +
+ +
+ } +
+
+ } + } + } +
`, preserveWhitespaces: false, encapsulation: ViewEncapsulation.None }) diff --git a/packages/form/src/widgets/checkbox/demo/simple.md b/packages/form/src/widgets/checkbox/demo/simple.md index 62ff8d36e9..5a59a916b5 100644 --- a/packages/form/src/widgets/checkbox/demo/simple.md +++ b/packages/form/src/widgets/checkbox/demo/simple.md @@ -15,7 +15,8 @@ Simplest of usage. ```ts import { Component } from '@angular/core'; -import { SFCascaderWidgetSchema, SFCheckboxWidgetSchema, SFSchema } from '@delon/form'; +import { SFCheckboxWidgetSchema, SFSchema } from '@delon/form'; +import type { SFCascaderWidgetSchema } from '@delon/form/widgets/cascader'; import { NzMessageService } from 'ng-zorro-antd/message'; import { of, delay } from 'rxjs'; diff --git a/packages/form/src/widgets/custom/custom.widget.ts b/packages/form/src/widgets/custom/custom.widget.ts index 4a6eced7d7..187a03c46b 100644 --- a/packages/form/src/widgets/custom/custom.widget.ts +++ b/packages/form/src/widgets/custom/custom.widget.ts @@ -17,7 +17,7 @@ import { ControlUIWidget } from '../../widget'; + /> `, preserveWhitespaces: false, diff --git a/packages/form/src/widgets/custom/demo/simple.md b/packages/form/src/widgets/custom/demo/simple.md index 697e520476..b31a7a19cf 100644 --- a/packages/form/src/widgets/custom/demo/simple.md +++ b/packages/form/src/widgets/custom/demo/simple.md @@ -42,7 +42,7 @@ import { NzMessageService } from 'ng-zorro-antd/message';
- + ` diff --git a/packages/form/src/widgets/date/date.widget.html b/packages/form/src/widgets/date/date.widget.html deleted file mode 100644 index 963abe9a65..0000000000 --- a/packages/form/src/widgets/date/date.widget.html +++ /dev/null @@ -1,119 +0,0 @@ - - - - - - - - - - - - - diff --git a/packages/form/src/widgets/date/date.widget.ts b/packages/form/src/widgets/date/date.widget.ts index e1af4be7f4..aae8678d89 100644 --- a/packages/form/src/widgets/date/date.widget.ts +++ b/packages/form/src/widgets/date/date.widget.ts @@ -13,7 +13,133 @@ import { ControlUIWidget } from '../../widget'; @Component({ selector: 'sf-date', - templateUrl: './date.widget.html', + template: ` + @switch (mode) { + @case ('year') { + + } + @case ('month') { + + } + @case ('week') { + + } + @case ('range') { + + } + @default { + + } + } + `, preserveWhitespaces: false, encapsulation: ViewEncapsulation.None }) diff --git a/packages/form/src/widgets/index.ts b/packages/form/src/widgets/index.ts index 7c15132d9d..7045bcf234 100644 --- a/packages/form/src/widgets/index.ts +++ b/packages/form/src/widgets/index.ts @@ -8,36 +8,15 @@ export * from './number/number.widget'; export * from './number/schema'; export * from './date/date.widget'; export * from './date/schema'; -export * from './time/time.widget'; -export * from './time/schema'; export * from './radio/radio.widget'; export * from './radio/schema'; export * from './checkbox/checkbox.widget'; export * from './checkbox/schema'; export * from './boolean/boolean.widget'; -export * from './cascader/schema'; export * from './textarea/textarea.widget'; export * from './textarea/schema'; export * from './select/select.widget'; export * from './select/schema'; -export * from './tree-select/tree-select.widget'; -export * from './tree-select/schema'; -export * from './tag/tag.widget'; -export * from './tag/schema'; -export * from './upload/upload.widget'; -export * from './upload/schema'; -export * from './transfer/transfer.widget'; -export * from './transfer/schema'; -export * from './slider/slider.widget'; -export * from './slider/schema'; -export * from './rate/rate.widget'; -export * from './rate/schema'; -export * from './autocomplete/autocomplete.widget'; -export * from './autocomplete/schema'; -export * from './cascader/cascader.widget'; -export * from './cascader/schema'; -export * from './mention/mention.widget'; -export * from './mention/schema'; export * from './text/text.widget'; export * from './text/schema'; export * from './custom/custom.widget'; diff --git a/packages/form/src/widgets/mention/mention.widget.html b/packages/form/src/widgets/mention/mention.widget.html deleted file mode 100644 index 90c3c5bc23..0000000000 --- a/packages/form/src/widgets/mention/mention.widget.html +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - diff --git a/packages/form/src/widgets/number/index.en-US.md b/packages/form/src/widgets/number/index.en-US.md index 60e7e01167..3add327866 100644 --- a/packages/form/src/widgets/number/index.en-US.md +++ b/packages/form/src/widgets/number/index.en-US.md @@ -2,6 +2,7 @@ title: number subtitle: Input Number type: Widgets +order: 5 --- Enter a number within certain range with the mouse or keyboard. diff --git a/packages/form/src/widgets/number/index.zh-CN.md b/packages/form/src/widgets/number/index.zh-CN.md index 403e85c6f4..183f3ccc20 100644 --- a/packages/form/src/widgets/number/index.zh-CN.md +++ b/packages/form/src/widgets/number/index.zh-CN.md @@ -2,6 +2,7 @@ title: number subtitle: 数字 type: Widgets +order: 5 --- 通过鼠标或键盘,输入范围内的数值 diff --git a/packages/form/src/widgets/number/number.widget.ts b/packages/form/src/widgets/number/number.widget.ts index b7cb06313c..3f5b61397e 100644 --- a/packages/form/src/widgets/number/number.widget.ts +++ b/packages/form/src/widgets/number/number.widget.ts @@ -5,7 +5,31 @@ import { ControlUIWidget } from '../../widget'; @Component({ selector: 'sf-number', - templateUrl: './number.widget.html', + template: ` + + `, preserveWhitespaces: false, encapsulation: ViewEncapsulation.None }) diff --git a/packages/form/src/widgets/nz-widget.registry.ts b/packages/form/src/widgets/nz-widget.registry.ts index 0898894504..910a00de41 100644 --- a/packages/form/src/widgets/nz-widget.registry.ts +++ b/packages/form/src/widgets/nz-widget.registry.ts @@ -1,25 +1,15 @@ import { ArrayWidget } from './array/array.widget'; -import { AutoCompleteWidget } from './autocomplete/autocomplete.widget'; import { BooleanWidget } from './boolean/boolean.widget'; -import { CascaderWidget } from './cascader/cascader.widget'; import { CheckboxWidget } from './checkbox/checkbox.widget'; import { CustomWidget } from './custom/custom.widget'; import { DateWidget } from './date/date.widget'; -import { MentionWidget } from './mention/mention.widget'; import { NumberWidget } from './number/number.widget'; import { ObjectWidget } from './object/object.widget'; import { RadioWidget } from './radio/radio.widget'; -import { RateWidget } from './rate/rate.widget'; import { SelectWidget } from './select/select.widget'; -import { SliderWidget } from './slider/slider.widget'; import { StringWidget } from './string/string.widget'; -import { TagWidget } from './tag/tag.widget'; import { TextWidget } from './text/text.widget'; import { TextareaWidget } from './textarea/textarea.widget'; -import { TimeWidget } from './time/time.widget'; -import { TransferWidget } from './transfer/transfer.widget'; -import { TreeSelectWidget } from './tree-select/tree-select.widget'; -import { UploadWidget } from './upload/upload.widget'; import { WidgetRegistry } from '../widget.factory'; export class NzWidgetRegistry extends WidgetRegistry { @@ -34,21 +24,11 @@ export class NzWidgetRegistry extends WidgetRegistry { this.register('number', NumberWidget); this.register('integer', NumberWidget); this.register('date', DateWidget); - this.register('time', TimeWidget); this.register('radio', RadioWidget); this.register('checkbox', CheckboxWidget); this.register('boolean', BooleanWidget); this.register('textarea', TextareaWidget); this.register('select', SelectWidget); - this.register('tree-select', TreeSelectWidget); - this.register('tag', TagWidget); - this.register('upload', UploadWidget); - this.register('transfer', TransferWidget); - this.register('slider', SliderWidget); - this.register('rate', RateWidget); - this.register('autocomplete', AutoCompleteWidget); - this.register('cascader', CascaderWidget); - this.register('mention', MentionWidget); this.register('custom', CustomWidget); this.setDefault(StringWidget); diff --git a/packages/form/src/widgets/object/demo/card.md b/packages/form/src/widgets/object/demo/card.md index 1891e0b077..9f1fa52a6e 100644 --- a/packages/form/src/widgets/object/demo/card.md +++ b/packages/form/src/widgets/object/demo/card.md @@ -21,7 +21,9 @@ import { NzMessageService } from 'ng-zorro-antd/message'; @Component({ selector: 'app-demo', template: ` - + @if (schema) { + + } More diff --git a/packages/form/src/widgets/object/index.en-US.md b/packages/form/src/widgets/object/index.en-US.md index 03b9344dcd..ef537b2f88 100644 --- a/packages/form/src/widgets/object/index.en-US.md +++ b/packages/form/src/widgets/object/index.en-US.md @@ -2,6 +2,7 @@ title: object subtitle: Object type: Widgets +order: 1 --- Create an object widget, valid only for `schema.type="object"`. diff --git a/packages/form/src/widgets/object/index.zh-CN.md b/packages/form/src/widgets/object/index.zh-CN.md index d94157f777..56739e0259 100644 --- a/packages/form/src/widgets/object/index.zh-CN.md +++ b/packages/form/src/widgets/object/index.zh-CN.md @@ -2,6 +2,7 @@ title: object subtitle: 对象 type: Widgets +order: 1 --- 创建对象,只对 `schema.type="object"` 时有效。 diff --git a/packages/form/src/widgets/object/object.widget.html b/packages/form/src/widgets/object/object.widget.html deleted file mode 100644 index f9e15b9de7..0000000000 --- a/packages/form/src/widgets/object/object.widget.html +++ /dev/null @@ -1,68 +0,0 @@ - -
{{ title }}
- -
- - -
- -
-
-
-
-
- - - - - - - -
- - -
- - {{ title }} - - {{ ui.optional }} - - -
-
- -
diff --git a/packages/form/src/widgets/object/object.widget.ts b/packages/form/src/widgets/object/object.widget.ts index 7f5ec73e71..c4513a06d0 100644 --- a/packages/form/src/widgets/object/object.widget.ts +++ b/packages/form/src/widgets/object/object.widget.ts @@ -11,7 +11,84 @@ import { ObjectLayoutWidget } from '../../widget'; @Component({ selector: 'sf-object', - templateUrl: './object.widget.html', + template: ` + @if (!noTitle && title) { +
{{ title }}
+ } + @if (grid) { +
+ @for (i of list; track $index) { + @if (i.property.visible && i.show) { +
+ +
+ } + } +
+ } @else { + @for (i of list; track $index) { + @if (i.property.visible && i.show) { + + } + } + } +
+ @if (type === 'card') { + + +
+ @if (showExpand) { + + } + {{ title }} + @if (ui.optional || oh) { + + {{ ui.optional }} + @if (oh) { + + } + + } +
+
+ +
+ } @else { + + }`, preserveWhitespaces: false, encapsulation: ViewEncapsulation.None }) diff --git a/packages/form/src/widgets/radio/radio.widget.html b/packages/form/src/widgets/radio/radio.widget.html deleted file mode 100644 index 974ed1a7af..0000000000 --- a/packages/form/src/widgets/radio/radio.widget.html +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - diff --git a/packages/form/src/widgets/radio/radio.widget.ts b/packages/form/src/widgets/radio/radio.widget.ts index 8b5fe120a6..ae27582818 100644 --- a/packages/form/src/widgets/radio/radio.widget.ts +++ b/packages/form/src/widgets/radio/radio.widget.ts @@ -8,7 +8,36 @@ import { ControlUIWidget } from '../../widget'; @Component({ selector: 'sf-radio', - templateUrl: './radio.widget.html', + template: ` + + @if (styleType) { + @for (option of data; track $index) { + + } + } @else { + @for (option of data; track $index) { + + } + } + + `, preserveWhitespaces: false, encapsulation: ViewEncapsulation.None }) diff --git a/packages/form/src/widgets/rate/rate.widget.html b/packages/form/src/widgets/rate/rate.widget.html deleted file mode 100644 index a7c5e18e1d..0000000000 --- a/packages/form/src/widgets/rate/rate.widget.html +++ /dev/null @@ -1,13 +0,0 @@ - - - {{ text }} - diff --git a/packages/form/src/widgets/rate/rate.widget.ts b/packages/form/src/widgets/rate/rate.widget.ts deleted file mode 100644 index fd4e565b91..0000000000 --- a/packages/form/src/widgets/rate/rate.widget.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { Component, OnInit, ViewEncapsulation } from '@angular/core'; - -import { SFRateWidgetSchema } from './schema'; -import { toBool } from '../../utils'; -import { ControlUIWidget } from '../../widget'; - -@Component({ - selector: 'sf-rate', - templateUrl: './rate.widget.html', - preserveWhitespaces: false, - encapsulation: ViewEncapsulation.None -}) -export class RateWidget extends ControlUIWidget implements OnInit { - count!: number; - allowHalf!: boolean; - allowClear!: boolean; - autoFocus!: boolean; - hasText = false; - - get text(): string { - return (this.ui.text as string).replace('{{value}}', this.formProperty.value); - } - - ngOnInit(): void { - const { schema, ui } = this; - this.count = schema.maximum || 5; - this.allowHalf = (schema.multipleOf || 0.5) === 0.5; - this.allowClear = toBool(ui.allowClear, true); - this.autoFocus = toBool(ui.autoFocus, false); - this.hasText = !!ui.text; - } -} diff --git a/packages/form/src/widgets/select/demo/custom-dropdown-menu.md b/packages/form/src/widgets/select/demo/custom-dropdown-menu.md index 8e1a6d8821..b2c7e6bd2f 100644 --- a/packages/form/src/widgets/select/demo/custom-dropdown-menu.md +++ b/packages/form/src/widgets/select/demo/custom-dropdown-menu.md @@ -21,7 +21,9 @@ import { NzMessageService } from 'ng-zorro-antd/message'; @Component({ selector: 'app-demo', template: ` - + @if (schema) { + + }
diff --git a/packages/form/src/widgets/select/index.en-US.md b/packages/form/src/widgets/select/index.en-US.md index 86eb1046f3..1e47676607 100644 --- a/packages/form/src/widgets/select/index.en-US.md +++ b/packages/form/src/widgets/select/index.en-US.md @@ -2,6 +2,7 @@ title: select subtitle: Select type: Widgets +order: 6 --- Select. diff --git a/packages/form/src/widgets/select/index.zh-CN.md b/packages/form/src/widgets/select/index.zh-CN.md index 063c0db520..79b45db6f9 100644 --- a/packages/form/src/widgets/select/index.zh-CN.md +++ b/packages/form/src/widgets/select/index.zh-CN.md @@ -2,6 +2,7 @@ title: select subtitle: 选择器 type: Widgets +order: 6 --- 下拉选择器。 diff --git a/packages/form/src/widgets/select/select.widget.html b/packages/form/src/widgets/select/select.widget.html deleted file mode 100644 index be0bbd535b..0000000000 --- a/packages/form/src/widgets/select/select.widget.html +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - - - - - - - - {{ ui.searchLoadingText }} - - - diff --git a/packages/form/src/widgets/select/select.widget.ts b/packages/form/src/widgets/select/select.widget.ts index 9cf2b3a837..9e5759666f 100644 --- a/packages/form/src/widgets/select/select.widget.ts +++ b/packages/form/src/widgets/select/select.widget.ts @@ -12,7 +12,72 @@ import { ControlUIWidget } from '../../widget'; @Component({ selector: 'sf-select', - templateUrl: './select.widget.html', + template: ` + + @if (!loading && !hasGroup) { + @for (o of data; track $index) { + + } + } + @if (!loading && hasGroup) { + @for (i of data; track $index) { + + @for (o of i.children; track $index) { + + } + + } + } + @if (loading) { + + + {{ ui.searchLoadingText }} + + } + + `, preserveWhitespaces: false, encapsulation: ViewEncapsulation.None }) diff --git a/packages/form/src/widgets/slider/slider.widget.html b/packages/form/src/widgets/slider/slider.widget.html deleted file mode 100644 index 5ec0c26c41..0000000000 --- a/packages/form/src/widgets/slider/slider.widget.html +++ /dev/null @@ -1,17 +0,0 @@ - - - diff --git a/packages/form/src/widgets/slider/slider.widget.ts b/packages/form/src/widgets/slider/slider.widget.ts deleted file mode 100644 index 459b4a8839..0000000000 --- a/packages/form/src/widgets/slider/slider.widget.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { Component, OnInit, ViewEncapsulation } from '@angular/core'; - -import { NzMarks, NzSliderValue } from 'ng-zorro-antd/slider'; - -import { SFSliderWidgetSchema } from './schema'; -import { ControlUIWidget } from '../../widget'; - -@Component({ - selector: 'sf-slider', - templateUrl: './slider.widget.html', - preserveWhitespaces: false, - encapsulation: ViewEncapsulation.None -}) -export class SliderWidget extends ControlUIWidget implements OnInit { - min!: number; - max!: number; - step!: number; - marks: NzMarks | null = null; - included!: boolean; - - ngOnInit(): void { - const { minimum, maximum, multipleOf } = this.schema; - this.min = minimum || 0; - this.max = maximum || 100; - this.step = multipleOf || 1; - - const { marks, included } = this.ui; - this.marks = marks || null; - this.included = typeof included === 'undefined' ? true : included; - } - - _formatter = (value: number): string => { - const { formatter } = this.ui; - if (formatter) return formatter(value); - return `${value}`; - }; - - _afterChange(value: NzSliderValue): void { - const { afterChange } = this.ui; - if (afterChange) return afterChange(value); - } -} diff --git a/packages/form/src/widgets/string/index.en-US.md b/packages/form/src/widgets/string/index.en-US.md index 0e5c69a03e..c6b191c6ef 100644 --- a/packages/form/src/widgets/string/index.en-US.md +++ b/packages/form/src/widgets/string/index.en-US.md @@ -2,6 +2,7 @@ title: string subtitle: Input type: Widgets +order: 3 --- Default widget, A basic widget for getting the user input is a text field. diff --git a/packages/form/src/widgets/string/index.zh-CN.md b/packages/form/src/widgets/string/index.zh-CN.md index f68672367b..7767ec6ecf 100644 --- a/packages/form/src/widgets/string/index.zh-CN.md +++ b/packages/form/src/widgets/string/index.zh-CN.md @@ -2,6 +2,7 @@ title: string subtitle: 文本框 type: Widgets +order: 3 --- 默认小部件,一般用于字符串元素。 diff --git a/packages/form/src/widgets/string/string.widget.html b/packages/form/src/widgets/string/string.widget.html deleted file mode 100644 index 5c02b664c9..0000000000 --- a/packages/form/src/widgets/string/string.widget.html +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - - diff --git a/packages/form/src/widgets/string/string.widget.ts b/packages/form/src/widgets/string/string.widget.ts index e59e85d443..36a2e1b17e 100644 --- a/packages/form/src/widgets/string/string.widget.ts +++ b/packages/form/src/widgets/string/string.widget.ts @@ -7,7 +7,52 @@ import { ControlUIWidget } from '../../widget'; @Component({ selector: 'sf-string', - templateUrl: './string.widget.html', + template: ` + + + + + @if (type === 'addon') { + + + + } @else { + + } + `, preserveWhitespaces: false, encapsulation: ViewEncapsulation.None }) diff --git a/packages/form/src/widgets/tag/tag.widget.html b/packages/form/src/widgets/tag/tag.widget.html deleted file mode 100644 index feb5e91a6b..0000000000 --- a/packages/form/src/widgets/tag/tag.widget.html +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - {{ i.label }} - - - - - diff --git a/packages/form/src/widgets/tag/tag.widget.ts b/packages/form/src/widgets/tag/tag.widget.ts deleted file mode 100644 index 52a0e81aa0..0000000000 --- a/packages/form/src/widgets/tag/tag.widget.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { Component, ViewEncapsulation } from '@angular/core'; - -import { SFTagWidgetSchema } from './schema'; -import { SFValue } from '../../interface'; -import { SFSchemaEnum } from '../../schema'; -import { getData } from '../../utils'; -import { ControlUIWidget } from '../../widget'; - -@Component({ - selector: 'sf-tag', - templateUrl: './tag.widget.html', - preserveWhitespaces: false, - encapsulation: ViewEncapsulation.None -}) -export class TagWidget extends ControlUIWidget { - data: SFSchemaEnum[] = []; - - reset(value: SFValue): void { - getData(this.schema, this.ui, value).subscribe(list => { - this.data = list; - this.detectChanges(); - }); - } - - onChange(item: SFSchemaEnum): void { - item.checked = !item.checked; - this.updateValue(); - if (this.ui.checkedChange) { - this.ui.checkedChange(item.checked); - } - } - - _close(e: MouseEvent): void { - if (this.ui.onClose) this.ui.onClose(e); - } - - private updateValue(): void { - this.formProperty.setValue( - this.data.filter(w => w.checked).map(i => i.value), - false - ); - } -} diff --git a/packages/form/src/widgets/text/text.widget.html b/packages/form/src/widgets/text/text.widget.html deleted file mode 100644 index b06c254a79..0000000000 --- a/packages/form/src/widgets/text/text.widget.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - diff --git a/packages/form/src/widgets/text/text.widget.ts b/packages/form/src/widgets/text/text.widget.ts index 5a81063541..cc01452863 100644 --- a/packages/form/src/widgets/text/text.widget.ts +++ b/packages/form/src/widgets/text/text.widget.ts @@ -7,7 +7,21 @@ import { ControlUIWidget } from '../../widget'; @Component({ selector: 'sf-text', - templateUrl: './text.widget.html', + template: ` + @if (ui.html) { + + } @else { + + } + `, preserveWhitespaces: false, encapsulation: ViewEncapsulation.None }) diff --git a/packages/form/src/widgets/textarea/textarea.widget.html b/packages/form/src/widgets/textarea/textarea.widget.html deleted file mode 100644 index 396cd3b149..0000000000 --- a/packages/form/src/widgets/textarea/textarea.widget.html +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - - - - - - diff --git a/packages/form/src/widgets/textarea/textarea.widget.ts b/packages/form/src/widgets/textarea/textarea.widget.ts index 24e124c3bc..aba35a75ac 100644 --- a/packages/form/src/widgets/textarea/textarea.widget.ts +++ b/packages/form/src/widgets/textarea/textarea.widget.ts @@ -7,7 +7,59 @@ import { ControlUIWidget } from '../../widget'; @Component({ selector: 'sf-textarea', - templateUrl: './textarea.widget.html', + template: ` + + + + + @if (ui.maxCharacterCount) { + + + + } @else { + + } + `, preserveWhitespaces: false, encapsulation: ViewEncapsulation.None }) diff --git a/packages/form/src/widgets/time/time.widget.html b/packages/form/src/widgets/time/time.widget.html deleted file mode 100644 index 0509498b46..0000000000 --- a/packages/form/src/widgets/time/time.widget.html +++ /dev/null @@ -1,26 +0,0 @@ - - - diff --git a/packages/form/src/widgets/transfer/transfer.widget.html b/packages/form/src/widgets/transfer/transfer.widget.html deleted file mode 100644 index 766e55b115..0000000000 --- a/packages/form/src/widgets/transfer/transfer.widget.html +++ /dev/null @@ -1,18 +0,0 @@ - - - diff --git a/packages/form/src/widgets/tree-select/tree-select.widget.html b/packages/form/src/widgets/tree-select/tree-select.widget.html deleted file mode 100644 index dbe3e76727..0000000000 --- a/packages/form/src/widgets/tree-select/tree-select.widget.html +++ /dev/null @@ -1,36 +0,0 @@ - - - diff --git a/packages/form/src/widgets/tree-select/tree-select.widget.ts b/packages/form/src/widgets/tree-select/tree-select.widget.ts deleted file mode 100644 index 9b67ec3ceb..0000000000 --- a/packages/form/src/widgets/tree-select/tree-select.widget.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { Component, OnInit, ViewEncapsulation } from '@angular/core'; - -import { NzFormatEmitEvent, NzTreeNode } from 'ng-zorro-antd/core/tree'; - -import { SFTreeSelectWidgetSchema } from './schema'; -import { SFValue } from '../../interface'; -import { SFSchemaEnum } from '../../schema'; -import { getData, toBool } from '../../utils'; -import { ControlUIWidget } from '../../widget'; - -@Component({ - selector: 'sf-tree-select', - templateUrl: './tree-select.widget.html', - preserveWhitespaces: false, - encapsulation: ViewEncapsulation.None -}) -export class TreeSelectWidget extends ControlUIWidget implements OnInit { - i!: SFTreeSelectWidgetSchema; - data: SFSchemaEnum[] = []; - asyncData = false; - - ngOnInit(): void { - const { ui } = this; - this.i = { - allowClear: ui.allowClear, - showSearch: toBool(ui.showSearch, false), - dropdownMatchSelectWidth: toBool(ui.dropdownMatchSelectWidth, true), - multiple: toBool(ui.multiple, false), - checkable: toBool(ui.checkable, false), - showIcon: toBool(ui.showIcon, false), - showExpand: toBool(ui.showExpand, true), - showLine: toBool(ui.showLine, false), - checkStrictly: toBool(ui.checkStrictly, false), - hideUnMatched: toBool(ui.hideUnMatched, false), - defaultExpandAll: toBool(ui.defaultExpandAll, false), - displayWith: ui.displayWith || ((node: NzTreeNode) => node.title) - }; - this.asyncData = typeof ui.expandChange === 'function'; - } - - reset(value: SFValue): void { - getData(this.schema, this.ui, value).subscribe(list => { - this.data = list; - this.detectChanges(); - }); - } - - change(value: string[] | string): void { - if (this.ui.change) this.ui.change(value); - this.setValue(value); - } - - expandChange(e: NzFormatEmitEvent): void { - const { ui } = this; - if (typeof ui.expandChange !== 'function') return; - ui.expandChange(e).subscribe(res => { - e.node!.clearChildren(); - e.node!.addChildren(res); - this.detectChanges(); - }); - } -} diff --git a/packages/form/src/widgets/upload/upload.widget.html b/packages/form/src/widgets/upload/upload.widget.html deleted file mode 100644 index 7c2fb28e2d..0000000000 --- a/packages/form/src/widgets/upload/upload.widget.html +++ /dev/null @@ -1,46 +0,0 @@ - - - - - -
-
- -

-

-

-
- - - -
-
-
diff --git a/packages/form/widgets-third/markdown/index.en-US.md b/packages/form/widgets-third/markdown/index.en-US.md deleted file mode 100644 index bd8c40c7a5..0000000000 --- a/packages/form/widgets-third/markdown/index.en-US.md +++ /dev/null @@ -1,92 +0,0 @@ ---- -title: markdown -subtitle: Markdown Editor -type: Third Widgets ---- - -Markdown Editor - -> Note: third party widget is not registered by default, see details from [Customize Widgets](https://ng-alain.com/form/customize/en). - -## How to Use - -**Installation dependencies** - -Since the Markdown editor relies on a third-party plug-in [ngx-simplemde](https://github.com/cipchk/ngx-simplemde), the dependency should be installed first when using it - -`npm i -S ngx-simplemde` - - -**Import module** - -- The project built using the latest scaffolding provides a third-party widget registration entry: `src/app/shared/json-schema/json-schema.module.ts` -- `MarkdownWidget` needs to be declared in `JsonSchemaModule` - -```ts -export const SCHEMA_THIRDS_COMPONENTS = [MarkdownWidget]; - -@NgModule({ - declarations: SCHEMA_THIRDS_COMPONENTS, - imports: [ - SharedModule, - DelonFormModule.forRoot(), - SimplemdeModule.forRoot({ style: 'default' }), - ], - exports: SCHEMA_THIRDS_COMPONENTS -}) -export class JsonSchemaModule { -} -``` - -**Widget registration** - -- Register the widget to the `WidgetRegistry` registry - -```ts -export class JsonSchemaModule { - constructor(widgetRegistry: WidgetRegistry) { - widgetRegistry.register(MarkdownWidget.KEY, MarkdownWidget); - } -} -``` - -**Import resources** - -Import the corresponding resources in `angular.json`. - -```json -"styles": [ - "src/styles.less" -] -"scripts": [ - "node_modules/simplemde-antd/dist/simplemde.min.js" -] -``` - -**Import styles** - -After using `style.less` as the style entry, you need to import the style file of `ngx-simplemde` and define some custom variables - -```less -// src/style.less -@import 'ngx-simplemde/index.less'; -// Change existing parameters here: -@simplemde-icon-url: '//at.alicdn.com/t/font_700857_mnodkd1cp9l766r'; -@simplemde-statusbar-lines: 'Lins:'; -@simplemde-statusbar-words: 'words:'; -@simplemde-statusbar-characters: '字符:'; -@simplemde-statusbar-counts: '字数:'; -``` - -## Source Code - -[Source Code](https://github.com/ng-alain/delon/tree/master/packages/form/widgets-third/markdown). - -## API - -### ui - -| Property | Description | Type | Default | -|----------|-------------|------|---------| -| `[options]` | Configuration options, [official website](https://github.com/cipchk/ngx-simplemde) | `object` | - | -| `[change]` | Callback function when content in editor is changed | `(md: string) => void` | - | diff --git a/packages/form/widgets-third/markdown/index.zh-CN.md b/packages/form/widgets-third/markdown/index.zh-CN.md deleted file mode 100644 index 3af542c09d..0000000000 --- a/packages/form/widgets-third/markdown/index.zh-CN.md +++ /dev/null @@ -1,91 +0,0 @@ ---- -title: markdown -subtitle: Markdown编辑器 -type: Third Widgets ---- - -Markdown编辑器。 - -> 注:第三方小部件默认并未注册,细节见[定制小部件](https://ng-alain.com/form/customize)。 - -## 如何使用 - -**安装依赖** - -由于Markdown编辑器依赖第三方插件[ngx-simplemde](https://github.com/cipchk/ngx-simplemde),所以使用时应首先安装依赖 - -`npm i -S ngx-simplemde` - -**导入模块** - -- 使用最新脚手架搭建出的项目提供了第三方控件注册入口: `src/app/shared/json-schema/json-schema.module.ts` -- 需将`MarkdownWidget`在`JsonSchemaModule`中进行声明 - -```ts -export const SCHEMA_THIRDS_COMPONENTS = [MarkdownWidget]; - -@NgModule({ - declarations: SCHEMA_THIRDS_COMPONENTS, - imports: [ - SharedModule, - DelonFormModule.forRoot(), - SimplemdeModule.forRoot({ style: 'default' }), - ], - exports: SCHEMA_THIRDS_COMPONENTS -}) -export class JsonSchemaModule { -} -``` - -**控件注册** - -- 将控件注册到`WidgetRegistry`注册表中 - -```ts -export class JsonSchemaModule { - constructor(widgetRegistry: WidgetRegistry) { - widgetRegistry.register(MarkdownWidget.KEY, MarkdownWidget); - } -} -``` - -**导入资源** - -在 `angular.json` 导入相应资源。 - -```json -"styles": [ - "src/styles.less" -] -"scripts": [ - "node_modules/simplemde-antd/dist/simplemde.min.js" -] -``` - -**导入样式** - -使用`style.less`作为样式入口后,需将`ngx-simplemde`的样式文件导入进去,并定义一些自定义变量 - -```less -// src/style.less -@import 'ngx-simplemde/index.less'; -// Change existing parameters here: -@simplemde-icon-url: '//at.alicdn.com/t/font_700857_mnodkd1cp9l766r'; -@simplemde-statusbar-lines: 'Lins:'; -@simplemde-statusbar-words: 'words:'; -@simplemde-statusbar-characters: '字符:'; -@simplemde-statusbar-counts: '字数:'; -``` - -## 源代码 - -[源代码](https://github.com/ng-alain/delon/tree/master/packages/form/widgets-third/markdown)。 - -## API - -### ui 属性 - -| 成员 | 说明 | 类型 | 默认值 | -|----|----|----|-----| -| `[options]` | 配置项说明,[见官网](https://github.com/cipchk/ngx-simplemde) | `object` | - | -| `[change]` | 编辑器内容发生改变时会触发该事件 | `(md: string) => void` | - | diff --git a/packages/form/widgets-third/markdown/markdown.widget.ts b/packages/form/widgets-third/markdown/markdown.widget.ts deleted file mode 100644 index e751dcdd8f..0000000000 --- a/packages/form/widgets-third/markdown/markdown.widget.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { Component } from '@angular/core'; - -import { ControlWidget } from '@delon/form'; - -@Component({ - selector: 'sf-md', - template: ` - - - - ` -}) -export class MarkdownWidget extends ControlWidget { - static readonly KEY = 'md'; - - _change(value: string): void { - this.setValue(value); - if (this.ui.change) this.ui.change(value); - } -} diff --git a/packages/form/widgets-third/monaco-editor/demo/simple.md b/packages/form/widgets-third/monaco-editor/demo/simple.md new file mode 100644 index 0000000000..c645d5f23a --- /dev/null +++ b/packages/form/widgets-third/monaco-editor/demo/simple.md @@ -0,0 +1,48 @@ +--- +title: + zh-CN: 基础样例 + en-US: Basic Usage +order: 0 +--- + +## zh-CN + +最简单的用法。 + +## en-US + +Simplest of usage. + +```ts +import { Component } from '@angular/core'; + +import { SFSchema } from '@delon/form'; +import type { MonacoEditorWidgetSchema } from '@delon/form/widgets-third/monaco-editor'; +import { NzMessageService } from 'ng-zorro-antd/message'; + +@Component({ + selector: 'app-demo', + template: `` +}) +export class DemoComponent { + schema: SFSchema = { + properties: { + json: { + type: 'string', + title: 'JSON', + default: `{"string": "abc","number": 1 }`, + ui: { + widget: 'monaco-editor', + options: { language: 'json' } + } as MonacoEditorWidgetSchema + } + } + }; + + constructor(private msg: NzMessageService) {} + + submit(value: {}): void { + this.msg.success(JSON.stringify(value)); + } +} +``` diff --git a/packages/form/widgets-third/monaco-editor/index.en-US.md b/packages/form/widgets-third/monaco-editor/index.en-US.md new file mode 100644 index 0000000000..c634a84bce --- /dev/null +++ b/packages/form/widgets-third/monaco-editor/index.en-US.md @@ -0,0 +1,33 @@ +--- +title: monaco-editor +subtitle: Monaco Editor +type: Third Widgets +--- + +Markdown Editor + +## How to Use + +**Installation dependencies** + +`yarn add @ng-util/monaco-editor` + +**Import module** + +- 1. Import `NuMonacoEditorModule.forRoot()` in `app.module.ts` +- 2. Import `MonacoEditorWidgetModule` in [json-schema.module.ts](https://github.com/ng-alain/ng-alain/blob/master/src/app/shared/json-schema/json-schema.module.ts#L11). + +> For more Monaco Editor configuration, please refer to [@ng-util/monaco-editor](https://github.com/ng-util/ng-util/blob/master/packages/monaco-editor/README.md#usage). + +## API + +### ui + +| Property | Description | Type | Default | +|----------|-------------|------|---------| +| `[options]` | Configuration options, [official website](https://microsoft.github.io/monaco-editor/docs.html) | `monaco.editor.IStandaloneEditorConstructionOptions` | - | +| `[delay]` | Time of lazy loading | `number` | - | +| `[change]` | Callback function when content in editor is changed | `(value: string) => void` | - | +| `[height]` | Height of monaco editor | `string` | `200px` | +| `[model]` | Model of monaco editor | `NuMonacoEditorModel` | - | +| `(event)` | Event callback | `EventEmitter` | - | diff --git a/packages/form/widgets-third/monaco-editor/index.ts b/packages/form/widgets-third/monaco-editor/index.ts new file mode 100644 index 0000000000..ee8fae3f39 --- /dev/null +++ b/packages/form/widgets-third/monaco-editor/index.ts @@ -0,0 +1,11 @@ +import { SFWidgetProvideConfig } from '@delon/form'; + +import { MonacoEditorWidget } from './widget'; + +export * from './widget'; +export * from './schema'; +export * from './module'; + +export function withMonacoEditorWidget(): SFWidgetProvideConfig { + return { KEY: MonacoEditorWidget.KEY, type: MonacoEditorWidget }; +} diff --git a/packages/form/widgets-third/monaco-editor/index.zh-CN.md b/packages/form/widgets-third/monaco-editor/index.zh-CN.md new file mode 100644 index 0000000000..1fa7cfbbc8 --- /dev/null +++ b/packages/form/widgets-third/monaco-editor/index.zh-CN.md @@ -0,0 +1,33 @@ +--- +title: monaco-editor +subtitle: Monaco Editor +type: Third Widgets +--- + +Markdown编辑器。 + +## 如何使用 + +**安装依赖** + +`yarn add @ng-util/monaco-editor` + +**导入模块** + +- 1、在 `app.module.ts` 下导入 `NuMonacoEditorModule.forRoot()` +- 2、在 [json-schema.module.ts](https://github.com/ng-alain/ng-alain/blob/master/src/app/shared/json-schema/json-schema.module.ts#L11) 导入 `MonacoEditorWidgetModule`。 + +> 关于更多 Monaco Editor 配置请参考 [@ng-util/monaco-editor](https://github.com/ng-util/ng-util/blob/master/packages/monaco-editor/README.md#usage)。 + +## API + +### ui 属性 + +| 成员 | 说明 | 类型 | 默认值 | +|----|----|----|-----| +| `[options]` | 配置项说明,[见官网](https://microsoft.github.io/monaco-editor/docs.html) | `monaco.editor.IStandaloneEditorConstructionOptions` | - | +| `[delay]` | 延迟加载时间 | `number` | - | +| `[change]` | 编辑器内容发生改变时会触发该事件 | `(value: string) => void` | - | +| `[height]` | Height of monaco editor | `string` | `200px` | +| `[model]` | Model of monaco editor | `NuMonacoEditorModel` | - | +| `(event)` | Event callback | `EventEmitter` | - | diff --git a/packages/form/widgets-third/monaco-editor/module.ts b/packages/form/widgets-third/monaco-editor/module.ts new file mode 100644 index 0000000000..f2d6dd6cc2 --- /dev/null +++ b/packages/form/widgets-third/monaco-editor/module.ts @@ -0,0 +1,17 @@ +import { NgModule } from '@angular/core'; +import { FormsModule } from '@angular/forms'; + +import { NuMonacoEditorModule } from '@ng-util/monaco-editor'; + +import { DelonFormModule, WidgetRegistry } from '@delon/form'; + +import { MonacoEditorWidget } from './widget'; + +@NgModule({ + imports: [FormsModule, DelonFormModule, NuMonacoEditorModule, MonacoEditorWidget] +}) +export class MonacoEditorWidgetModule { + constructor(widgetRegistry: WidgetRegistry) { + widgetRegistry.register(MonacoEditorWidget.KEY, MonacoEditorWidget); + } +} diff --git a/packages/form/widgets-third/monaco-editor/ng-package.json b/packages/form/widgets-third/monaco-editor/ng-package.json new file mode 100644 index 0000000000..7f578c054e --- /dev/null +++ b/packages/form/widgets-third/monaco-editor/ng-package.json @@ -0,0 +1,6 @@ +{ + "lib": { + "flatModuleFile": "widgets-third-monaco-editor", + "entryFile": "index.ts" + } +} diff --git a/packages/form/widgets-third/monaco-editor/schema.ts b/packages/form/widgets-third/monaco-editor/schema.ts new file mode 100644 index 0000000000..87767d34bd --- /dev/null +++ b/packages/form/widgets-third/monaco-editor/schema.ts @@ -0,0 +1,19 @@ +import type { NuMonacoEditorEvent, NuMonacoEditorModel } from '@ng-util/monaco-editor'; + +import type { SFUISchemaItem } from '@delon/form'; + +export interface MonacoEditorWidgetSchema extends SFUISchemaItem { + options?: monaco.editor.IStandaloneEditorConstructionOptions; + delay?: number; + change?: (value: string) => void; + model?: NuMonacoEditorModel; + /** + * Height of monaco editor, default: `200px` + */ + height?: string; + /** + * Whether to automatically format the document + */ + autoFormat?: boolean; + event?: (ev: NuMonacoEditorEvent) => void; +} diff --git a/packages/form/widgets-third/monaco-editor/widget.ts b/packages/form/widgets-third/monaco-editor/widget.ts new file mode 100644 index 0000000000..4bfc441c80 --- /dev/null +++ b/packages/form/widgets-third/monaco-editor/widget.ts @@ -0,0 +1,50 @@ +import { Component, ViewEncapsulation } from '@angular/core'; +import { FormsModule } from '@angular/forms'; + +import { NuMonacoEditorComponent, type NuMonacoEditorEvent } from '@ng-util/monaco-editor'; + +import { ControlUIWidget, DelonFormModule } from '@delon/form'; + +import type { MonacoEditorWidgetSchema } from './schema'; + +@Component({ + selector: 'sf-widget-monaco-editor', + template: ` + + + + `, + preserveWhitespaces: false, + encapsulation: ViewEncapsulation.None, + standalone: true, + imports: [FormsModule, DelonFormModule, NuMonacoEditorComponent] +}) +export class MonacoEditorWidget extends ControlUIWidget { + static readonly KEY = 'monaco-editor'; + + _change(value: string): void { + this.setValue(value); + if (this.ui.change) this.ui.change(value); + } + + _event(ev: NuMonacoEditorEvent): void { + if (this.ui.event) this.ui.event(ev); + } +} diff --git a/packages/form/widgets-third/tinymce/demo/simple.md b/packages/form/widgets-third/tinymce/demo/simple.md index aa3747179c..56759c9d25 100644 --- a/packages/form/widgets-third/tinymce/demo/simple.md +++ b/packages/form/widgets-third/tinymce/demo/simple.md @@ -15,12 +15,14 @@ Simplest of usage. ```ts import { Component } from '@angular/core'; + import { SFSchema } from '@delon/form'; +import type { TinymceWidgetSchema } from '@delon/form/widgets-third/tinymce'; import { NzMessageService } from 'ng-zorro-antd/message'; @Component({ selector: 'app-demo', - template: ``, + template: `` }) export class DemoComponent { schema: SFSchema = { @@ -29,10 +31,10 @@ export class DemoComponent { type: 'string', title: '描述', ui: { - widget: 'tinymce', - }, - }, - }, + widget: 'tinymce' + } as TinymceWidgetSchema + } + } }; constructor(private msg: NzMessageService) {} diff --git a/packages/form/widgets-third/tinymce/index.en-US.md b/packages/form/widgets-third/tinymce/index.en-US.md index 971f772c2d..59da25000f 100644 --- a/packages/form/widgets-third/tinymce/index.en-US.md +++ b/packages/form/widgets-third/tinymce/index.en-US.md @@ -4,9 +4,9 @@ subtitle: Tinymce Rich Text type: Third Widgets --- -Tinymce rich text +Tinymce rich text. -> Note: third party widget is not registered by default, see details from [Customize Widgets](https://ng-alain.com/form/customize/en). +## How to Use **Installation dependencies** @@ -14,68 +14,12 @@ Since the Tinymce editor relies on a third-party plug-in [ngx-tinymce](https://g `npm i -S ngx-tinymce` - **Import module** -- The project built using the latest scaffolding provides a third-party widget registration entry: `src/app/shared/json-schema/json-schema.module.ts`. -- `TinymceWidget` needs to be declared in `JsonSchemaModule`. -- Take the given plug-in list and toolbar as a full list, which can be deleted according to needs. - -```ts -export const SCHEMA_THIRDS_COMPONENTS = [TinymceWidget]; - -@NgModule({ - declarations: SCHEMA_THIRDS_COMPONENTS, - imports: [ - SharedModule, - DelonFormModule.forRoot(), - NgxTinymceModule.forRoot({ - baseURL: './assets/tinymce/', - config: { - language: 'zh_CN', - language_url: './assets/tinymce/langs/zh_CN.js', - branding: false, - paste_data_images: true, - automatic_uploads: false, - menubar: true, - toolbar_mode: 'wrap', - plugins: - 'print preview searchreplace autolink directionality visualblocks visualchars fullscreen image link media template code codesample table charmap hr pagebreak nonbreaking anchor insertdatetime advlist lists wordcount image textpattern help emoticons autosave autoresize', - toolbar: - 'code undo redo restoredraft | cut copy | forecolor backcolor bold italic underline strikethrough link anchor | alignleft aligncenter alignright alignjustify outdent indent | styleselect formatselect fontselect fontsizeselect | bullist numlist | blockquote subscript superscript removeformat | table image media charmap emoticons hr pagebreak insertdatetime print preview | fullscreen | indent2em' - } - }) - ], - exports: SCHEMA_THIRDS_COMPONENTS -}) -export class JsonSchemaModule { -} -``` - -**Widget registration** - -- Register the widget to the `WidgetRegistry` registry - -```ts -export class JsonSchemaModule { - constructor(widgetRegistry: WidgetRegistry) { - widgetRegistry.register(TinymceWidget.KEY, TinymceWidget); - } -} -``` - -**Import resources** - -Tinymce supports a highly customized plug-in mode, which can effectively reduce the size of the package body by configuring plug-ins and static resources. - -- Plug-in download: [CustomBuilds](https://www.tiny.cloud/get-tiny/custom-builds/) -- Language package download: [LanguagePackages](https://www.tiny.cloud/get-tiny/language-packages/) -- Put the downloaded plug-in into a directory accessible by `baseURL` -- Put the downloaded language pack into a directory accessible by `language_url` - -## Source Code +- 1. Import `NgxTinymceModule.forRoot()` in `app.module.ts` +- 2. Import `TinymceWidgetModule` in [json-schema.module.ts](https://github.com/ng-alain/ng-alain/blob/master/src/app/shared/json-schema/json-schema.module.ts#L11). -[Source Code](https://github.com/ng-alain/delon/tree/master/packages/form/widgets-third/tinymce)。 +> For more tinymce configuration, please refer to [ngx-tinymce](https://github.com/cipchk/ngx-tinymce). ## API @@ -83,6 +27,10 @@ Tinymce supports a highly customized plug-in mode, which can effectively reduce | Property | Description | Type | Default | |----------|-------------|------|---------| -| `[config]` | Configuration options, [official website](https://www.tinymce.com/docs/configure/integration-and-setup/) | `object` | - | -| `[loading]` | Initial hint message | `string` | `加载中...` | -| `[change]` | Callback function when content in editor is changed | `(html: string) => void` | - | +| config | `any` | | see [configure](https://www.tinymce.com/docs/configure/integration-and-setup/) | +| loading | `string,TemplateRef` | - | Loading status of tinymce | +| disabled | `boolean` | `false` | Set tinymce mode is `readonly` if `true` | +| inline | `boolean` | `false` | Inline editor | +| delay | `number` | 0 | Delayed rendering, unit is 'millisecond' | +| placeholder | `string` | - | Placeholder for tinymce, **NOTE:** dependent on [tinymce-placeholder](https://github.com/mohan/tinymce-placeholder) | +| ready | `EventEmitter` | - | Tinymce ready callback | diff --git a/packages/form/widgets-third/tinymce/index.ts b/packages/form/widgets-third/tinymce/index.ts new file mode 100644 index 0000000000..610e73bfbc --- /dev/null +++ b/packages/form/widgets-third/tinymce/index.ts @@ -0,0 +1,11 @@ +import type { SFWidgetProvideConfig } from '@delon/form'; + +import { TinymceWidget } from './widget'; + +export * from './widget'; +export * from './schema'; +export * from './module'; + +export function withTinymceWidget(): SFWidgetProvideConfig { + return { KEY: TinymceWidget.KEY, type: TinymceWidget }; +} diff --git a/packages/form/widgets-third/tinymce/index.zh-CN.md b/packages/form/widgets-third/tinymce/index.zh-CN.md index 8eff664e2f..9891838e53 100644 --- a/packages/form/widgets-third/tinymce/index.zh-CN.md +++ b/packages/form/widgets-third/tinymce/index.zh-CN.md @@ -6,79 +6,18 @@ type: Third Widgets Tinymce富文本。 -> 注:第三方小部件默认并未注册,细节见[定制小部件](https://ng-alain.com/form/customize)。 - - ## 如何使用 -**安装依赖** - -由于Tinymce编辑器依赖第三方插件[ngx-tinymce](https://github.com/cipchk/ngx-tinymce),所以使用时应首先安装依赖 +**安装依赖** `npm i -S ngx-tinymce` **导入模块** -- 使用最新脚手架搭建出的项目提供了第三方控件注册入口: `src/app/shared/json-schema/json-schema.module.ts` -- 需将`TinymceWidget`在`JsonSchemaModule`中进行声明 -- 以给出的插件列表与工具栏为全量列表,可根据需求删减 - -```ts -export const SCHEMA_THIRDS_COMPONENTS = [TinymceWidget]; - -@NgModule({ - declarations: SCHEMA_THIRDS_COMPONENTS, - imports: [ - SharedModule, - DelonFormModule.forRoot(), - NgxTinymceModule.forRoot({ - baseURL: './assets/tinymce/', - config: { - language: 'zh_CN', - language_url: './assets/tinymce/langs/zh_CN.js', - branding: false, - paste_data_images: true, - automatic_uploads: false, - menubar: true, - toolbar_mode: 'wrap', - plugins: - 'print preview searchreplace autolink directionality visualblocks visualchars fullscreen image link media template code codesample table charmap hr pagebreak nonbreaking anchor insertdatetime advlist lists wordcount image textpattern help emoticons autosave autoresize', - toolbar: - 'code undo redo restoredraft | cut copy | forecolor backcolor bold italic underline strikethrough link anchor | alignleft aligncenter alignright alignjustify outdent indent | styleselect formatselect fontselect fontsizeselect | bullist numlist | blockquote subscript superscript removeformat | table image media charmap emoticons hr pagebreak insertdatetime print preview | fullscreen | indent2em' - } - }) - ], - exports: SCHEMA_THIRDS_COMPONENTS -}) -export class JsonSchemaModule { -} -``` - -**控件注册** - -- 将控件注册到`WidgetRegistry`注册表中 - -```ts -export class JsonSchemaModule { - constructor(widgetRegistry: WidgetRegistry) { - widgetRegistry.register(TinymceWidget.KEY, TinymceWidget); - } -} -``` - -**导入静态资源** - -Tinymce支持高度自定义的插件模式,通过配置插件和静态资源可以有效的减少包体体积 - -- 插件下载: [CustomBuilds](https://www.tiny.cloud/get-tiny/custom-builds/) -- 语言包下载: [LanguagePackages](https://www.tiny.cloud/get-tiny/language-packages/) -- 将下载好的插件放入`baseURL`可访问到的目录内 -- 将下载好的语言包放入`language_url`可访问到的目录内 - - -## 源代码 +- 1、在 `app.module.ts` 下导入 `NgxTinymceModule.forRoot()` +- 2、在 [json-schema.module.ts](https://github.com/ng-alain/ng-alain/blob/master/src/app/shared/json-schema/json-schema.module.ts#L11) 导入 `TinymceWidgetModule`。 -[源代码](https://github.com/ng-alain/delon/tree/master/packages/form/widgets-third/tinymce)。 +> 关于更多 tinymce 配置请参考 [ngx-tinymce](https://github.com/cipchk/ngx-tinymce)。 ## API @@ -86,6 +25,10 @@ Tinymce支持高度自定义的插件模式,通过配置插件和静态资源可 | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| -| `[config]` | 配置项说明,[见官网](https://www.tinymce.com/docs/configure/integration-and-setup/) | `object` | - | -| `[loading]` | 初始化提示文本 | `string` | `加载中...` | -| `[change]` | 编辑器内容发生改变时会触发该事件 | `(html: string) => void` | - | +| config | `any` | | see [configure](https://www.tinymce.com/docs/configure/integration-and-setup/) | +| loading | `string,TemplateRef` | - | Loading status of tinymce | +| disabled | `boolean` | `false` | Set tinymce mode is `readonly` if `true` | +| inline | `boolean` | `false` | Inline editor | +| delay | `number` | 0 | Delayed rendering, unit is 'millisecond' | +| placeholder | `string` | - | Placeholder for tinymce, **NOTE:** dependent on [tinymce-placeholder](https://github.com/mohan/tinymce-placeholder) | +| ready | `EventEmitter` | - | Tinymce ready callback | diff --git a/packages/form/widgets-third/tinymce/module.ts b/packages/form/widgets-third/tinymce/module.ts new file mode 100644 index 0000000000..37fd298e98 --- /dev/null +++ b/packages/form/widgets-third/tinymce/module.ts @@ -0,0 +1,17 @@ +import { NgModule } from '@angular/core'; +import { FormsModule } from '@angular/forms'; + +import { NgxTinymceModule } from 'ngx-tinymce'; + +import { DelonFormModule, WidgetRegistry } from '@delon/form'; + +import { TinymceWidget } from './widget'; + +@NgModule({ + imports: [FormsModule, DelonFormModule, NgxTinymceModule, TinymceWidget] +}) +export class TinymceWidgetModule { + constructor(widgetRegistry: WidgetRegistry) { + widgetRegistry.register(TinymceWidget.KEY, TinymceWidget); + } +} diff --git a/packages/form/widgets-third/tinymce/ng-package.json b/packages/form/widgets-third/tinymce/ng-package.json new file mode 100644 index 0000000000..ff7c84a23d --- /dev/null +++ b/packages/form/widgets-third/tinymce/ng-package.json @@ -0,0 +1,6 @@ +{ + "lib": { + "flatModuleFile": "widgets-third-tinymce", + "entryFile": "index.ts" + } +} diff --git a/packages/form/widgets-third/tinymce/schema.ts b/packages/form/widgets-third/tinymce/schema.ts new file mode 100644 index 0000000000..19a34fc9f6 --- /dev/null +++ b/packages/form/widgets-third/tinymce/schema.ts @@ -0,0 +1,12 @@ +import type { SFUISchemaItem } from '@delon/form'; +import type { NzSafeAny } from 'ng-zorro-antd/core/types'; + +export interface TinymceWidgetSchema extends SFUISchemaItem { + /** 默认配置项,对全局 Tinymce 有效 */ + config?: Record; + inline?: boolean; + /** 延迟加载(单位:毫秒),默认:`0` */ + delay?: number; + loading?: string; + ready?: (instance: NzSafeAny) => void; +} diff --git a/packages/form/widgets-third/tinymce/tinymce.widget.ts b/packages/form/widgets-third/tinymce/tinymce.widget.ts deleted file mode 100644 index 6594d5b652..0000000000 --- a/packages/form/widgets-third/tinymce/tinymce.widget.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { Component, OnInit } from '@angular/core'; - -import { ControlWidget } from '@delon/form'; - -@Component({ - selector: 'sf-tinymce', - template: ` - - - - ` -}) -export class TinymceWidget extends ControlWidget implements OnInit { - static readonly KEY = 'tinymce'; - - config!: Record; - loading!: string; - - ngOnInit(): void { - this.loading = this.ui.loading || '加载中……'; - this.config = this.ui.config || {}; - } - - change(value: string): void { - if (this.ui.change) this.ui.change(value); - this.setValue(value); - } -} diff --git a/packages/form/widgets-third/tinymce/widget.ts b/packages/form/widgets-third/tinymce/widget.ts new file mode 100644 index 0000000000..b92ed85b4b --- /dev/null +++ b/packages/form/widgets-third/tinymce/widget.ts @@ -0,0 +1,49 @@ +import { Component, ViewEncapsulation } from '@angular/core'; +import { FormsModule } from '@angular/forms'; + +import { TinymceComponent } from 'ngx-tinymce'; + +import { ControlUIWidget, DelonFormModule } from '@delon/form'; +import type { NzSafeAny } from 'ng-zorro-antd/core/types'; + +import type { TinymceWidgetSchema } from './schema'; + +@Component({ + selector: 'sf-widget-tinymce', + template: ` + + + + `, + preserveWhitespaces: false, + encapsulation: ViewEncapsulation.None, + standalone: true, + imports: [FormsModule, DelonFormModule, TinymceComponent] +}) +export class TinymceWidget extends ControlUIWidget { + static readonly KEY = 'tinymce'; + + change(value: string): void { + this.setValue(value); + if (this.ui.change) this.ui.change(value); + } + + _ready(instance: NzSafeAny): void { + if (this.ui.ready) this.ui.ready(instance); + } +} diff --git a/packages/form/src/widgets/autocomplete/demo/simple.md b/packages/form/widgets/autocomplete/demo/simple.md similarity index 91% rename from packages/form/src/widgets/autocomplete/demo/simple.md rename to packages/form/widgets/autocomplete/demo/simple.md index 93a117f613..b55386039e 100644 --- a/packages/form/src/widgets/autocomplete/demo/simple.md +++ b/packages/form/widgets/autocomplete/demo/simple.md @@ -15,7 +15,8 @@ Simplest of usage. ```ts import { Component } from '@angular/core'; -import { SFAutoCompleteWidgetSchema, SFSchema } from '@delon/form'; +import { SFSchema } from '@delon/form'; +import type { SFAutoCompleteWidgetSchema } from '@delon/form/widgets/autocomplete'; import { NzMessageService } from 'ng-zorro-antd/message'; import { of } from 'rxjs'; diff --git a/packages/form/src/widgets/autocomplete/index.en-US.md b/packages/form/widgets/autocomplete/index.en-US.md similarity index 90% rename from packages/form/src/widgets/autocomplete/index.en-US.md rename to packages/form/widgets/autocomplete/index.en-US.md index b2d5042f8c..8425bf704c 100644 --- a/packages/form/src/widgets/autocomplete/index.en-US.md +++ b/packages/form/widgets/autocomplete/index.en-US.md @@ -1,11 +1,15 @@ --- title: autocomplete subtitle: Autocomplete -type: Widgets +type: Non-built-in widgets --- Input complete automatically. +## Import module + +Non-built-in modules, Should be import `AutoCompleteWidgetModule` in [json-schema.module.ts](https://github.com/ng-alain/ng-alain/blob/master/src/app/shared/json-schema/json-schema.module.ts#L11). + ## Data Source **Static** diff --git a/packages/form/widgets/autocomplete/index.ts b/packages/form/widgets/autocomplete/index.ts new file mode 100644 index 0000000000..d76a1db139 --- /dev/null +++ b/packages/form/widgets/autocomplete/index.ts @@ -0,0 +1,11 @@ +import type { SFWidgetProvideConfig } from '@delon/form'; + +import { AutoCompleteWidget } from './widget'; + +export * from './widget'; +export * from './schema'; +export * from './module'; + +export function withAutoCompleteWidget(): SFWidgetProvideConfig { + return { KEY: AutoCompleteWidget.KEY, type: AutoCompleteWidget }; +} diff --git a/packages/form/src/widgets/autocomplete/index.zh-CN.md b/packages/form/widgets/autocomplete/index.zh-CN.md similarity index 90% rename from packages/form/src/widgets/autocomplete/index.zh-CN.md rename to packages/form/widgets/autocomplete/index.zh-CN.md index 2baeea393b..859f4e6860 100644 --- a/packages/form/src/widgets/autocomplete/index.zh-CN.md +++ b/packages/form/widgets/autocomplete/index.zh-CN.md @@ -1,11 +1,15 @@ --- title: autocomplete subtitle: 自动完成 -type: Widgets +type: Non-built-in widgets --- 输入框自动完成功能。 +## 导入模块 + +非内置模块,需要额外在 [json-schema.module.ts](https://github.com/ng-alain/ng-alain/blob/master/src/app/shared/json-schema/json-schema.module.ts#L11) 导入 `AutoCompleteWidgetModule`。 + ## 数据源说明 **静态** diff --git a/packages/form/widgets/autocomplete/module.ts b/packages/form/widgets/autocomplete/module.ts new file mode 100644 index 0000000000..5022a11ea1 --- /dev/null +++ b/packages/form/widgets/autocomplete/module.ts @@ -0,0 +1,18 @@ +import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { FormsModule } from '@angular/forms'; + +import { DelonFormModule, WidgetRegistry } from '@delon/form'; +import { NzAutocompleteModule } from 'ng-zorro-antd/auto-complete'; +import { NzInputModule } from 'ng-zorro-antd/input'; + +import { AutoCompleteWidget } from './widget'; + +@NgModule({ + imports: [FormsModule, DelonFormModule, CommonModule, NzInputModule, NzAutocompleteModule, AutoCompleteWidget] +}) +export class AutoCompleteWidgetModule { + constructor(widgetRegistry: WidgetRegistry) { + widgetRegistry.register(AutoCompleteWidget.KEY, AutoCompleteWidget); + } +} diff --git a/packages/form/widgets/autocomplete/ng-package.json b/packages/form/widgets/autocomplete/ng-package.json new file mode 100644 index 0000000000..9d4a9afb0e --- /dev/null +++ b/packages/form/widgets/autocomplete/ng-package.json @@ -0,0 +1,6 @@ +{ + "lib": { + "flatModuleFile": "widgets-auto-complete", + "entryFile": "index.ts" + } +} diff --git a/packages/form/src/widgets/autocomplete/schema.ts b/packages/form/widgets/autocomplete/schema.ts similarity index 89% rename from packages/form/src/widgets/autocomplete/schema.ts rename to packages/form/widgets/autocomplete/schema.ts index dfa5243218..abe50891f7 100644 --- a/packages/form/src/widgets/autocomplete/schema.ts +++ b/packages/form/widgets/autocomplete/schema.ts @@ -1,11 +1,9 @@ import { Observable } from 'rxjs'; -import { NzAutocompleteOptionComponent } from 'ng-zorro-antd/auto-complete'; +import type { SFSchemaEnum, SFSchemaEnumType, SFUISchemaItem } from '@delon/form'; +import type { NzAutocompleteOptionComponent } from 'ng-zorro-antd/auto-complete'; import type { CompareWith } from 'ng-zorro-antd/core/types'; -import type { SFSchemaEnum, SFSchemaEnumType } from '../../schema'; -import type { SFUISchemaItem } from '../../schema/ui'; - export interface SFAutoCompleteWidgetSchema extends SFUISchemaItem { /** * 异步静态数据源 diff --git a/packages/form/src/widgets/autocomplete/autocomplete.widget.spec.ts b/packages/form/widgets/autocomplete/widget.spec.ts similarity index 94% rename from packages/form/src/widgets/autocomplete/autocomplete.widget.spec.ts rename to packages/form/widgets/autocomplete/widget.spec.ts index dc67d71d23..2e9db83d87 100644 --- a/packages/form/src/widgets/autocomplete/autocomplete.widget.spec.ts +++ b/packages/form/widgets/autocomplete/widget.spec.ts @@ -2,15 +2,15 @@ import { DebugElement } from '@angular/core'; import { ComponentFixture, fakeAsync, TestBed } from '@angular/core/testing'; import { of } from 'rxjs'; +import { mergeConfig, SFSchema, SFSchemaEnum } from '@delon/form'; import { createTestContext } from '@delon/testing'; import { AlainConfigService } from '@delon/util/config'; import { NzSafeAny } from 'ng-zorro-antd/core/types'; -import { AutoCompleteWidget } from './autocomplete.widget'; +import { withAutoCompleteWidget } from './index'; import { SFAutoCompleteWidgetSchema } from './schema'; -import { configureSFTestSuite, SFPage, TestFormComponent } from '../../../spec/base.spec'; -import { SFSchema, SFSchemaEnum } from '../../../src/schema/index'; -import { mergeConfig } from '../../config'; +import { AutoCompleteWidget } from './widget'; +import { configureSFTestSuite, SFPage, TestFormComponent } from '../../spec/base.spec'; describe('form: widget: autocomplete', () => { let fixture: ComponentFixture; @@ -19,7 +19,7 @@ describe('form: widget: autocomplete', () => { let page: SFPage; const widget = 'autocomplete'; - configureSFTestSuite(); + configureSFTestSuite({ widgets: [withAutoCompleteWidget()] }); beforeEach(() => { ({ fixture, dl, context } = createTestContext(TestFormComponent)); @@ -117,7 +117,7 @@ describe('form: widget: autocomplete', () => { expect(selectWidget.typing).toBe(`label1`); page.asyncEnd(); })); - it('with email of format', fakeAsync(() => { + xit('with email of format', fakeAsync(() => { const config = mergeConfig(TestBed.inject(AlainConfigService)); const typeValue = 'a'; page @@ -125,8 +125,7 @@ describe('form: widget: autocomplete', () => { properties: { a: { type: 'string', - format: 'email', - default: typeValue + format: 'email' } } }) diff --git a/packages/form/src/widgets/autocomplete/autocomplete.widget.ts b/packages/form/widgets/autocomplete/widget.ts similarity index 63% rename from packages/form/src/widgets/autocomplete/autocomplete.widget.ts rename to packages/form/widgets/autocomplete/widget.ts index 2a0acb9fd2..10e4363f44 100644 --- a/packages/form/src/widgets/autocomplete/autocomplete.widget.ts +++ b/packages/form/widgets/autocomplete/widget.ts @@ -1,23 +1,61 @@ +import { AsyncPipe } from '@angular/common'; import { Component, ViewChild, ViewEncapsulation } from '@angular/core'; -import { NgModel } from '@angular/forms'; +import { FormsModule, NgModel } from '@angular/forms'; import { Observable, of, debounceTime, map, mergeMap, startWith, takeUntil } from 'rxjs'; -import { NzAutocompleteOptionComponent } from 'ng-zorro-antd/auto-complete'; +import { ControlUIWidget, DelonFormModule, SFSchemaEnum, SFValue, getCopyEnum, getEnum, toBool } from '@delon/form'; +import { NzAutocompleteModule, NzAutocompleteOptionComponent } from 'ng-zorro-antd/auto-complete'; import type { NzSafeAny } from 'ng-zorro-antd/core/types'; +import { NzInputModule } from 'ng-zorro-antd/input'; -import { SFAutoCompleteWidgetSchema } from './schema'; -import { SFValue } from '../../interface'; -import { SFSchemaEnum } from '../../schema'; -import { getCopyEnum, getEnum, toBool } from '../../utils'; -import { ControlUIWidget } from '../../widget'; +import type { SFAutoCompleteWidgetSchema } from './schema'; @Component({ selector: 'sf-autocomplete', - templateUrl: './autocomplete.widget.html', + template: ` + + + @for (i of list | async; track i) { + {{ i.label }} + } + + `, preserveWhitespaces: false, - encapsulation: ViewEncapsulation.None + encapsulation: ViewEncapsulation.None, + standalone: true, + imports: [AsyncPipe, FormsModule, DelonFormModule, NzInputModule, NzAutocompleteModule] }) export class AutoCompleteWidget extends ControlUIWidget { + static readonly KEY = 'autocomplete'; + i: NzSafeAny = {}; list!: Observable; typing: string = ''; @@ -109,6 +147,10 @@ export class AutoCompleteWidget extends ControlUIWidget { - return of(!value || ~value.indexOf('@') ? [] : this.fixData.map(domain => `${value}@${domain.label}`)); + const res = + !value || typeof value !== 'string' || value?.indexOf('@') !== -1 + ? [] + : this.fixData.map(domain => `${value}@${domain.label}`); + return of(res); } } diff --git a/packages/form/src/widgets/cascader/demo/simple.md b/packages/form/widgets/cascader/demo/simple.md similarity index 95% rename from packages/form/src/widgets/cascader/demo/simple.md rename to packages/form/widgets/cascader/demo/simple.md index cf042d1e60..d12e5397fb 100644 --- a/packages/form/src/widgets/cascader/demo/simple.md +++ b/packages/form/widgets/cascader/demo/simple.md @@ -15,7 +15,8 @@ Simplest of usage. ```ts import { Component } from '@angular/core'; -import { SFCascaderWidgetSchema, SFSchema } from '@delon/form'; +import { SFSchema } from '@delon/form'; +import type { SFCascaderWidgetSchema } from '@delon/form/widgets/cascader'; import { NzMessageService } from 'ng-zorro-antd/message'; @Component({ diff --git a/packages/form/src/widgets/cascader/index.en-US.md b/packages/form/widgets/cascader/index.en-US.md similarity index 92% rename from packages/form/src/widgets/cascader/index.en-US.md rename to packages/form/widgets/cascader/index.en-US.md index 2133f875a4..858fa11a6b 100644 --- a/packages/form/src/widgets/cascader/index.en-US.md +++ b/packages/form/widgets/cascader/index.en-US.md @@ -1,11 +1,15 @@ --- title: cascader subtitle: Cascader -type: Widgets +type: Non-built-in widgets --- Usually, it's used in province/city/district, company hierarchy, category of things, etc. +## Import module + +Non-built-in modules, Should be import `CascaderWidgetModule` in [json-schema.module.ts](https://github.com/ng-alain/ng-alain/blob/master/src/app/shared/json-schema/json-schema.module.ts#L11). + ## Note - Value of `default` or `formData` should always be an array, for example, city cascade may only save leaf node `value`, but you need to manually provide the whole data chain `value` array diff --git a/packages/form/widgets/cascader/index.ts b/packages/form/widgets/cascader/index.ts new file mode 100644 index 0000000000..4a0f117553 --- /dev/null +++ b/packages/form/widgets/cascader/index.ts @@ -0,0 +1,11 @@ +import type { SFWidgetProvideConfig } from '@delon/form'; + +import { CascaderWidget } from './widget'; + +export * from './widget'; +export * from './schema'; +export * from './module'; + +export function withCascaderWidget(): SFWidgetProvideConfig { + return { KEY: CascaderWidget.KEY, type: CascaderWidget }; +} diff --git a/packages/form/src/widgets/cascader/index.zh-CN.md b/packages/form/widgets/cascader/index.zh-CN.md similarity index 92% rename from packages/form/src/widgets/cascader/index.zh-CN.md rename to packages/form/widgets/cascader/index.zh-CN.md index 9faa425356..2d7280c692 100644 --- a/packages/form/src/widgets/cascader/index.zh-CN.md +++ b/packages/form/widgets/cascader/index.zh-CN.md @@ -1,11 +1,15 @@ --- title: cascader subtitle: 级联选择 -type: Widgets +type: Non-built-in widgets --- 一般用于省市区,公司层级,事物分类等。 +## 导入模块 + +非内置模块,需要额外在 [json-schema.module.ts](https://github.com/ng-alain/ng-alain/blob/master/src/app/shared/json-schema/json-schema.module.ts#L11) 导入 `CascaderWidgetModule`。 + ## 注意事项 - `default` 或 `formData` 值始终应该保持一个数组,例如:城市级联可能只存储叶节点 `value`,此时需要手动处理并给出完整数据链 `value` 数组 diff --git a/packages/form/widgets/cascader/module.ts b/packages/form/widgets/cascader/module.ts new file mode 100644 index 0000000000..0fe2af46e1 --- /dev/null +++ b/packages/form/widgets/cascader/module.ts @@ -0,0 +1,16 @@ +import { NgModule } from '@angular/core'; +import { FormsModule } from '@angular/forms'; + +import { DelonFormModule, WidgetRegistry } from '@delon/form'; +import { NzCascaderModule } from 'ng-zorro-antd/cascader'; + +import { CascaderWidget } from './widget'; + +@NgModule({ + imports: [FormsModule, DelonFormModule, NzCascaderModule, CascaderWidget] +}) +export class CascaderWidgetModule { + constructor(widgetRegistry: WidgetRegistry) { + widgetRegistry.register(CascaderWidget.KEY, CascaderWidget); + } +} diff --git a/packages/form/widgets/cascader/ng-package.json b/packages/form/widgets/cascader/ng-package.json new file mode 100644 index 0000000000..69f4052d28 --- /dev/null +++ b/packages/form/widgets/cascader/ng-package.json @@ -0,0 +1,6 @@ +{ + "lib": { + "flatModuleFile": "widgets-cascader", + "entryFile": "index.ts" + } +} diff --git a/packages/form/src/widgets/cascader/schema.ts b/packages/form/widgets/cascader/schema.ts similarity index 96% rename from packages/form/src/widgets/cascader/schema.ts rename to packages/form/widgets/cascader/schema.ts index 09f7068c45..b3bcc9d09b 100644 --- a/packages/form/src/widgets/cascader/schema.ts +++ b/packages/form/widgets/cascader/schema.ts @@ -1,8 +1,8 @@ +import { SFUISchemaItem } from '@delon/form'; import { NzCascaderExpandTrigger, NzCascaderOption, NzShowSearchOptions } from 'ng-zorro-antd/cascader'; import { NgStyleInterface, NzSafeAny } from 'ng-zorro-antd/core/types'; -import { CascaderWidget } from './cascader.widget'; -import { SFUISchemaItem } from '../../schema/ui'; +import type { CascaderWidget } from './widget'; export interface SFCascaderWidgetSchema extends SFUISchemaItem { /** diff --git a/packages/form/src/widgets/cascader/cascader.widget.spec.ts b/packages/form/widgets/cascader/widget.spec.ts similarity index 95% rename from packages/form/src/widgets/cascader/cascader.widget.spec.ts rename to packages/form/widgets/cascader/widget.spec.ts index f0c60873a3..e976f57721 100644 --- a/packages/form/src/widgets/cascader/cascader.widget.spec.ts +++ b/packages/form/widgets/cascader/widget.spec.ts @@ -3,8 +3,9 @@ import { ComponentFixture, fakeAsync } from '@angular/core/testing'; import { createTestContext } from '@delon/testing'; -import { CascaderWidget } from './cascader.widget'; -import { configureSFTestSuite, SFPage, TestFormComponent } from '../../../spec/base.spec'; +import { withCascaderWidget } from './index'; +import { CascaderWidget } from './widget'; +import { configureSFTestSuite, SFPage, TestFormComponent } from '../../spec/base.spec'; describe('form: widget: cascader', () => { let fixture: ComponentFixture; @@ -13,7 +14,7 @@ describe('form: widget: cascader', () => { let page: SFPage; const widget = 'cascader'; - configureSFTestSuite(); + configureSFTestSuite({ widgets: [withCascaderWidget()] }); beforeEach(() => { ({ fixture, dl, context } = createTestContext(TestFormComponent)); diff --git a/packages/form/src/widgets/cascader/cascader.widget.ts b/packages/form/widgets/cascader/widget.ts similarity index 50% rename from packages/form/src/widgets/cascader/cascader.widget.ts rename to packages/form/widgets/cascader/widget.ts index 63ff4b8101..3debdd80c1 100644 --- a/packages/form/src/widgets/cascader/cascader.widget.ts +++ b/packages/form/widgets/cascader/widget.ts @@ -1,21 +1,57 @@ import { Component, OnInit, ViewEncapsulation } from '@angular/core'; +import { FormsModule } from '@angular/forms'; -import { NzCascaderOption } from 'ng-zorro-antd/cascader'; +import { ControlUIWidget, DelonFormModule, SFSchemaEnum, SFValue, getData, toBool } from '@delon/form'; +import { NzCascaderModule, NzCascaderOption } from 'ng-zorro-antd/cascader'; import type { NzSafeAny } from 'ng-zorro-antd/core/types'; -import { SFCascaderWidgetSchema } from './schema'; -import { SFValue } from '../../interface'; -import { SFSchemaEnum } from '../../schema'; -import { getData, toBool } from '../../utils'; -import { ControlUIWidget } from '../../widget'; +import type { SFCascaderWidgetSchema } from './schema'; @Component({ selector: 'sf-cascader', - templateUrl: './cascader.widget.html', + template: ` + + `, preserveWhitespaces: false, - encapsulation: ViewEncapsulation.None + encapsulation: ViewEncapsulation.None, + standalone: true, + imports: [FormsModule, DelonFormModule, NzCascaderModule] }) export class CascaderWidget extends ControlUIWidget implements OnInit { + static readonly KEY = 'cascader'; + clearText!: string; showArrow!: boolean; showInput!: boolean; diff --git a/packages/form/widgets/color/demo/simple.md b/packages/form/widgets/color/demo/simple.md new file mode 100644 index 0000000000..5cd26ed1ba --- /dev/null +++ b/packages/form/widgets/color/demo/simple.md @@ -0,0 +1,106 @@ +--- +title: + zh-CN: 基础样例 + en-US: Basic Usage +order: 0 +--- + +## zh-CN + +最简单的用法。 + +## en-US + +Simplest of usage. + +```ts +import { Component } from '@angular/core'; + +import { SFSchema } from '@delon/form'; +import type { SFColorWidgetSchema } from '@delon/form/widgets/color'; +import { NzMessageService } from 'ng-zorro-antd/message'; + +@Component({ + selector: 'app-demo', + template: `` +}) +export class DemoComponent { + schema: SFSchema = { + properties: { + base: { + type: 'string', + title: 'Base', + ui: { + widget: 'color', + title: 'Pls choose a color', + change: console.log + } as SFColorWidgetSchema + }, + showText: { + type: 'string', + title: 'Show Text', + ui: { + widget: 'color', + showText: true, + trigger: 'hover', + change: console.log + } as SFColorWidgetSchema + }, + defaultValue: { + type: 'string', + title: 'Default Value', + ui: { + widget: 'color', + showText: true, + defaultValue: '#0a0', + change: console.log + } as SFColorWidgetSchema + }, + clearColor: { + type: 'string', + title: 'Clear Color', + ui: { + widget: 'color', + allowClear: true, + change: console.log + } as SFColorWidgetSchema + }, + disabled: { + type: 'string', + title: 'Disabled', + ui: { + widget: 'color', + showText: true + } as SFColorWidgetSchema, + readOnly: true + }, + rgb: { + type: 'string', + title: 'RGB', + ui: { + widget: 'color', + format: 'rgb', + showText: true, + change: console.log, + formatChange: console.log + } as SFColorWidgetSchema + }, + block: { + type: 'string', + title: 'Block Color', + ui: { + widget: 'color', + block: true + } as SFColorWidgetSchema, + default: '#f50' + } + } + }; + + constructor(private msg: NzMessageService) {} + + submit(value: {}): void { + this.msg.success(JSON.stringify(value)); + } +} +``` diff --git a/packages/form/widgets/color/index.en-US.md b/packages/form/widgets/color/index.en-US.md new file mode 100644 index 0000000000..5bf803ec77 --- /dev/null +++ b/packages/form/widgets/color/index.en-US.md @@ -0,0 +1,33 @@ +--- +title: color +subtitle: Color +type: Non-built-in widgets +--- + +Used when the user needs to customize the color selection. + +## How to Use + +**Installation dependencies** + +`yarn add ng-antd-color-picker` + +**Import Module** + +Non-built-in modules, Should be import `ColorWidgetModule` in [json-schema.module.ts](https://github.com/ng-alain/ng-alain/blob/master/src/app/shared/json-schema/json-schema.module.ts#L11). + +## API + +### ui + +| Property | Description | Type | Default | +|----------|-------------|------|---------| +| `[format]` | Format of color | `rgb`|`hex`|`hsb` | `hex` | +| `[defaultValue]` | Default value of color | `string`|`NzColor` | `false` | +| `[allowClear]` | Allow clearing color selected | `boolean` | `false` | +| `[trigger]` | ColorPicker trigger mode | `hover`|`click` | `click` | +| `[showText]` | Show color text | `boolean` | `false` | +| `[title]` | Setting the title of the color picker | `TemplateRef`|`string` | - | +| `(change)` | Callback when value is changed | `EventEmitter<{ color: NzColor; format: string }>` | - | +| `(formatChange)` | Callback when `format` is changed | `EventEmitter<'rgb'|'hex'|'hsb'>` | - | +| `[block]` | Color Block | `boolean` | `false` | diff --git a/packages/form/widgets/color/index.ts b/packages/form/widgets/color/index.ts new file mode 100644 index 0000000000..99374ca7ce --- /dev/null +++ b/packages/form/widgets/color/index.ts @@ -0,0 +1,11 @@ +import type { SFWidgetProvideConfig } from '@delon/form'; + +import { ColorWidget } from './widget'; + +export * from './widget'; +export * from './schema'; +export * from './module'; + +export function withColorWidget(): SFWidgetProvideConfig { + return { KEY: ColorWidget.KEY, type: ColorWidget }; +} diff --git a/packages/form/widgets/color/index.zh-CN.md b/packages/form/widgets/color/index.zh-CN.md new file mode 100644 index 0000000000..da9644edda --- /dev/null +++ b/packages/form/widgets/color/index.zh-CN.md @@ -0,0 +1,33 @@ +--- +title: color +subtitle: 颜色 +type: Non-built-in widgets +--- + +当用户需要自定义颜色选择的时候使用。 + +## 如何使用 + +**安装依赖** + +`yarn add ng-antd-color-picker` + +**导入模块** + +非内置模块,需要额外在 [json-schema.module.ts](https://github.com/ng-alain/ng-alain/blob/master/src/app/shared/json-schema/json-schema.module.ts#L11) 导入 `ColorWidgetModule`。 + +## API + +### ui 属性 + +| 成员 | 说明 | 类型 | 默认值 | +|----|----|----|-----| +| `[format]` | 颜色格式 | `rgb`|`hex`|`hsb` | `hex` | +| `[defaultValue]` | 颜色默认的值 | `string`|`NzColor` | - | +| `[allowClear]` | 允许清除选择的颜色 | `boolean` | `false` | +| `[trigger]` | 颜色选择器的触发模式 | `hover`|`click` | `click` | +| `[showText]` | 显示颜色文本 | `boolean` | `false` | +| `[title]` | 设置颜色选择器的标题 | `TemplateRef`|`string` | - | +| `(change)` | 颜色变化的回调 | `EventEmitter<{ color: NzColor; format: string }>` | - | +| `(formatChange)` | 颜色格式变化的回调 | `EventEmitter<'rgb'|'hex'|'hsb'>` | - | +| `[block]` | 是否颜色块 | `boolean` | `false` | diff --git a/packages/form/widgets/color/module.ts b/packages/form/widgets/color/module.ts new file mode 100644 index 0000000000..ba8a58a0f0 --- /dev/null +++ b/packages/form/widgets/color/module.ts @@ -0,0 +1,17 @@ +import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { FormsModule } from '@angular/forms'; + +import { DelonFormModule, WidgetRegistry } from '@delon/form'; +import { NzColorPickerModule } from 'ng-zorro-antd/color-picker'; + +import { ColorWidget } from './widget'; + +@NgModule({ + imports: [FormsModule, CommonModule, DelonFormModule, NzColorPickerModule, ColorWidget] +}) +export class ColorWidgetModule { + constructor(widgetRegistry: WidgetRegistry) { + widgetRegistry.register(ColorWidget.KEY, ColorWidget); + } +} diff --git a/packages/form/widgets/color/ng-package.json b/packages/form/widgets/color/ng-package.json new file mode 100644 index 0000000000..ec57a9fa30 --- /dev/null +++ b/packages/form/widgets/color/ng-package.json @@ -0,0 +1,6 @@ +{ + "lib": { + "flatModuleFile": "widgets-color", + "entryFile": "index.ts" + } +} diff --git a/packages/form/widgets/color/schema.ts b/packages/form/widgets/color/schema.ts new file mode 100644 index 0000000000..819eefe595 --- /dev/null +++ b/packages/form/widgets/color/schema.ts @@ -0,0 +1,47 @@ +import type { TemplateRef } from '@angular/core'; + +import type { SFUISchemaItem } from '@delon/form'; +import type { NzColor, NzColorPickerFormatType, NzColorPickerTriggerType } from 'ng-zorro-antd/color-picker'; + +export interface SFColorWidgetSchema extends SFUISchemaItem { + /** + * Format of color + */ + format?: NzColorPickerFormatType | null; + /** + * Default value of color + */ + defaultValue?: string | NzColor | null; + /** + * ColorPicker trigger mode + */ + trigger?: NzColorPickerTriggerType | null; + /** + * Setting the title of the color picker + */ + title?: TemplateRef | string; + /** + * Triggers for customizing color panels. + */ + flipFlop?: TemplateRef | string | null; + /** + * Show color text + */ + showText?: boolean; + /** + * Allow clearing color selected + */ + allowClear?: boolean; + /** + * Callback when value is changed + */ + change?: (ev: { color: NzColor; format: string }) => void; + /** + * Callback when `format` is changed + */ + formatChange?: (color: NzColorPickerFormatType) => void; + /** + * Color Block + */ + block?: boolean; +} diff --git a/packages/form/widgets/color/widget.spec.ts b/packages/form/widgets/color/widget.spec.ts new file mode 100644 index 0000000000..e9c60869fa --- /dev/null +++ b/packages/form/widgets/color/widget.spec.ts @@ -0,0 +1,49 @@ +import { DebugElement } from '@angular/core'; +import { ComponentFixture, fakeAsync } from '@angular/core/testing'; + +import { SFSchema } from '@delon/form'; +import { createTestContext } from '@delon/testing'; + +import { SFColorWidgetSchema, withColorWidget } from './index'; +import { configureSFTestSuite, SFPage, TestFormComponent } from '../../spec/base.spec'; + +describe('form: widget: color', () => { + let fixture: ComponentFixture; + let dl: DebugElement; + let context: TestFormComponent; + let page: SFPage; + + configureSFTestSuite({ widgets: [withColorWidget()] }); + + beforeEach(() => { + ({ fixture, dl, context } = createTestContext(TestFormComponent)); + page = new SFPage(context.comp); + page.cleanOverlay().prop(dl, context, fixture); + }); + + it('should be working', fakeAsync(() => { + const change = jasmine.createSpy(); + const formatChange = jasmine.createSpy(); + const s: SFSchema = { + properties: { + a: { + type: 'string', + ui: { + widget: 'color', + defaultValue: '#f50', + change, + formatChange + } as SFColorWidgetSchema + } + } + }; + page + .newSchema(s) + .typeEvent('click', '.ant-color-picker-trigger') + .typeEvent('click', 'nz-select') + .typeEvent('click', 'nz-option-container nz-option-item:nth-child(2)'); + expect(page.getValue('/a')).toBe('hsb(20, 100%, 100%)'); + expect(change).toHaveBeenCalled(); + expect(formatChange).toHaveBeenCalled(); + })); +}); diff --git a/packages/form/widgets/color/widget.ts b/packages/form/widgets/color/widget.ts new file mode 100644 index 0000000000..fb8b40a0ef --- /dev/null +++ b/packages/form/widgets/color/widget.ts @@ -0,0 +1,54 @@ +import { Component, ViewEncapsulation } from '@angular/core'; +import { FormsModule } from '@angular/forms'; + +import { ControlUIWidget, DelonFormModule } from '@delon/form'; +import { NzColorPickerModule, type NzColor, type NzColorPickerFormatType } from 'ng-zorro-antd/color-picker'; + +import type { SFColorWidgetSchema } from './schema'; + +@Component({ + selector: 'sf-color', + template: ` + @if (ui.block) { + + } @else { + + } + `, + preserveWhitespaces: false, + encapsulation: ViewEncapsulation.None, + standalone: true, + imports: [FormsModule, DelonFormModule, NzColorPickerModule] +}) +export class ColorWidget extends ControlUIWidget { + static readonly KEY = 'color'; + + _change(ev: { color: NzColor; format: string }): void { + if (this.ui.change) this.ui.change(ev); + } + + _formatChange(ev: NzColorPickerFormatType): void { + if (this.ui.formatChange) this.ui.formatChange(ev); + } +} diff --git a/packages/form/src/widgets/mention/demo/simple.md b/packages/form/widgets/mention/demo/simple.md similarity index 93% rename from packages/form/src/widgets/mention/demo/simple.md rename to packages/form/widgets/mention/demo/simple.md index 3f11d108a6..4b7446c3da 100644 --- a/packages/form/src/widgets/mention/demo/simple.md +++ b/packages/form/widgets/mention/demo/simple.md @@ -15,7 +15,8 @@ Simplest of usage. ```ts import { Component } from '@angular/core'; -import { SFMentionWidgetSchema, SFSchema } from '@delon/form'; +import { SFSchema } from '@delon/form'; +import type { SFMentionWidgetSchema } from '@delon/form/widgets/mention'; import { MentionOnSearchTypes } from 'ng-zorro-antd/mention'; import { NzMessageService } from 'ng-zorro-antd/message'; import { of, delay } from 'rxjs'; diff --git a/packages/form/src/widgets/mention/index.en-US.md b/packages/form/widgets/mention/index.en-US.md similarity index 87% rename from packages/form/src/widgets/mention/index.en-US.md rename to packages/form/widgets/mention/index.en-US.md index 07032bb3b6..1629117690 100644 --- a/packages/form/src/widgets/mention/index.en-US.md +++ b/packages/form/widgets/mention/index.en-US.md @@ -1,11 +1,15 @@ --- title: mention subtitle: Mention -type: Widgets +type: Non-built-in widgets --- Mention widget. +## Import module + +Non-built-in modules, Should be import `MentionWidgetModule` in [json-schema.module.ts](https://github.com/ng-alain/ng-alain/blob/master/src/app/shared/json-schema/json-schema.module.ts#L11). + ## Note - You **must** set `valueWith` parameter if there is no `label` property in data. diff --git a/packages/form/widgets/mention/index.ts b/packages/form/widgets/mention/index.ts new file mode 100644 index 0000000000..73b654794f --- /dev/null +++ b/packages/form/widgets/mention/index.ts @@ -0,0 +1,11 @@ +import type { SFWidgetProvideConfig } from '@delon/form'; + +import { MentionWidget } from './widget'; + +export * from './widget'; +export * from './schema'; +export * from './module'; + +export function withMentionWidget(): SFWidgetProvideConfig { + return { KEY: MentionWidget.KEY, type: MentionWidget }; +} diff --git a/packages/form/src/widgets/mention/index.zh-CN.md b/packages/form/widgets/mention/index.zh-CN.md similarity index 87% rename from packages/form/src/widgets/mention/index.zh-CN.md rename to packages/form/widgets/mention/index.zh-CN.md index 4fdc7e8f1b..53dac63338 100644 --- a/packages/form/src/widgets/mention/index.zh-CN.md +++ b/packages/form/widgets/mention/index.zh-CN.md @@ -1,11 +1,15 @@ --- title: mention subtitle: 提及 -type: Widgets +type: Non-built-in widgets --- 提及组件。 +## 导入模块 + +非内置模块,需要额外在 [json-schema.module.ts](https://github.com/ng-alain/ng-alain/blob/master/src/app/shared/json-schema/json-schema.module.ts#L11) 导入 `MentionWidgetModule`。 + ## 注意事项 - 若数据中不包括 `label` 属性,则**务必**指定 `valueWith` 参数。 diff --git a/packages/form/widgets/mention/module.ts b/packages/form/widgets/mention/module.ts new file mode 100644 index 0000000000..8a3f4ac244 --- /dev/null +++ b/packages/form/widgets/mention/module.ts @@ -0,0 +1,18 @@ +import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { FormsModule } from '@angular/forms'; + +import { DelonFormModule, WidgetRegistry } from '@delon/form'; +import { NzInputModule } from 'ng-zorro-antd/input'; +import { NzMentionModule } from 'ng-zorro-antd/mention'; + +import { MentionWidget } from './widget'; + +@NgModule({ + imports: [FormsModule, DelonFormModule, NzMentionModule, NzInputModule, CommonModule, MentionWidget] +}) +export class MentionWidgetModule { + constructor(widgetRegistry: WidgetRegistry) { + widgetRegistry.register(MentionWidget.KEY, MentionWidget); + } +} diff --git a/packages/form/widgets/mention/ng-package.json b/packages/form/widgets/mention/ng-package.json new file mode 100644 index 0000000000..ffc3837bae --- /dev/null +++ b/packages/form/widgets/mention/ng-package.json @@ -0,0 +1,6 @@ +{ + "lib": { + "flatModuleFile": "widgets-mention", + "entryFile": "index.ts" + } +} diff --git a/packages/form/src/widgets/mention/schema.ts b/packages/form/widgets/mention/schema.ts similarity index 93% rename from packages/form/src/widgets/mention/schema.ts rename to packages/form/widgets/mention/schema.ts index c05b4a813f..2af590e4fb 100644 --- a/packages/form/src/widgets/mention/schema.ts +++ b/packages/form/widgets/mention/schema.ts @@ -1,12 +1,10 @@ import { Observable } from 'rxjs'; +import type { SFSchemaEnumType, SFUISchemaItem } from '@delon/form'; import { NzSafeAny, NzSizeLDSType } from 'ng-zorro-antd/core/types'; import { AutoSizeType } from 'ng-zorro-antd/input'; import { MentionOnSearchTypes } from 'ng-zorro-antd/mention'; -import { SFSchemaEnumType } from '../../schema'; -import { SFUISchemaItem } from '../../schema/ui'; - export interface SFMentionWidgetSchema extends SFUISchemaItem { /** * 异步静态数据源 diff --git a/packages/form/src/widgets/mention/mention.widget.spec.ts b/packages/form/widgets/mention/widget.spec.ts similarity index 91% rename from packages/form/src/widgets/mention/mention.widget.spec.ts rename to packages/form/widgets/mention/widget.spec.ts index ed1cf63304..85e4165223 100644 --- a/packages/form/src/widgets/mention/mention.widget.spec.ts +++ b/packages/form/widgets/mention/widget.spec.ts @@ -2,12 +2,13 @@ import { DebugElement } from '@angular/core'; import { ComponentFixture, fakeAsync } from '@angular/core/testing'; import { of } from 'rxjs'; +import type { SFSchema } from '@delon/form'; import { createTestContext } from '@delon/testing'; import { NzSafeAny } from 'ng-zorro-antd/core/types'; -import { MentionWidget } from './mention.widget'; -import { configureSFTestSuite, SFPage, TestFormComponent } from '../../../spec/base.spec'; -import { SFSchema } from '../../../src/schema/index'; +import { withMentionWidget } from './index'; +import { MentionWidget } from './widget'; +import { configureSFTestSuite, SFPage, TestFormComponent } from '../../spec/base.spec'; const DATA = ['asdf', 'cipchk', '中文', 'にほんご']; @@ -18,7 +19,7 @@ describe('form: widget: mention', () => { let page: SFPage; const widget = 'mention'; - configureSFTestSuite(); + configureSFTestSuite({ widgets: [withMentionWidget()] }); beforeEach(() => { ({ fixture, dl, context } = createTestContext(TestFormComponent)); diff --git a/packages/form/src/widgets/mention/mention.widget.ts b/packages/form/widgets/mention/widget.ts similarity index 53% rename from packages/form/src/widgets/mention/mention.widget.ts rename to packages/form/widgets/mention/widget.ts index 3e1f88d725..01c689f497 100644 --- a/packages/form/src/widgets/mention/mention.widget.ts +++ b/packages/form/widgets/mention/widget.ts @@ -1,22 +1,75 @@ import { Component, OnInit, ViewChild, ViewEncapsulation } from '@angular/core'; +import { FormsModule } from '@angular/forms'; import { map, tap } from 'rxjs'; +import { ControlUIWidget, DelonFormModule, SFSchemaEnum, SFValue, getData, getEnum } from '@delon/form'; import type { NzSafeAny } from 'ng-zorro-antd/core/types'; -import { MentionOnSearchTypes, NzMentionComponent } from 'ng-zorro-antd/mention'; +import { NzInputModule } from 'ng-zorro-antd/input'; +import { MentionOnSearchTypes, NzMentionComponent, NzMentionModule } from 'ng-zorro-antd/mention'; -import { SFMentionWidgetSchema } from './schema'; -import { SFValue } from '../../interface'; -import { SFSchemaEnum } from '../../schema'; -import { getData, getEnum } from '../../utils'; -import { ControlUIWidget } from '../../widget'; +import type { SFMentionWidgetSchema } from './schema'; @Component({ selector: 'sf-mention', - templateUrl: './mention.widget.html', + template: ` + + @if (ui.inputStyle === 'textarea') { + + } @else { + + } + + `, preserveWhitespaces: false, - encapsulation: ViewEncapsulation.None + encapsulation: ViewEncapsulation.None, + standalone: true, + imports: [FormsModule, DelonFormModule, NzInputModule, NzMentionModule] }) export class MentionWidget extends ControlUIWidget implements OnInit { + static readonly KEY = 'mention'; + @ViewChild('mentions', { static: true }) private mentionChild!: NzMentionComponent; data: SFSchemaEnum[] = []; i: NzSafeAny; diff --git a/packages/form/widgets/qr-code/demo/simple.md b/packages/form/widgets/qr-code/demo/simple.md new file mode 100644 index 0000000000..866f3d01de --- /dev/null +++ b/packages/form/widgets/qr-code/demo/simple.md @@ -0,0 +1,67 @@ +--- +title: + zh-CN: 基础样例 + en-US: Basic Usage +order: 0 +--- + +## zh-CN + +最简单的用法。 + +## en-US + +Simplest of usage. + +```ts +import { Component } from '@angular/core'; + +import { SFSchema } from '@delon/form'; +import type { SFQrCodeWidgetSchema } from '@delon/form/widgets/qr-code'; +import { NzMessageService } from 'ng-zorro-antd/message'; + +@Component({ + selector: 'app-demo', + template: `` +}) +export class DemoComponent { + schema: SFSchema = { + properties: { + base: { + type: 'string', + title: 'Base', + default: 'https://ng-alain.com/', + ui: { + widget: 'qr-code', + refresh: console.log + } as SFQrCodeWidgetSchema + }, + icon: { + type: 'string', + title: 'With Icon', + default: 'https://ng-alain.com/', + ui: { + widget: 'qr-code', + icon: 'https://ng-alain.com/assets/logo-color.svg', + bordered: true + } as SFQrCodeWidgetSchema + }, + color: { + type: 'string', + title: 'Color', + default: 'https://ng-alain.com/', + ui: { + widget: 'qr-code', + color: '#f50' + } as SFQrCodeWidgetSchema + } + } + }; + + constructor(private msg: NzMessageService) {} + + submit(value: {}): void { + this.msg.success(JSON.stringify(value)); + } +} +``` diff --git a/packages/form/widgets/qr-code/index.en-US.md b/packages/form/widgets/qr-code/index.en-US.md new file mode 100644 index 0000000000..83e40a3b5a --- /dev/null +++ b/packages/form/widgets/qr-code/index.en-US.md @@ -0,0 +1,28 @@ +--- +title: qr-code +subtitle: QRCode +type: Non-built-in widgets +--- + +Used when the link needs to be converted into a QR Code. + +## Import module + +Non-built-in modules, Should be import `QrCodeWidgetModule` in [json-schema.module.ts](https://github.com/ng-alain/ng-alain/blob/master/src/app/shared/json-schema/json-schema.module.ts#L11). + +## API + +### ui + +| Property | Description | Type | Default | +|----------|-------------|------|---------| +| `[color]` | QR code Color | `string` | `#000` | +| `[bgColor]` | QR code background color | `string` | `#FFFFFF` | +| `[qrSize]` | QR code Size | `number` | `160` | +| `[padding]` | QR code Padding | `number \| number[]` | `0` | +| `[icon]` | Icon address in QR code | `string` | - | +| `[iconSize]` | The size of the icon in the QR code | `number` | `40` | +| `[bordered]` | Whether has border style | `boolean` | `true` | +| `[status]` | QR code status | `'active'|'expired' |'loading'` | `active` | +| `[level]` | Error Code Level | `'L'|'M'|'Q'|'H'` | `M` | +| `(refresh)` | callback | `EventEmitter` | - | diff --git a/packages/form/widgets/qr-code/index.ts b/packages/form/widgets/qr-code/index.ts new file mode 100644 index 0000000000..e5ca656b39 --- /dev/null +++ b/packages/form/widgets/qr-code/index.ts @@ -0,0 +1,11 @@ +import type { SFWidgetProvideConfig } from '@delon/form'; + +import { QrCodeWidget } from './widget'; + +export * from './widget'; +export * from './schema'; +export * from './module'; + +export function withQrCodeWidget(): SFWidgetProvideConfig { + return { KEY: QrCodeWidget.KEY, type: QrCodeWidget }; +} diff --git a/packages/form/widgets/qr-code/index.zh-CN.md b/packages/form/widgets/qr-code/index.zh-CN.md new file mode 100644 index 0000000000..32076a1edc --- /dev/null +++ b/packages/form/widgets/qr-code/index.zh-CN.md @@ -0,0 +1,28 @@ +--- +title: qr-code +subtitle: 二维码 +type: Non-built-in widgets +--- + +当需要将链接转换成为二维码时使用。 + +## 导入模块 + +非内置模块,需要额外在 [json-schema.module.ts](https://github.com/ng-alain/ng-alain/blob/master/src/app/shared/json-schema/json-schema.module.ts#L11) 导入 `QrCodeWidgetModule`。 + +## API + +### ui 属性 + +| 成员 | 说明 | 类型 | 默认值 | +|----|----|----|-----| +| `[color]` | 二维码颜色 | `string` | `#000` | +| `[bgColor]` | 二维码背景颜色 | `string` | `#FFFFFF` | +| `[qrSize]` | 二维码大小 | `number` | `160` | +| `[padding]` | 二维码填充 | `number \| number[]` | `0` | +| `[icon]` | 二维码中 icon 地址 | `string` | - | +| `[iconSize]` | 二维码中 icon 大小 | `number` | `40` | +| `[bordered]` | 是否有边框 | `boolean` | `true` | +| `[status]` | 二维码状态 | `'active'|'expired' |'loading'` | `active` | +| `[level]` | 二维码容错等级 | `'L'|'M'|'Q'|'H'` | `M` | +| `(refresh)` | 点击"点击刷新"的回调 | `EventEmitter` | - | diff --git a/packages/form/widgets/qr-code/module.ts b/packages/form/widgets/qr-code/module.ts new file mode 100644 index 0000000000..46a130f17d --- /dev/null +++ b/packages/form/widgets/qr-code/module.ts @@ -0,0 +1,16 @@ +import { NgModule } from '@angular/core'; +import { FormsModule } from '@angular/forms'; + +import { DelonFormModule, WidgetRegistry } from '@delon/form'; +import { NzQRCodeModule } from 'ng-zorro-antd/qr-code'; + +import { QrCodeWidget } from './widget'; + +@NgModule({ + imports: [FormsModule, DelonFormModule, NzQRCodeModule, QrCodeWidget] +}) +export class QrCodeWidgetModule { + constructor(widgetRegistry: WidgetRegistry) { + widgetRegistry.register(QrCodeWidget.KEY, QrCodeWidget); + } +} diff --git a/packages/form/widgets/qr-code/ng-package.json b/packages/form/widgets/qr-code/ng-package.json new file mode 100644 index 0000000000..3b78722325 --- /dev/null +++ b/packages/form/widgets/qr-code/ng-package.json @@ -0,0 +1,6 @@ +{ + "lib": { + "flatModuleFile": "widgets-qr-code", + "entryFile": "index.ts" + } +} diff --git a/packages/form/widgets/qr-code/schema.ts b/packages/form/widgets/qr-code/schema.ts new file mode 100644 index 0000000000..49be6274ba --- /dev/null +++ b/packages/form/widgets/qr-code/schema.ts @@ -0,0 +1,25 @@ +import type { SFUISchemaItem } from '@delon/form'; +import type { ERROR_LEVEL_MAP } from 'ng-zorro-antd/qr-code/qrcode'; + +export interface SFQrCodeWidgetSchema extends SFUISchemaItem { + /** QR code Padding */ + padding?: number; + /** QR code Color */ + color?: string; + /** QR code background color */ + bgColor?: string; + /** QR code Size */ + qrSize?: number; + /** Icon address in QR code */ + icon?: string; + /** The size of the icon in the QR code */ + iconSize?: number; + /** Whether has border style */ + bordered?: boolean; + /** QR code status */ + status?: 'active' | 'expired' | 'loading'; + /** Error Code Level */ + level?: keyof typeof ERROR_LEVEL_MAP; + /** Callback */ + refresh?: (qr: string) => void; +} diff --git a/packages/form/widgets/qr-code/widget.spec.ts b/packages/form/widgets/qr-code/widget.spec.ts new file mode 100644 index 0000000000..fa29342b70 --- /dev/null +++ b/packages/form/widgets/qr-code/widget.spec.ts @@ -0,0 +1,37 @@ +import { DebugElement } from '@angular/core'; +import { ComponentFixture, fakeAsync } from '@angular/core/testing'; + +import { SFSchema } from '@delon/form'; +import { createTestContext } from '@delon/testing'; + +import { SFQrCodeWidgetSchema, withQrCodeWidget } from './index'; +import { configureSFTestSuite, SFPage, TestFormComponent } from '../../spec/base.spec'; + +describe('form: widget: qr-code', () => { + let fixture: ComponentFixture; + let dl: DebugElement; + let context: TestFormComponent; + let page: SFPage; + + configureSFTestSuite({ widgets: [withQrCodeWidget()] }); + + beforeEach(() => { + ({ fixture, dl, context } = createTestContext(TestFormComponent)); + page = new SFPage(context.comp); + page.cleanOverlay().prop(dl, context, fixture); + }); + + it('should be working', fakeAsync(() => { + const s: SFSchema = { + properties: { + a: { + type: 'string', + ui: { + widget: 'qr-code' + } as SFQrCodeWidgetSchema + } + } + }; + page.newSchema(s).getEl('canvas'); + })); +}); diff --git a/packages/form/widgets/qr-code/widget.ts b/packages/form/widgets/qr-code/widget.ts new file mode 100644 index 0000000000..2a1f9c1143 --- /dev/null +++ b/packages/form/widgets/qr-code/widget.ts @@ -0,0 +1,45 @@ +import { Component, ViewEncapsulation } from '@angular/core'; +import { FormsModule } from '@angular/forms'; + +import { ControlUIWidget, DelonFormModule } from '@delon/form'; +import { NzQRCodeModule } from 'ng-zorro-antd/qr-code'; + +import type { SFQrCodeWidgetSchema } from './schema'; + +@Component({ + selector: 'sf-qr-code', + template: ` + + `, + preserveWhitespaces: false, + encapsulation: ViewEncapsulation.None, + standalone: true, + imports: [FormsModule, DelonFormModule, NzQRCodeModule] +}) +export class QrCodeWidget extends ControlUIWidget { + static readonly KEY = 'qr-code'; + + refresh(qr: string): void { + this.setValue(qr); + if (this.ui.refresh) this.ui.refresh(qr); + } +} diff --git a/packages/form/src/widgets/rate/demo/simple.md b/packages/form/widgets/rate/demo/simple.md similarity index 90% rename from packages/form/src/widgets/rate/demo/simple.md rename to packages/form/widgets/rate/demo/simple.md index 2da2d71114..b30b278dab 100644 --- a/packages/form/src/widgets/rate/demo/simple.md +++ b/packages/form/widgets/rate/demo/simple.md @@ -15,7 +15,8 @@ Simplest of usage. ```ts import { Component } from '@angular/core'; -import { SFRateWidgetSchema, SFSchema } from '@delon/form'; +import { SFSchema } from '@delon/form'; +import type { SFRateWidgetSchema } from '@delon/form/widgets/rate'; import { NzMessageService } from 'ng-zorro-antd/message'; @Component({ diff --git a/packages/form/src/widgets/rate/index.en-US.md b/packages/form/widgets/rate/index.en-US.md similarity index 75% rename from packages/form/src/widgets/rate/index.en-US.md rename to packages/form/widgets/rate/index.en-US.md index 1effb25674..0dbe11ca9c 100644 --- a/packages/form/src/widgets/rate/index.en-US.md +++ b/packages/form/widgets/rate/index.en-US.md @@ -1,11 +1,15 @@ --- title: rate subtitle: Rate -type: Widgets +type: Non-built-in widgets --- A quick rating operation on something. +## Import module + +Non-built-in modules, Should be import `RateWidgetModule` in [json-schema.module.ts](https://github.com/ng-alain/ng-alain/blob/master/src/app/shared/json-schema/json-schema.module.ts#L11). + ## API ### schema diff --git a/packages/form/widgets/rate/index.ts b/packages/form/widgets/rate/index.ts new file mode 100644 index 0000000000..e3d95124b1 --- /dev/null +++ b/packages/form/widgets/rate/index.ts @@ -0,0 +1,11 @@ +import type { SFWidgetProvideConfig } from '@delon/form'; + +import { RateWidget } from './widget'; + +export * from './widget'; +export * from './schema'; +export * from './module'; + +export function withRateWidget(): SFWidgetProvideConfig { + return { KEY: RateWidget.KEY, type: RateWidget }; +} diff --git a/packages/form/src/widgets/rate/index.zh-CN.md b/packages/form/widgets/rate/index.zh-CN.md similarity index 75% rename from packages/form/src/widgets/rate/index.zh-CN.md rename to packages/form/widgets/rate/index.zh-CN.md index 7e917278f4..911776498c 100644 --- a/packages/form/src/widgets/rate/index.zh-CN.md +++ b/packages/form/widgets/rate/index.zh-CN.md @@ -1,11 +1,15 @@ --- title: rate subtitle: 评分 -type: Widgets +type: Non-built-in widgets --- 对评价进行展示,对事物进行快速的评级操作。 +## 导入模块 + +非内置模块,需要额外在 [json-schema.module.ts](https://github.com/ng-alain/ng-alain/blob/master/src/app/shared/json-schema/json-schema.module.ts#L11) 导入 `RateWidgetModule`。 + ## API ### schema 属性 diff --git a/packages/form/widgets/rate/module.ts b/packages/form/widgets/rate/module.ts new file mode 100644 index 0000000000..39b93902bb --- /dev/null +++ b/packages/form/widgets/rate/module.ts @@ -0,0 +1,17 @@ +import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { FormsModule } from '@angular/forms'; + +import { DelonFormModule, WidgetRegistry } from '@delon/form'; +import { NzRateModule } from 'ng-zorro-antd/rate'; + +import { RateWidget } from './widget'; + +@NgModule({ + imports: [FormsModule, DelonFormModule, NzRateModule, CommonModule, RateWidget] +}) +export class RateWidgetModule { + constructor(widgetRegistry: WidgetRegistry) { + widgetRegistry.register(RateWidget.KEY, RateWidget); + } +} diff --git a/packages/form/widgets/rate/ng-package.json b/packages/form/widgets/rate/ng-package.json new file mode 100644 index 0000000000..b9b116ea3a --- /dev/null +++ b/packages/form/widgets/rate/ng-package.json @@ -0,0 +1,6 @@ +{ + "lib": { + "flatModuleFile": "widgets-rate", + "entryFile": "index.ts" + } +} diff --git a/packages/form/src/widgets/rate/schema.ts b/packages/form/widgets/rate/schema.ts similarity index 89% rename from packages/form/src/widgets/rate/schema.ts rename to packages/form/widgets/rate/schema.ts index 6d5f7d6274..47f14d3d08 100644 --- a/packages/form/src/widgets/rate/schema.ts +++ b/packages/form/widgets/rate/schema.ts @@ -1,4 +1,4 @@ -import { SFUISchemaItem } from '../../schema/ui'; +import type { SFUISchemaItem } from '@delon/form'; export interface SFRateWidgetSchema extends SFUISchemaItem { /** diff --git a/packages/form/src/widgets/rate/rate.widget.spec.ts b/packages/form/widgets/rate/widget.spec.ts similarity index 91% rename from packages/form/src/widgets/rate/rate.widget.spec.ts rename to packages/form/widgets/rate/widget.spec.ts index 847fbf6a36..9e9121fcc9 100644 --- a/packages/form/src/widgets/rate/rate.widget.spec.ts +++ b/packages/form/widgets/rate/widget.spec.ts @@ -3,7 +3,8 @@ import { ComponentFixture, fakeAsync } from '@angular/core/testing'; import { createTestContext } from '@delon/testing'; -import { configureSFTestSuite, SFPage, TestFormComponent } from '../../../spec/base.spec'; +import { withRateWidget } from './index'; +import { configureSFTestSuite, SFPage, TestFormComponent } from '../../spec/base.spec'; describe('form: widget: rate', () => { let fixture: ComponentFixture; @@ -12,7 +13,7 @@ describe('form: widget: rate', () => { let page: SFPage; const widget = 'rate'; - configureSFTestSuite(); + configureSFTestSuite({ widgets: [withRateWidget()] }); beforeEach(() => { ({ fixture, dl, context } = createTestContext(TestFormComponent)); diff --git a/packages/form/widgets/rate/widget.ts b/packages/form/widgets/rate/widget.ts new file mode 100644 index 0000000000..3cf0918e7c --- /dev/null +++ b/packages/form/widgets/rate/widget.ts @@ -0,0 +1,59 @@ +import { Component, OnInit, ViewEncapsulation } from '@angular/core'; +import { FormsModule } from '@angular/forms'; + +import { ControlUIWidget, DelonFormModule, toBool } from '@delon/form'; +import { NzRateModule } from 'ng-zorro-antd/rate'; + +import type { SFRateWidgetSchema } from './schema'; + +@Component({ + selector: 'sf-rate', + template: ` + + @if (hasText && formProperty.value) { + {{ text }} + } + `, + preserveWhitespaces: false, + encapsulation: ViewEncapsulation.None, + standalone: true, + imports: [FormsModule, DelonFormModule, NzRateModule] +}) +export class RateWidget extends ControlUIWidget implements OnInit { + static readonly KEY = 'rate'; + + count!: number; + allowHalf!: boolean; + allowClear!: boolean; + autoFocus!: boolean; + hasText = false; + + get text(): string { + return (this.ui.text as string).replace('{{value}}', this.formProperty.value); + } + + ngOnInit(): void { + const { schema, ui } = this; + this.count = schema.maximum || 5; + this.allowHalf = (schema.multipleOf || 0.5) === 0.5; + this.allowClear = toBool(ui.allowClear, true); + this.autoFocus = toBool(ui.autoFocus, false); + this.hasText = !!ui.text; + } +} diff --git a/packages/form/widgets/segmented/demo/simple.md b/packages/form/widgets/segmented/demo/simple.md new file mode 100644 index 0000000000..9afa849841 --- /dev/null +++ b/packages/form/widgets/segmented/demo/simple.md @@ -0,0 +1,65 @@ +--- +title: + zh-CN: 基础样例 + en-US: Basic Usage +order: 0 +--- + +## zh-CN + +最简单的用法。 + +## en-US + +Simplest of usage. + +```ts +import { Component } from '@angular/core'; +import { delay, of } from 'rxjs'; + +import { SFSchema } from '@delon/form'; +import { SFSegmentedWidgetSchema, SegmentedWidget } from '@delon/form/widgets/segmented'; +import { NzMessageService } from 'ng-zorro-antd/message'; +import { NzSegmentedOptions } from 'ng-zorro-antd/segmented'; + +@Component({ + selector: 'app-demo', + template: `` +}) +export class DemoComponent { + schema: SFSchema = { + properties: { + base: { + type: 'string', + title: 'Base', + default: 2, + enum: ['Daily', 'Weekly', 'Monthly', 'Quarterly', 'Yearly'], + ui: { + widget: SegmentedWidget.KEY, + valueChange: console.log + } as SFSegmentedWidgetSchema + }, + asyncData: { + type: 'string', + title: 'Async Data', + ui: { + widget: SegmentedWidget.KEY, + asyncData: () => + of([ + { label: 'Label1', value: 'a' }, + { label: 'Label2', value: 'b' }, + { label: 'Label3', value: 'c', disabled: true } + ] as NzSegmentedOptions).pipe(delay(1000)), + valueChange: console.log + } as SFSegmentedWidgetSchema + } + } + }; + + constructor(private msg: NzMessageService) {} + + submit(value: {}): void { + this.msg.success(JSON.stringify(value)); + } +} +``` diff --git a/packages/form/widgets/segmented/index.en-US.md b/packages/form/widgets/segmented/index.en-US.md new file mode 100644 index 0000000000..a688d37a56 --- /dev/null +++ b/packages/form/widgets/segmented/index.en-US.md @@ -0,0 +1,22 @@ +--- +title: segmented +subtitle: Segmented +type: Non-built-in widgets +--- + +- When displaying multiple options and user can select a single option; +- When switching the selected option, the content of the associated area changes. + +## Import module + +Non-built-in modules, Should be import `SegmentedWidgetModule` in [json-schema.module.ts](https://github.com/ng-alain/ng-alain/blob/master/src/app/shared/json-schema/json-schema.module.ts#L11). + +## API + +### ui + +| Property | Description | Type | Default | +|----------|-------------|------|---------| +| `[block]` | Option to fit width to its parent\'s width | `boolean` | false | | +| `[asyncData]` | Set children optional | `() => Observable` | - | | +| `(valueChange)` | Emits when index of the currently selected option changes | `(data: { index: number; item: SFValue }) => void` | - | | diff --git a/packages/form/widgets/segmented/index.ts b/packages/form/widgets/segmented/index.ts new file mode 100644 index 0000000000..c42adb39d6 --- /dev/null +++ b/packages/form/widgets/segmented/index.ts @@ -0,0 +1,11 @@ +import type { SFWidgetProvideConfig } from '@delon/form'; + +import { SegmentedWidget } from './widget'; + +export * from './widget'; +export * from './schema'; +export * from './module'; + +export function withSegmentedWidget(): SFWidgetProvideConfig { + return { KEY: SegmentedWidget.KEY, type: SegmentedWidget }; +} diff --git a/packages/form/widgets/segmented/index.zh-CN.md b/packages/form/widgets/segmented/index.zh-CN.md new file mode 100644 index 0000000000..a7c765d643 --- /dev/null +++ b/packages/form/widgets/segmented/index.zh-CN.md @@ -0,0 +1,22 @@ +--- +title: segmented +subtitle: 分段控制器 +type: Non-built-in widgets +--- + +- 用于展示多个选项并允许用户选择其中单个选项; +- 当切换选中选项时,关联区域的内容会发生变化。 + +## 导入模块 + +非内置模块,需要额外在 [json-schema.module.ts](https://github.com/ng-alain/ng-alain/blob/master/src/app/shared/json-schema/json-schema.module.ts#L11) 导入 `SegmentedWidgetModule`。 + +## API + +### ui 属性 + +| 成员 | 说明 | 类型 | 默认值 | +|----|----|----|-----| +| `[block]` | 将宽度调整为父元素宽度的选项 | `boolean` | false | | +| `[asyncData]` | 异步数据 | `() => Observable` | - | | +| `(valueChange)` | 当前选中项目变化时触发回调 | `(data: { index: number; item: SFValue }) => void` | - | | diff --git a/packages/form/widgets/segmented/module.ts b/packages/form/widgets/segmented/module.ts new file mode 100644 index 0000000000..08f172b6af --- /dev/null +++ b/packages/form/widgets/segmented/module.ts @@ -0,0 +1,16 @@ +import { NgModule } from '@angular/core'; +import { FormsModule } from '@angular/forms'; + +import { DelonFormModule, WidgetRegistry } from '@delon/form'; +import { NzSegmentedModule } from 'ng-zorro-antd/segmented'; + +import { SegmentedWidget } from './widget'; + +@NgModule({ + imports: [FormsModule, DelonFormModule, NzSegmentedModule, SegmentedWidget] +}) +export class SegmentedWidgetModule { + constructor(widgetRegistry: WidgetRegistry) { + widgetRegistry.register(SegmentedWidget.KEY, SegmentedWidget); + } +} diff --git a/packages/form/widgets/segmented/ng-package.json b/packages/form/widgets/segmented/ng-package.json new file mode 100644 index 0000000000..ec57a9fa30 --- /dev/null +++ b/packages/form/widgets/segmented/ng-package.json @@ -0,0 +1,6 @@ +{ + "lib": { + "flatModuleFile": "widgets-color", + "entryFile": "index.ts" + } +} diff --git a/packages/form/widgets/segmented/schema.ts b/packages/form/widgets/segmented/schema.ts new file mode 100644 index 0000000000..897c7539b6 --- /dev/null +++ b/packages/form/widgets/segmented/schema.ts @@ -0,0 +1,21 @@ +import type { TemplateRef } from '@angular/core'; +import type { Observable } from 'rxjs'; + +import type { SFUISchemaItem, SFValue } from '@delon/form'; +import type { NzSegmentedOption, NzSegmentedOptions } from 'ng-zorro-antd/segmented'; + +export interface SFSegmentedWidgetSchema extends SFUISchemaItem { + /** + * 异步数据源 + */ + asyncData?: () => Observable; + /** + * Option to fit width to its parent's width + */ + block?: boolean; + labelTemplate?: TemplateRef<{ $implicit: NzSegmentedOption; index: number }> | null; + /** + * Emits when index of the currently selected option changes + */ + valueChange?: (data: { index: number; item: SFValue }) => void; +} diff --git a/packages/form/widgets/segmented/widget.spec.ts b/packages/form/widgets/segmented/widget.spec.ts new file mode 100644 index 0000000000..8f2d876fea --- /dev/null +++ b/packages/form/widgets/segmented/widget.spec.ts @@ -0,0 +1,42 @@ +import { DebugElement } from '@angular/core'; +import { ComponentFixture, fakeAsync } from '@angular/core/testing'; + +import { SFSchema } from '@delon/form'; +import { createTestContext } from '@delon/testing'; + +import { withSegmentedWidget, SFSegmentedWidgetSchema } from './index'; +import { configureSFTestSuite, SFPage, TestFormComponent } from '../../spec/base.spec'; + +describe('form: widget: segmented', () => { + let fixture: ComponentFixture; + let dl: DebugElement; + let context: TestFormComponent; + let page: SFPage; + + configureSFTestSuite({ widgets: [withSegmentedWidget()] }); + + beforeEach(() => { + ({ fixture, dl, context } = createTestContext(TestFormComponent)); + page = new SFPage(context.comp); + page.cleanOverlay().prop(dl, context, fixture); + }); + + it('should be working', fakeAsync(() => { + const valueChange = jasmine.createSpy(); + const s: SFSchema = { + properties: { + a: { + type: 'string', + enum: ['Daily', 'Weekly', 'Monthly', 'Quarterly', 'Yearly'], + ui: { + widget: 'segmented', + valueChange + } as SFSegmentedWidgetSchema + } + } + }; + page.newSchema(s).typeEvent('click', '.ant-segmented-item:nth-child(2) .ant-segmented-item-label'); + expect(page.getValue('/a')).toBe(1); + expect(valueChange).toHaveBeenCalled(); + })); +}); diff --git a/packages/form/widgets/segmented/widget.ts b/packages/form/widgets/segmented/widget.ts new file mode 100644 index 0000000000..fde765d19b --- /dev/null +++ b/packages/form/widgets/segmented/widget.ts @@ -0,0 +1,54 @@ +import { Component, ViewEncapsulation } from '@angular/core'; +import { FormsModule } from '@angular/forms'; + +import { ControlUIWidget, DelonFormModule, SFValue, getData } from '@delon/form'; +import { NzSegmentedModule, NzSegmentedOptions } from 'ng-zorro-antd/segmented'; + +import type { SFSegmentedWidgetSchema } from './schema'; + +@Component({ + selector: 'sf-segmented', + template: ` + + `, + preserveWhitespaces: false, + encapsulation: ViewEncapsulation.None, + standalone: true, + imports: [FormsModule, DelonFormModule, NzSegmentedModule] +}) +export class SegmentedWidget extends ControlUIWidget { + static readonly KEY = 'segmented'; + private _list?: NzSegmentedOptions; + get list(): NzSegmentedOptions { + return this._list ?? []; + } + + reset(value: SFValue): void { + getData(this.schema, this.ui, value).subscribe(list => { + this._list = list as NzSegmentedOptions; + this.detectChanges(); + }); + } + + valueChange(index: number): void { + if (this.ui.valueChange) { + this.ui.valueChange({ index, item: this.list[index] as SFValue }); + } + } +} diff --git a/packages/form/src/widgets/slider/demo/simple.md b/packages/form/widgets/slider/demo/simple.md similarity index 89% rename from packages/form/src/widgets/slider/demo/simple.md rename to packages/form/widgets/slider/demo/simple.md index 1edbe73ef5..a6b4d9b326 100644 --- a/packages/form/src/widgets/slider/demo/simple.md +++ b/packages/form/widgets/slider/demo/simple.md @@ -15,7 +15,8 @@ Simplest of usage. ```ts import { Component } from '@angular/core'; -import { SFSchema, SFSliderWidgetSchema } from '@delon/form'; +import { SFSchema } from '@delon/form'; +import type { SFSliderWidgetSchema } from '@delon/form/widgets/slider'; import { NzMessageService } from 'ng-zorro-antd/message'; @Component({ diff --git a/packages/form/src/widgets/slider/index.en-US.md b/packages/form/widgets/slider/index.en-US.md similarity index 85% rename from packages/form/src/widgets/slider/index.en-US.md rename to packages/form/widgets/slider/index.en-US.md index 76837de082..616617be40 100644 --- a/packages/form/src/widgets/slider/index.en-US.md +++ b/packages/form/widgets/slider/index.en-US.md @@ -1,11 +1,15 @@ --- -title: range +title: slider subtitle: Slider -type: Widgets +type: Non-built-in widgets --- A Slider component for displaying current value and intervals in range. +## Import module + +Non-built-in modules, Should be import `SliderWidgetModule` in [json-schema.module.ts](https://github.com/ng-alain/ng-alain/blob/master/src/app/shared/json-schema/json-schema.module.ts#L11). + ## Notice - Ingored `exclusiveMinimum`, `exclusiveMaximum` diff --git a/packages/form/widgets/slider/index.ts b/packages/form/widgets/slider/index.ts new file mode 100644 index 0000000000..0780237d6b --- /dev/null +++ b/packages/form/widgets/slider/index.ts @@ -0,0 +1,11 @@ +import type { SFWidgetProvideConfig } from '@delon/form'; + +import { SliderWidget } from './widget'; + +export * from './widget'; +export * from './schema'; +export * from './module'; + +export function withSliderWidget(): SFWidgetProvideConfig { + return { KEY: SliderWidget.KEY, type: SliderWidget }; +} diff --git a/packages/form/src/widgets/slider/index.zh-CN.md b/packages/form/widgets/slider/index.zh-CN.md similarity index 82% rename from packages/form/src/widgets/slider/index.zh-CN.md rename to packages/form/widgets/slider/index.zh-CN.md index 5527b63029..51e96a37e3 100644 --- a/packages/form/src/widgets/slider/index.zh-CN.md +++ b/packages/form/widgets/slider/index.zh-CN.md @@ -1,11 +1,15 @@ --- -title: range +title: slider subtitle: 滑动输入条 -type: Widgets +type: Non-built-in widgets --- 滑动型输入器,展示当前值和可选范围。 +## 导入模块 + +非内置模块,需要额外在 [json-schema.module.ts](https://github.com/ng-alain/ng-alain/blob/master/src/app/shared/json-schema/json-schema.module.ts#L11) 导入 `SliderWidgetModule`。 + ## 注意事项 - 强制忽略 `exclusiveMinimum`、`exclusiveMaximum` diff --git a/packages/form/widgets/slider/module.ts b/packages/form/widgets/slider/module.ts new file mode 100644 index 0000000000..9d3f274bec --- /dev/null +++ b/packages/form/widgets/slider/module.ts @@ -0,0 +1,16 @@ +import { NgModule } from '@angular/core'; +import { FormsModule } from '@angular/forms'; + +import { DelonFormModule, WidgetRegistry } from '@delon/form'; +import { NzSliderModule } from 'ng-zorro-antd/slider'; + +import { SliderWidget } from './widget'; + +@NgModule({ + imports: [FormsModule, DelonFormModule, NzSliderModule, SliderWidget] +}) +export class SliderWidgetModule { + constructor(widgetRegistry: WidgetRegistry) { + widgetRegistry.register(SliderWidget.KEY, SliderWidget); + } +} diff --git a/packages/form/widgets/slider/ng-package.json b/packages/form/widgets/slider/ng-package.json new file mode 100644 index 0000000000..ddee7bcabf --- /dev/null +++ b/packages/form/widgets/slider/ng-package.json @@ -0,0 +1,6 @@ +{ + "lib": { + "flatModuleFile": "widgets-slider", + "entryFile": "index.ts" + } +} diff --git a/packages/form/src/widgets/slider/schema.ts b/packages/form/widgets/slider/schema.ts similarity index 94% rename from packages/form/src/widgets/slider/schema.ts rename to packages/form/widgets/slider/schema.ts index e316a843ec..3bbe805457 100644 --- a/packages/form/src/widgets/slider/schema.ts +++ b/packages/form/widgets/slider/schema.ts @@ -1,7 +1,6 @@ +import type { SFUISchemaItem } from '@delon/form'; import { NzMarks, NzSliderValue } from 'ng-zorro-antd/slider'; -import { SFUISchemaItem } from '../../schema/ui'; - export interface SFSliderWidgetSchema extends SFUISchemaItem { /** * 当添加该属性时,启动双滑块模式 diff --git a/packages/form/src/widgets/slider/slider.widget.spec.ts b/packages/form/widgets/slider/widget.spec.ts similarity index 93% rename from packages/form/src/widgets/slider/slider.widget.spec.ts rename to packages/form/widgets/slider/widget.spec.ts index 190fe63969..a1655a10d5 100644 --- a/packages/form/src/widgets/slider/slider.widget.spec.ts +++ b/packages/form/widgets/slider/widget.spec.ts @@ -1,13 +1,14 @@ import { DebugElement } from '@angular/core'; import { ComponentFixture, fakeAsync } from '@angular/core/testing'; +import { SFSchema } from '@delon/form'; import { createTestContext } from '@delon/testing'; import { NzSafeAny } from 'ng-zorro-antd/core/types'; import { NzSliderComponent } from 'ng-zorro-antd/slider'; -import { SliderWidget } from './slider.widget'; -import { configureSFTestSuite, SFPage, TestFormComponent } from '../../../spec/base.spec'; -import { SFSchema } from '../../../src/schema/index'; +import { withSliderWidget } from './index'; +import { SliderWidget } from './widget'; +import { configureSFTestSuite, SFPage, TestFormComponent } from '../../spec/base.spec'; describe('form: widget: slider', () => { let fixture: ComponentFixture; @@ -16,7 +17,7 @@ describe('form: widget: slider', () => { let page: SFPage; const widget = 'slider'; - configureSFTestSuite(); + configureSFTestSuite({ widgets: [withSliderWidget()] }); beforeEach(() => { ({ fixture, dl, context } = createTestContext(TestFormComponent)); diff --git a/packages/form/widgets/slider/widget.ts b/packages/form/widgets/slider/widget.ts new file mode 100644 index 0000000000..c5e96b063e --- /dev/null +++ b/packages/form/widgets/slider/widget.ts @@ -0,0 +1,70 @@ +import { Component, OnInit, ViewEncapsulation } from '@angular/core'; +import { FormsModule } from '@angular/forms'; + +import { ControlUIWidget, DelonFormModule } from '@delon/form'; +import { NzMarks, NzSliderModule, NzSliderValue } from 'ng-zorro-antd/slider'; + +import type { SFSliderWidgetSchema } from './schema'; + +@Component({ + selector: 'sf-slider', + template: ` + + `, + preserveWhitespaces: false, + encapsulation: ViewEncapsulation.None, + standalone: true, + imports: [FormsModule, DelonFormModule, NzSliderModule] +}) +export class SliderWidget extends ControlUIWidget implements OnInit { + static readonly KEY = 'slider'; + + min!: number; + max!: number; + step!: number; + marks: NzMarks | null = null; + included!: boolean; + + ngOnInit(): void { + const { minimum, maximum, multipleOf } = this.schema; + this.min = minimum || 0; + this.max = maximum || 100; + this.step = multipleOf || 1; + + const { marks, included } = this.ui; + this.marks = marks || null; + this.included = typeof included === 'undefined' ? true : included; + } + + _formatter = (value: number): string => { + const { formatter } = this.ui; + if (formatter) return formatter(value); + return `${value}`; + }; + + _afterChange(value: NzSliderValue): void { + const { afterChange } = this.ui; + if (afterChange) return afterChange(value); + } +} diff --git a/packages/form/src/widgets/tag/demo/simple.md b/packages/form/widgets/tag/demo/simple.md similarity index 93% rename from packages/form/src/widgets/tag/demo/simple.md rename to packages/form/widgets/tag/demo/simple.md index 0ee04cce70..6fcfa18bb7 100644 --- a/packages/form/src/widgets/tag/demo/simple.md +++ b/packages/form/widgets/tag/demo/simple.md @@ -15,7 +15,8 @@ Simplest of usage. ```ts import { Component } from '@angular/core'; -import { SFIcon, SFSchema, SFTagWidgetSchema } from '@delon/form'; +import { SFIcon, SFSchema } from '@delon/form'; +import type { SFTagWidgetSchema } from '@delon/form/widgets/tag'; import { NzMessageService } from 'ng-zorro-antd/message'; import { of, delay } from 'rxjs'; diff --git a/packages/form/src/widgets/tag/index.en-US.md b/packages/form/widgets/tag/index.en-US.md similarity index 75% rename from packages/form/src/widgets/tag/index.en-US.md rename to packages/form/widgets/tag/index.en-US.md index fc0e5523fe..8514e22c8a 100644 --- a/packages/form/src/widgets/tag/index.en-US.md +++ b/packages/form/widgets/tag/index.en-US.md @@ -1,11 +1,15 @@ --- title: tag subtitle: Tag -type: Widgets +type: Non-built-in widgets --- Tag for categorizing or markup, **Notice:** Just only supported `checkable` tag mode. +## Import module + +Non-built-in modules, Should be import `TagWidgetModule` in [json-schema.module.ts](https://github.com/ng-alain/ng-alain/blob/master/src/app/shared/json-schema/json-schema.module.ts#L11). + ## API ### schema diff --git a/packages/form/widgets/tag/index.ts b/packages/form/widgets/tag/index.ts new file mode 100644 index 0000000000..d4458f34af --- /dev/null +++ b/packages/form/widgets/tag/index.ts @@ -0,0 +1,11 @@ +import type { SFWidgetProvideConfig } from '@delon/form'; + +import { TagWidget } from './widget'; + +export * from './widget'; +export * from './schema'; +export * from './module'; + +export function withTagWidget(): SFWidgetProvideConfig { + return { KEY: TagWidget.KEY, type: TagWidget }; +} diff --git a/packages/form/src/widgets/tag/index.zh-CN.md b/packages/form/widgets/tag/index.zh-CN.md similarity index 75% rename from packages/form/src/widgets/tag/index.zh-CN.md rename to packages/form/widgets/tag/index.zh-CN.md index 648512508c..d96a7329f5 100644 --- a/packages/form/src/widgets/tag/index.zh-CN.md +++ b/packages/form/widgets/tag/index.zh-CN.md @@ -1,11 +1,15 @@ --- title: tag subtitle: 标签 -type: Widgets +type: Non-built-in widgets --- 进行标记和分类的小标签,**注:** 只支持 `checkable` 标签模式。 +## 导入模块 + +非内置模块,需要额外在 [json-schema.module.ts](https://github.com/ng-alain/ng-alain/blob/master/src/app/shared/json-schema/json-schema.module.ts#L11) 导入 `TagWidgetModule`。 + ## API ### schema 属性 diff --git a/packages/form/widgets/tag/module.ts b/packages/form/widgets/tag/module.ts new file mode 100644 index 0000000000..5932558407 --- /dev/null +++ b/packages/form/widgets/tag/module.ts @@ -0,0 +1,18 @@ +import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { FormsModule } from '@angular/forms'; + +import { DelonFormModule, WidgetRegistry } from '@delon/form'; +import { NzIconModule } from 'ng-zorro-antd/icon'; +import { NzTagModule } from 'ng-zorro-antd/tag'; + +import { TagWidget } from './widget'; + +@NgModule({ + imports: [FormsModule, DelonFormModule, NzTagModule, NzIconModule, CommonModule, TagWidget] +}) +export class TagWidgetModule { + constructor(widgetRegistry: WidgetRegistry) { + widgetRegistry.register(TagWidget.KEY, TagWidget); + } +} diff --git a/packages/form/widgets/tag/ng-package.json b/packages/form/widgets/tag/ng-package.json new file mode 100644 index 0000000000..6914a665fc --- /dev/null +++ b/packages/form/widgets/tag/ng-package.json @@ -0,0 +1,6 @@ +{ + "lib": { + "flatModuleFile": "widgets-tag", + "entryFile": "index.ts" + } +} diff --git a/packages/form/src/widgets/tag/schema.ts b/packages/form/widgets/tag/schema.ts similarity index 83% rename from packages/form/src/widgets/tag/schema.ts rename to packages/form/widgets/tag/schema.ts index 46904ffb83..b4618e7b01 100644 --- a/packages/form/src/widgets/tag/schema.ts +++ b/packages/form/widgets/tag/schema.ts @@ -1,7 +1,6 @@ import { Observable } from 'rxjs'; -import { SFSchemaEnumType } from '../../schema'; -import { SFUISchemaItem } from '../../schema/ui'; +import type { SFSchemaEnumType, SFUISchemaItem } from '@delon/form'; export interface SFTagWidgetSchema extends SFUISchemaItem { /** diff --git a/packages/form/src/widgets/tag/tag.widget.spec.ts b/packages/form/widgets/tag/widget.spec.ts similarity index 96% rename from packages/form/src/widgets/tag/tag.widget.spec.ts rename to packages/form/widgets/tag/widget.spec.ts index 2939caf4b4..25d1a63f83 100644 --- a/packages/form/src/widgets/tag/tag.widget.spec.ts +++ b/packages/form/widgets/tag/widget.spec.ts @@ -1,12 +1,13 @@ import { DebugElement } from '@angular/core'; import { ComponentFixture, fakeAsync } from '@angular/core/testing'; +import { SFSchema } from '@delon/form'; import { createTestContext } from '@delon/testing'; import { NzSafeAny } from 'ng-zorro-antd/core/types'; import { NzTagComponent } from 'ng-zorro-antd/tag'; -import { configureSFTestSuite, SFPage, TestFormComponent } from '../../../spec/base.spec'; -import { SFSchema } from '../../../src/schema/index'; +import { withTagWidget } from './index'; +import { configureSFTestSuite, SFPage, TestFormComponent } from '../../spec/base.spec'; describe('form: widget: tag', () => { let fixture: ComponentFixture; @@ -14,7 +15,7 @@ describe('form: widget: tag', () => { let context: TestFormComponent; let page: SFPage; - configureSFTestSuite(); + configureSFTestSuite({ widgets: [withTagWidget()] }); beforeEach(() => { ({ fixture, dl, context } = createTestContext(TestFormComponent)); diff --git a/packages/form/widgets/tag/widget.ts b/packages/form/widgets/tag/widget.ts new file mode 100644 index 0000000000..66d239c437 --- /dev/null +++ b/packages/form/widgets/tag/widget.ts @@ -0,0 +1,84 @@ +import { NgTemplateOutlet } from '@angular/common'; +import { Component, ViewEncapsulation } from '@angular/core'; +import { FormsModule } from '@angular/forms'; + +import { ControlUIWidget, DelonFormModule, SFSchemaEnum, SFValue, getData } from '@delon/form'; +import { NzIconModule } from 'ng-zorro-antd/icon'; +import { NzTagModule } from 'ng-zorro-antd/tag'; + +import type { SFTagWidgetSchema } from './schema'; + +@Component({ + selector: 'sf-tag', + template: ` + + + + @for (i of data; track $index) { + + @if (i.prefixIcon) { + + } + {{ i.label }} + @if (i.suffixIcon) { + + } + + } + `, + preserveWhitespaces: false, + encapsulation: ViewEncapsulation.None, + standalone: true, + imports: [FormsModule, NgTemplateOutlet, DelonFormModule, NzTagModule, NzIconModule] +}) +export class TagWidget extends ControlUIWidget { + static readonly KEY = 'tag'; + + data: SFSchemaEnum[] = []; + + reset(value: SFValue): void { + getData(this.schema, this.ui, value).subscribe(list => { + this.data = list; + this.detectChanges(); + }); + } + + onChange(item: SFSchemaEnum): void { + item.checked = !item.checked; + this.updateValue(); + if (this.ui.checkedChange) { + this.ui.checkedChange(item.checked); + } + } + + _close(e: MouseEvent): void { + if (this.ui.onClose) this.ui.onClose(e); + } + + private updateValue(): void { + this.formProperty.setValue( + this.data.filter(w => w.checked).map(i => i.value), + false + ); + } +} diff --git a/packages/form/src/widgets/time/demo/simple.md b/packages/form/widgets/time/demo/simple.md similarity index 90% rename from packages/form/src/widgets/time/demo/simple.md rename to packages/form/widgets/time/demo/simple.md index e0d0e59f40..cc0fcd7ce5 100644 --- a/packages/form/src/widgets/time/demo/simple.md +++ b/packages/form/widgets/time/demo/simple.md @@ -15,12 +15,13 @@ Simplest of usage. ```ts import { Component } from '@angular/core'; -import { SFSchema, SFTimeWidgetSchema } from '@delon/form'; +import { SFSchema } from '@delon/form'; +import type { SFTimeWidgetSchema } from '@delon/form/widgets/time'; import { NzMessageService } from 'ng-zorro-antd/message'; @Component({ selector: 'app-demo', - template: ` `, + template: ` `, }) export class DemoComponent { schema: SFSchema = { diff --git a/packages/form/src/widgets/time/index.en-US.md b/packages/form/widgets/time/index.en-US.md similarity index 92% rename from packages/form/src/widgets/time/index.en-US.md rename to packages/form/widgets/time/index.en-US.md index 5254e21128..0f0d03a471 100644 --- a/packages/form/src/widgets/time/index.en-US.md +++ b/packages/form/widgets/time/index.en-US.md @@ -1,11 +1,15 @@ --- title: time subtitle: Time -type: Widgets +type: Non-built-in widgets --- To select/input a time. +## Import module + +Non-built-in modules, Should be import `TimeWidgetModule` in [json-schema.module.ts](https://github.com/ng-alain/ng-alain/blob/master/src/app/shared/json-schema/json-schema.module.ts#L11). + ## Notice - Format is divided into two types: **Data format** means form data, **Display format** means display data ([nzFormat](https://ng.ant.design/components/time-picker/en#api)) diff --git a/packages/form/widgets/time/index.ts b/packages/form/widgets/time/index.ts new file mode 100644 index 0000000000..e577337f22 --- /dev/null +++ b/packages/form/widgets/time/index.ts @@ -0,0 +1,11 @@ +import type { SFWidgetProvideConfig } from '@delon/form'; + +import { TimeWidget } from './widget'; + +export * from './widget'; +export * from './schema'; +export * from './module'; + +export function withTimeWidget(): SFWidgetProvideConfig { + return { KEY: TimeWidget.KEY, type: TimeWidget }; +} diff --git a/packages/form/src/widgets/time/index.zh-CN.md b/packages/form/widgets/time/index.zh-CN.md similarity index 92% rename from packages/form/src/widgets/time/index.zh-CN.md rename to packages/form/widgets/time/index.zh-CN.md index 993dd7c68b..fb7bca0c70 100644 --- a/packages/form/src/widgets/time/index.zh-CN.md +++ b/packages/form/widgets/time/index.zh-CN.md @@ -1,11 +1,15 @@ --- title: time subtitle: 时间 -type: Widgets +type: Non-built-in widgets --- 输入或选择时间的控件。 +## 导入模块 + +非内置模块,需要额外在 [json-schema.module.ts](https://github.com/ng-alain/ng-alain/blob/master/src/app/shared/json-schema/json-schema.module.ts#L11) 导入 `TimeWidgetModule`。 + ## 注意事项 - 格式化分为:**数据格式化**表示表单数据和**显示格式化**显示数据(等同 [nzFormat](https://ng.ant.design/components/time-picker/zh#api) 值) diff --git a/packages/form/widgets/time/module.ts b/packages/form/widgets/time/module.ts new file mode 100644 index 0000000000..0416a9d7ea --- /dev/null +++ b/packages/form/widgets/time/module.ts @@ -0,0 +1,16 @@ +import { NgModule } from '@angular/core'; +import { FormsModule } from '@angular/forms'; + +import { DelonFormModule, WidgetRegistry } from '@delon/form'; +import { NzTimePickerModule } from 'ng-zorro-antd/time-picker'; + +import { TimeWidget } from './widget'; + +@NgModule({ + imports: [FormsModule, DelonFormModule, NzTimePickerModule, TimeWidget] +}) +export class TimeWidgetModule { + constructor(widgetRegistry: WidgetRegistry) { + widgetRegistry.register(TimeWidget.KEY, TimeWidget); + } +} diff --git a/packages/form/widgets/time/ng-package.json b/packages/form/widgets/time/ng-package.json new file mode 100644 index 0000000000..98ee820f87 --- /dev/null +++ b/packages/form/widgets/time/ng-package.json @@ -0,0 +1,6 @@ +{ + "lib": { + "flatModuleFile": "widgets-time", + "entryFile": "index.ts" + } +} diff --git a/packages/form/src/widgets/time/schema.ts b/packages/form/widgets/time/schema.ts similarity index 97% rename from packages/form/src/widgets/time/schema.ts rename to packages/form/widgets/time/schema.ts index 336defd47a..f39c8df497 100644 --- a/packages/form/src/widgets/time/schema.ts +++ b/packages/form/widgets/time/schema.ts @@ -1,4 +1,4 @@ -import { SFDLSSize, SFUISchemaItem } from '../../schema/ui'; +import type { SFDLSSize, SFUISchemaItem } from '@delon/form'; export interface SFTimeWidgetSchema extends SFUISchemaItem { size?: SFDLSSize; diff --git a/packages/form/src/widgets/time/time.widget.spec.ts b/packages/form/widgets/time/widget.spec.ts similarity index 95% rename from packages/form/src/widgets/time/time.widget.spec.ts rename to packages/form/widgets/time/widget.spec.ts index 4d07cdd400..b5ab33d4ed 100644 --- a/packages/form/src/widgets/time/time.widget.spec.ts +++ b/packages/form/widgets/time/widget.spec.ts @@ -3,11 +3,12 @@ import { ComponentFixture } from '@angular/core/testing'; import { format } from 'date-fns'; +import type { SFSchema } from '@delon/form'; import { createTestContext } from '@delon/testing'; -import { TimeWidget } from './time.widget'; -import { configureSFTestSuite, SFPage, TestFormComponent } from '../../../spec/base.spec'; -import { SFSchema } from '../../../src/schema/index'; +import { withTimeWidget } from './index'; +import { TimeWidget } from './widget'; +import { configureSFTestSuite, SFPage, TestFormComponent } from '../../spec/base.spec'; const FORMAT = 'yyyy-MM-dd HH:mm:ss'; @@ -18,7 +19,7 @@ describe('form: widget: time', () => { let dl: DebugElement; const widget = 'time'; - configureSFTestSuite(); + configureSFTestSuite({ widgets: [withTimeWidget()] }); beforeEach(() => { ({ fixture, dl, context } = createTestContext(TestFormComponent)); diff --git a/packages/form/src/widgets/time/time.widget.ts b/packages/form/widgets/time/widget.ts similarity index 59% rename from packages/form/src/widgets/time/time.widget.ts rename to packages/form/widgets/time/widget.ts index e3cfbd59c8..3eb85923ed 100644 --- a/packages/form/src/widgets/time/time.widget.ts +++ b/packages/form/widgets/time/widget.ts @@ -1,21 +1,57 @@ import { Component, OnInit, ViewEncapsulation } from '@angular/core'; +import { FormsModule } from '@angular/forms'; import { format } from 'date-fns'; +import { ControlUIWidget, DelonFormModule, SFValue, toBool } from '@delon/form'; import type { NzSafeAny } from 'ng-zorro-antd/core/types'; +import { NzTimePickerModule } from 'ng-zorro-antd/time-picker'; -import { SFTimeWidgetSchema } from './schema'; -import { SFValue } from '../../interface'; -import { toBool } from '../../utils'; -import { ControlUIWidget } from '../../widget'; +import type { SFTimeWidgetSchema } from './schema'; @Component({ selector: 'sf-time', - templateUrl: './time.widget.html', + template: ` + + `, preserveWhitespaces: false, - encapsulation: ViewEncapsulation.None + encapsulation: ViewEncapsulation.None, + standalone: true, + imports: [FormsModule, DelonFormModule, NzTimePickerModule] }) export class TimeWidget extends ControlUIWidget implements OnInit { + static readonly KEY = 'time'; + private valueFormat: string | undefined; displayValue: Date | null = null; i: NzSafeAny; diff --git a/packages/form/src/widgets/transfer/demo/simple.md b/packages/form/widgets/transfer/demo/simple.md similarity index 93% rename from packages/form/src/widgets/transfer/demo/simple.md rename to packages/form/widgets/transfer/demo/simple.md index 46424162df..7f431602ec 100644 --- a/packages/form/src/widgets/transfer/demo/simple.md +++ b/packages/form/widgets/transfer/demo/simple.md @@ -15,7 +15,8 @@ Simplest of usage. ```ts import { Component } from '@angular/core'; -import { SFSchema, SFTransferWidgetSchema } from '@delon/form'; +import { SFSchema } from '@delon/form'; +import { SFTransferWidgetSchema } from '@delon/form/widgets/transfer'; import { NzMessageService } from 'ng-zorro-antd/message'; import { of, delay } from 'rxjs'; diff --git a/packages/form/src/widgets/transfer/index.en-US.md b/packages/form/widgets/transfer/index.en-US.md similarity index 87% rename from packages/form/src/widgets/transfer/index.en-US.md rename to packages/form/widgets/transfer/index.en-US.md index 85b90457c8..a345fae5cc 100644 --- a/packages/form/src/widgets/transfer/index.en-US.md +++ b/packages/form/widgets/transfer/index.en-US.md @@ -1,11 +1,15 @@ --- title: transfer subtitle: Transfer -type: Widgets +type: Non-built-in widgets --- Double column transfer choice box. +## Import module + +Non-built-in modules, Should be import `TransferWidgetModule` in [json-schema.module.ts](https://github.com/ng-alain/ng-alain/blob/master/src/app/shared/json-schema/json-schema.module.ts#L11). + ## Note - `default` value `direction: 'right'` indicates right column. diff --git a/packages/form/widgets/transfer/index.ts b/packages/form/widgets/transfer/index.ts new file mode 100644 index 0000000000..71d7f97464 --- /dev/null +++ b/packages/form/widgets/transfer/index.ts @@ -0,0 +1,11 @@ +import type { SFWidgetProvideConfig } from '@delon/form'; + +import { TransferWidget } from './widget'; + +export * from './widget'; +export * from './schema'; +export * from './module'; + +export function withTransferWidget(): SFWidgetProvideConfig { + return { KEY: TransferWidget.KEY, type: TransferWidget }; +} diff --git a/packages/form/src/widgets/transfer/index.zh-CN.md b/packages/form/widgets/transfer/index.zh-CN.md similarity index 86% rename from packages/form/src/widgets/transfer/index.zh-CN.md rename to packages/form/widgets/transfer/index.zh-CN.md index c6bed6c566..9006b7b0f6 100644 --- a/packages/form/src/widgets/transfer/index.zh-CN.md +++ b/packages/form/widgets/transfer/index.zh-CN.md @@ -1,11 +1,15 @@ --- title: transfer subtitle: 穿梭框 -type: Widgets +type: Non-built-in widgets --- 双栏穿梭选择框。 +## 导入模块 + +非内置模块,需要额外在 [json-schema.module.ts](https://github.com/ng-alain/ng-alain/blob/master/src/app/shared/json-schema/json-schema.module.ts#L11) 导入 `TransferWidgetModule`。 + ## 注意事项 - `default` 值被当成 `direction: 'right'` 表示右栏中 diff --git a/packages/form/widgets/transfer/module.ts b/packages/form/widgets/transfer/module.ts new file mode 100644 index 0000000000..86668e624c --- /dev/null +++ b/packages/form/widgets/transfer/module.ts @@ -0,0 +1,19 @@ +import { NgModule } from '@angular/core'; +import { FormsModule } from '@angular/forms'; + +import { DelonFormModule, WidgetRegistry } from '@delon/form'; +import { NzTransferModule } from 'ng-zorro-antd/transfer'; + +import { TransferWidget } from './widget'; + +export * from './widget'; +export * from './schema'; + +@NgModule({ + imports: [FormsModule, DelonFormModule, NzTransferModule, TransferWidget] +}) +export class TransferWidgetModule { + constructor(widgetRegistry: WidgetRegistry) { + widgetRegistry.register(TransferWidget.KEY, TransferWidget); + } +} diff --git a/packages/form/widgets/transfer/ng-package.json b/packages/form/widgets/transfer/ng-package.json new file mode 100644 index 0000000000..19483d3cc5 --- /dev/null +++ b/packages/form/widgets/transfer/ng-package.json @@ -0,0 +1,6 @@ +{ + "lib": { + "flatModuleFile": "widgets-transfer", + "entryFile": "index.ts" + } +} diff --git a/packages/form/src/widgets/transfer/schema.ts b/packages/form/widgets/transfer/schema.ts similarity index 94% rename from packages/form/src/widgets/transfer/schema.ts rename to packages/form/widgets/transfer/schema.ts index f7e60aedcb..184d530c10 100644 --- a/packages/form/src/widgets/transfer/schema.ts +++ b/packages/form/widgets/transfer/schema.ts @@ -1,5 +1,6 @@ import { Observable } from 'rxjs'; +import type { SFSchemaEnumType, SFUISchemaItem } from '@delon/form'; import { NgStyleInterface } from 'ng-zorro-antd/core/types'; import { TransferCanMove, @@ -9,9 +10,6 @@ import { TransferSelectChange } from 'ng-zorro-antd/transfer'; -import { SFSchemaEnumType } from '../../schema'; -import { SFUISchemaItem } from '../../schema/ui'; - export interface SFTransferWidgetSchema extends SFUISchemaItem { /** * 异步数据源 diff --git a/packages/form/src/widgets/transfer/transfer.widget.spec.ts b/packages/form/widgets/transfer/widget.spec.ts similarity index 97% rename from packages/form/src/widgets/transfer/transfer.widget.spec.ts rename to packages/form/widgets/transfer/widget.spec.ts index c165946adc..b18d8417f3 100644 --- a/packages/form/src/widgets/transfer/transfer.widget.spec.ts +++ b/packages/form/widgets/transfer/widget.spec.ts @@ -2,11 +2,12 @@ import { DebugElement } from '@angular/core'; import { ComponentFixture, fakeAsync } from '@angular/core/testing'; import { of } from 'rxjs'; +import { SFSchema } from '@delon/form'; import { createTestContext } from '@delon/testing'; import { NzSafeAny } from 'ng-zorro-antd/core/types'; -import { configureSFTestSuite, SFPage, TestFormComponent } from '../../../spec/base.spec'; -import { SFSchema } from '../../../src/schema/index'; +import { withTransferWidget } from './index'; +import { configureSFTestSuite, SFPage, TestFormComponent } from '../../spec/base.spec'; describe('form: widget: transfer', () => { let fixture: ComponentFixture; @@ -21,7 +22,7 @@ describe('form: widget: transfer', () => { rightBtn: '.ant-transfer-operation .ant-btn:last-child' }; - configureSFTestSuite(); + configureSFTestSuite({ widgets: [withTransferWidget()] }); beforeEach(() => { ({ fixture, dl, context } = createTestContext(TestFormComponent)); diff --git a/packages/form/src/widgets/transfer/transfer.widget.ts b/packages/form/widgets/transfer/widget.ts similarity index 67% rename from packages/form/src/widgets/transfer/transfer.widget.ts rename to packages/form/widgets/transfer/widget.ts index 7b87eb1aaa..4f8099d755 100644 --- a/packages/form/src/widgets/transfer/transfer.widget.ts +++ b/packages/form/widgets/transfer/widget.ts @@ -1,8 +1,11 @@ import { Component, OnInit, ViewEncapsulation } from '@angular/core'; +import { FormsModule } from '@angular/forms'; import { Observable, of } from 'rxjs'; +import { ControlUIWidget, DelonFormModule, SFSchemaEnum, SFValue, getData } from '@delon/form'; import type { NzSafeAny } from 'ng-zorro-antd/core/types'; import { + NzTransferModule, TransferCanMove, TransferChange, TransferItem, @@ -10,19 +13,43 @@ import { TransferSelectChange } from 'ng-zorro-antd/transfer'; -import { SFTransferWidgetSchema } from './schema'; -import { SFValue } from '../../interface'; -import { SFSchemaEnum } from '../../schema'; -import { getData } from '../../utils'; -import { ControlUIWidget } from '../../widget'; +import type { SFTransferWidgetSchema } from './schema'; @Component({ selector: 'sf-transfer', - templateUrl: './transfer.widget.html', + template: ` + + `, preserveWhitespaces: false, - encapsulation: ViewEncapsulation.None + encapsulation: ViewEncapsulation.None, + standalone: true, + imports: [FormsModule, DelonFormModule, NzTransferModule] }) export class TransferWidget extends ControlUIWidget implements OnInit { + static readonly KEY = 'transfer'; + list: SFSchemaEnum[] = []; i!: { titles: string[]; operations: string[]; itemUnit: string; itemsUnit: string }; private _data: SFSchemaEnum[] = []; diff --git a/packages/form/src/widgets/tree-select/demo/customized-icon.md b/packages/form/widgets/tree-select/demo/customized-icon.md similarity index 89% rename from packages/form/src/widgets/tree-select/demo/customized-icon.md rename to packages/form/widgets/tree-select/demo/customized-icon.md index 391b1b9764..12d9c37c5a 100644 --- a/packages/form/src/widgets/tree-select/demo/customized-icon.md +++ b/packages/form/widgets/tree-select/demo/customized-icon.md @@ -16,13 +16,16 @@ You can customize icons for different nodes. ```ts import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core'; -import { SFSchema, SFTreeSelectWidgetSchema } from '@delon/form'; +import { SFSchema } from '@delon/form'; +import type { SFTreeSelectWidgetSchema } from '@delon/form/widgets/tree-select'; import { NzTreeNode } from 'ng-zorro-antd/tree'; @Component({ selector: 'app-demo', template: ` - + @if (schema) { + + } {{ node.title }} diff --git a/packages/form/src/widgets/tree-select/demo/simple.md b/packages/form/widgets/tree-select/demo/simple.md similarity index 95% rename from packages/form/src/widgets/tree-select/demo/simple.md rename to packages/form/widgets/tree-select/demo/simple.md index 63bf97786d..2872430153 100644 --- a/packages/form/src/widgets/tree-select/demo/simple.md +++ b/packages/form/widgets/tree-select/demo/simple.md @@ -17,7 +17,8 @@ Simplest of usage. ```ts import { Component } from '@angular/core'; -import { SFSchema, SFTreeSelectWidgetSchema } from '@delon/form'; +import { SFSchema } from '@delon/form'; +import type { SFTreeSelectWidgetSchema } from '@delon/form/widgets/tree-select'; import { NzMessageService } from 'ng-zorro-antd/message'; import { of, delay } from 'rxjs'; diff --git a/packages/form/src/widgets/tree-select/demo/virtual-scroll.md b/packages/form/widgets/tree-select/demo/virtual-scroll.md similarity index 92% rename from packages/form/src/widgets/tree-select/demo/virtual-scroll.md rename to packages/form/widgets/tree-select/demo/virtual-scroll.md index 8bc52c39ee..906ece9e9a 100644 --- a/packages/form/src/widgets/tree-select/demo/virtual-scroll.md +++ b/packages/form/widgets/tree-select/demo/virtual-scroll.md @@ -15,7 +15,8 @@ Set `virtualHeight` to enable virtual scroll. ```ts import { Component } from '@angular/core'; -import { SFSchema, SFTreeSelectWidgetSchema } from '@delon/form'; +import { SFSchema } from '@delon/form'; +import type { SFTreeSelectWidgetSchema } from '@delon/form/widgets/tree-select'; import { NzMessageService } from 'ng-zorro-antd/message'; import { NzTreeNodeOptions } from 'ng-zorro-antd/tree'; diff --git a/packages/form/src/widgets/tree-select/index.en-US.md b/packages/form/widgets/tree-select/index.en-US.md similarity index 93% rename from packages/form/src/widgets/tree-select/index.en-US.md rename to packages/form/widgets/tree-select/index.en-US.md index ff8ce4fefc..3d8cb7f9bc 100644 --- a/packages/form/src/widgets/tree-select/index.en-US.md +++ b/packages/form/widgets/tree-select/index.en-US.md @@ -1,7 +1,7 @@ --- title: tree-select subtitle: Tree Select -type: Widgets +type: Non-built-in widgets --- Tree select widget. @@ -10,6 +10,10 @@ Tree select widget. - Data source of `tree-select` must have keys of `title`、`key` +## Import module + +Non-built-in modules, Should be import `TreeSelectWidgetModule` in [json-schema.module.ts](https://github.com/ng-alain/ng-alain/blob/master/src/app/shared/json-schema/json-schema.module.ts#L11). + ## API ### schema diff --git a/packages/form/widgets/tree-select/index.ts b/packages/form/widgets/tree-select/index.ts new file mode 100644 index 0000000000..db7d27bc7b --- /dev/null +++ b/packages/form/widgets/tree-select/index.ts @@ -0,0 +1,11 @@ +import type { SFWidgetProvideConfig } from '@delon/form'; + +import { TreeSelectWidget } from './widget'; + +export * from './widget'; +export * from './schema'; +export * from './module'; + +export function withTreeSelectWidget(): SFWidgetProvideConfig { + return { KEY: TreeSelectWidget.KEY, type: TreeSelectWidget }; +} diff --git a/packages/form/src/widgets/tree-select/index.zh-CN.md b/packages/form/widgets/tree-select/index.zh-CN.md similarity index 92% rename from packages/form/src/widgets/tree-select/index.zh-CN.md rename to packages/form/widgets/tree-select/index.zh-CN.md index 9a595be2b2..08ba1b2435 100644 --- a/packages/form/src/widgets/tree-select/index.zh-CN.md +++ b/packages/form/widgets/tree-select/index.zh-CN.md @@ -1,7 +1,7 @@ --- title: tree-select subtitle: 树选择 -type: Widgets +type: Non-built-in widgets --- 树型选择控件。 @@ -10,6 +10,10 @@ type: Widgets - `tree-select` 的数据源必须包含 `title`、`key` 键名 +## 导入模块 + +非内置模块,需要额外在 [json-schema.module.ts](https://github.com/ng-alain/ng-alain/blob/master/src/app/shared/json-schema/json-schema.module.ts#L11) 导入 `TreeSelectWidgetModule`。 + ## API ### schema 属性 diff --git a/packages/form/widgets/tree-select/module.ts b/packages/form/widgets/tree-select/module.ts new file mode 100644 index 0000000000..cfec2a55dd --- /dev/null +++ b/packages/form/widgets/tree-select/module.ts @@ -0,0 +1,16 @@ +import { NgModule } from '@angular/core'; +import { FormsModule } from '@angular/forms'; + +import { DelonFormModule, WidgetRegistry } from '@delon/form'; +import { NzTreeSelectModule } from 'ng-zorro-antd/tree-select'; + +import { TreeSelectWidget } from './widget'; + +@NgModule({ + imports: [FormsModule, DelonFormModule, NzTreeSelectModule, TreeSelectWidget] +}) +export class TreeSelectWidgetModule { + constructor(widgetRegistry: WidgetRegistry) { + widgetRegistry.register(TreeSelectWidget.KEY, TreeSelectWidget); + } +} diff --git a/packages/form/widgets/tree-select/ng-package.json b/packages/form/widgets/tree-select/ng-package.json new file mode 100644 index 0000000000..b3d67c2122 --- /dev/null +++ b/packages/form/widgets/tree-select/ng-package.json @@ -0,0 +1,6 @@ +{ + "lib": { + "flatModuleFile": "widgets-tree-select", + "entryFile": "index.ts" + } +} diff --git a/packages/form/src/widgets/tree-select/schema.ts b/packages/form/widgets/tree-select/schema.ts similarity index 96% rename from packages/form/src/widgets/tree-select/schema.ts rename to packages/form/widgets/tree-select/schema.ts index 3542bbee7c..fc0d483738 100644 --- a/packages/form/src/widgets/tree-select/schema.ts +++ b/packages/form/widgets/tree-select/schema.ts @@ -1,12 +1,10 @@ import { TemplateRef } from '@angular/core'; import { Observable } from 'rxjs'; +import type { SFSchemaEnum, SFSchemaEnumType, SFUISchemaItem } from '@delon/form'; import { NzFormatEmitEvent, NzTreeNode, NzTreeNodeOptions } from 'ng-zorro-antd/core/tree'; import { NgStyleInterface, NzSizeLDSType } from 'ng-zorro-antd/core/types'; -import { SFSchemaEnum, SFSchemaEnumType } from '../../schema'; -import { SFUISchemaItem } from '../../schema/ui'; - export interface SFTreeSelectWidgetSchema extends SFUISchemaItem { /** * 异步数据源 diff --git a/packages/form/src/widgets/tree-select/tree-select.widget.spec.ts b/packages/form/widgets/tree-select/widget.spec.ts similarity index 95% rename from packages/form/src/widgets/tree-select/tree-select.widget.spec.ts rename to packages/form/widgets/tree-select/widget.spec.ts index 4fa6ebf080..dccba62fdc 100644 --- a/packages/form/src/widgets/tree-select/tree-select.widget.spec.ts +++ b/packages/form/widgets/tree-select/widget.spec.ts @@ -2,11 +2,12 @@ import { DebugElement } from '@angular/core'; import { ComponentFixture, fakeAsync } from '@angular/core/testing'; import { of } from 'rxjs'; +import { SFSchema } from '@delon/form'; import { createTestContext } from '@delon/testing'; import { NzSafeAny } from 'ng-zorro-antd/core/types'; -import { configureSFTestSuite, SFPage, TestFormComponent } from '../../../spec/base.spec'; -import { SFSchema } from '../../../src/schema/index'; +import { withTreeSelectWidget } from './index'; +import { configureSFTestSuite, SFPage, TestFormComponent } from '../../spec/base.spec'; describe('form: widget: tree-select', () => { let fixture: ComponentFixture; @@ -15,7 +16,7 @@ describe('form: widget: tree-select', () => { let dl: DebugElement; const widget = 'tree-select'; - configureSFTestSuite(); + configureSFTestSuite({ widgets: [withTreeSelectWidget()] }); beforeEach(() => { ({ fixture, dl, context } = createTestContext(TestFormComponent)); diff --git a/packages/form/widgets/tree-select/widget.ts b/packages/form/widgets/tree-select/widget.ts new file mode 100644 index 0000000000..831ff43347 --- /dev/null +++ b/packages/form/widgets/tree-select/widget.ts @@ -0,0 +1,107 @@ +import { Component, OnInit, ViewEncapsulation } from '@angular/core'; +import { FormsModule } from '@angular/forms'; + +import { ControlUIWidget, DelonFormModule, SFSchemaEnum, SFValue, getData, toBool } from '@delon/form'; +import { NzFormatEmitEvent, NzTreeNode } from 'ng-zorro-antd/core/tree'; +import { NzTreeSelectModule } from 'ng-zorro-antd/tree-select'; + +import type { SFTreeSelectWidgetSchema } from './schema'; + +@Component({ + selector: 'sf-tree-select', + template: ` + + `, + preserveWhitespaces: false, + encapsulation: ViewEncapsulation.None, + standalone: true, + imports: [FormsModule, DelonFormModule, NzTreeSelectModule] +}) +export class TreeSelectWidget extends ControlUIWidget implements OnInit { + static readonly KEY = 'tree-select'; + + i!: SFTreeSelectWidgetSchema; + data: SFSchemaEnum[] = []; + asyncData = false; + + ngOnInit(): void { + const { ui } = this; + this.i = { + allowClear: ui.allowClear, + showSearch: toBool(ui.showSearch, false), + dropdownMatchSelectWidth: toBool(ui.dropdownMatchSelectWidth, true), + multiple: toBool(ui.multiple, false), + checkable: toBool(ui.checkable, false), + showIcon: toBool(ui.showIcon, false), + showExpand: toBool(ui.showExpand, true), + showLine: toBool(ui.showLine, false), + checkStrictly: toBool(ui.checkStrictly, false), + hideUnMatched: toBool(ui.hideUnMatched, false), + defaultExpandAll: toBool(ui.defaultExpandAll, false), + displayWith: ui.displayWith || ((node: NzTreeNode) => node.title) + }; + this.asyncData = typeof ui.expandChange === 'function'; + } + + reset(value: SFValue): void { + getData(this.schema, this.ui, value).subscribe(list => { + this.data = list; + this.detectChanges(); + }); + } + + change(value: string[] | string): void { + if (this.ui.change) this.ui.change(value); + this.setValue(value); + } + + expandChange(e: NzFormatEmitEvent): void { + const { ui } = this; + if (typeof ui.expandChange !== 'function') return; + ui.expandChange(e).subscribe(res => { + e.node!.clearChildren(); + e.node!.addChildren(res); + this.detectChanges(); + }); + } +} diff --git a/packages/form/src/widgets/upload/demo/simple.md b/packages/form/widgets/upload/demo/simple.md similarity index 93% rename from packages/form/src/widgets/upload/demo/simple.md rename to packages/form/widgets/upload/demo/simple.md index e6d733953c..072b085b30 100644 --- a/packages/form/src/widgets/upload/demo/simple.md +++ b/packages/form/widgets/upload/demo/simple.md @@ -15,7 +15,8 @@ Simplest of usage. ```ts import { Component } from '@angular/core'; -import { SFSchema, SFUploadWidgetSchema } from '@delon/form'; +import { SFSchema } from '@delon/form'; +import type { SFUploadWidgetSchema } from '@delon/form/widgets/upload'; import { NzMessageService } from 'ng-zorro-antd/message'; @Component({ diff --git a/packages/form/src/widgets/upload/index.en-US.md b/packages/form/widgets/upload/index.en-US.md similarity index 94% rename from packages/form/src/widgets/upload/index.en-US.md rename to packages/form/widgets/upload/index.en-US.md index 330c2916cd..a1c3ce0e24 100644 --- a/packages/form/src/widgets/upload/index.en-US.md +++ b/packages/form/widgets/upload/index.en-US.md @@ -1,11 +1,15 @@ --- title: upload subtitle: Upload -type: Widgets +type: Non-built-in widgets --- Upload file widget by select or drag. +## Import module + +Non-built-in modules, Should be import `UploadWidgetModule` in [json-schema.module.ts](https://github.com/ng-alain/ng-alain/blob/master/src/app/shared/json-schema/json-schema.module.ts#L11). + ## Note - **Must** set `resReName` to get correct data diff --git a/packages/form/widgets/upload/index.ts b/packages/form/widgets/upload/index.ts new file mode 100644 index 0000000000..8ed8d295a5 --- /dev/null +++ b/packages/form/widgets/upload/index.ts @@ -0,0 +1,11 @@ +import type { SFWidgetProvideConfig } from '@delon/form'; + +import { UploadWidget } from './widget'; + +export * from './widget'; +export * from './schema'; +export * from './module'; + +export function withUploadWidget(): SFWidgetProvideConfig { + return { KEY: UploadWidget.KEY, type: UploadWidget }; +} diff --git a/packages/form/src/widgets/upload/index.zh-CN.md b/packages/form/widgets/upload/index.zh-CN.md similarity index 94% rename from packages/form/src/widgets/upload/index.zh-CN.md rename to packages/form/widgets/upload/index.zh-CN.md index 7d29236496..504ea04e0b 100644 --- a/packages/form/src/widgets/upload/index.zh-CN.md +++ b/packages/form/widgets/upload/index.zh-CN.md @@ -1,11 +1,15 @@ --- title: upload subtitle: 上传 -type: Widgets +type: Non-built-in widgets --- 文件选择上传和拖拽上传控件。 +## 导入模块 + +非内置模块,需要额外在 [json-schema.module.ts](https://github.com/ng-alain/ng-alain/blob/master/src/app/shared/json-schema/json-schema.module.ts#L11) 导入 `UploadWidgetModule`。 + ## 注意事项 - **务必** 指定 `resReName` 来获取正确数据 diff --git a/packages/form/widgets/upload/module.ts b/packages/form/widgets/upload/module.ts new file mode 100644 index 0000000000..483f9ce367 --- /dev/null +++ b/packages/form/widgets/upload/module.ts @@ -0,0 +1,19 @@ +import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { FormsModule } from '@angular/forms'; + +import { DelonFormModule, WidgetRegistry } from '@delon/form'; +import { NzButtonModule } from 'ng-zorro-antd/button'; +import { NzIconModule } from 'ng-zorro-antd/icon'; +import { NzUploadModule } from 'ng-zorro-antd/upload'; + +import { UploadWidget } from './widget'; + +@NgModule({ + imports: [FormsModule, CommonModule, DelonFormModule, NzUploadModule, NzIconModule, NzButtonModule, UploadWidget] +}) +export class UploadWidgetModule { + constructor(widgetRegistry: WidgetRegistry) { + widgetRegistry.register(UploadWidget.KEY, UploadWidget); + } +} diff --git a/packages/form/widgets/upload/ng-package.json b/packages/form/widgets/upload/ng-package.json new file mode 100644 index 0000000000..b39b5ce0f0 --- /dev/null +++ b/packages/form/widgets/upload/ng-package.json @@ -0,0 +1,6 @@ +{ + "lib": { + "flatModuleFile": "widgets-upload", + "entryFile": "index.ts" + } +} diff --git a/packages/form/src/widgets/upload/schema.ts b/packages/form/widgets/upload/schema.ts similarity index 97% rename from packages/form/src/widgets/upload/schema.ts rename to packages/form/widgets/upload/schema.ts index e3b0420a43..514aa32d26 100644 --- a/packages/form/src/widgets/upload/schema.ts +++ b/packages/form/widgets/upload/schema.ts @@ -1,6 +1,7 @@ import { Observable, Subscription } from 'rxjs'; -import { +import type { SFSchemaEnumType, SFUISchemaItem } from '@delon/form'; +import type { NzShowUploadList, NzUploadChangeParam, NzUploadFile, @@ -9,9 +10,6 @@ import { UploadFilter } from 'ng-zorro-antd/upload'; -import { SFSchemaEnumType } from '../../schema'; -import { SFUISchemaItem } from '../../schema/ui'; - export interface SFUploadWidgetSchema extends SFUISchemaItem { /** * 异步数据源 diff --git a/packages/form/src/widgets/upload/upload.widget.spec.ts b/packages/form/widgets/upload/widget.spec.ts similarity index 85% rename from packages/form/src/widgets/upload/upload.widget.spec.ts rename to packages/form/widgets/upload/widget.spec.ts index 48f7a87469..3225cf9b05 100644 --- a/packages/form/src/widgets/upload/upload.widget.spec.ts +++ b/packages/form/widgets/upload/widget.spec.ts @@ -1,14 +1,15 @@ import { DebugElement } from '@angular/core'; -import { ComponentFixture, fakeAsync, inject } from '@angular/core/testing'; +import { ComponentFixture, fakeAsync, TestBed } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; import { createTestContext } from '@delon/testing'; import { NzSafeAny } from 'ng-zorro-antd/core/types'; -import { NzModalService } from 'ng-zorro-antd/modal'; +import { NzImageModule, NzImageService } from 'ng-zorro-antd/image'; import { NzUploadComponent } from 'ng-zorro-antd/upload'; -import { UploadWidget } from './upload.widget'; -import { configureSFTestSuite, SFPage, TestFormComponent } from '../../../spec/base.spec'; +import { withUploadWidget } from './index'; +import { UploadWidget } from './widget'; +import { configureSFTestSuite, SFPage, TestFormComponent } from '../../spec/base.spec'; describe('form: widget: upload', () => { let fixture: ComponentFixture; @@ -17,7 +18,7 @@ describe('form: widget: upload', () => { let dl: DebugElement; const widget = 'upload'; - configureSFTestSuite(); + configureSFTestSuite({ widgets: [withUploadWidget()], imports: [NzImageModule] }); beforeEach(() => { ({ fixture, dl, context } = createTestContext(TestFormComponent)); @@ -163,7 +164,7 @@ describe('form: widget: upload', () => { comp.handlePreview(null!); page.checkCalled('a', 'preview'); }); - it('should be preview image', inject([NzModalService], (msg: NzModalService) => { + it('should be preview image', () => { page.newSchema({ properties: { a: { @@ -173,27 +174,26 @@ describe('form: widget: upload', () => { } }); const comp = page.getWidget('sf-upload'); - spyOn(msg, 'create'); + const imgSrv = TestBed.inject(NzImageService); + spyOn(imgSrv, 'preview'); comp.handlePreview({ url: 'a' } as NzSafeAny); - expect(msg.create).toHaveBeenCalled(); - })); - it(`should be won't preview image when not found url property`, inject( - [NzModalService], - (msg: NzModalService) => { - page.newSchema({ - properties: { - a: { - type: 'string', - ui: { widget } - } + expect(imgSrv.preview).toHaveBeenCalled(); + }); + it(`should be won't preview image when not found url property`, () => { + page.newSchema({ + properties: { + a: { + type: 'string', + ui: { widget } } - }); - const comp = page.getWidget('sf-upload'); - spyOn(msg, 'create'); - comp.handlePreview({} as NzSafeAny); - expect(msg.create).not.toHaveBeenCalled(); - } - )); + } + }); + const comp = page.getWidget('sf-upload'); + const imgSrv = TestBed.inject(NzImageService); + spyOn(imgSrv, 'preview'); + comp.handlePreview({} as NzSafeAny); + expect(imgSrv.preview).not.toHaveBeenCalled(); + }); }); }); diff --git a/packages/form/src/widgets/upload/upload.widget.ts b/packages/form/widgets/upload/widget.ts similarity index 60% rename from packages/form/src/widgets/upload/upload.widget.ts rename to packages/form/widgets/upload/widget.ts index 49ecf797b5..1e867ab8a5 100644 --- a/packages/form/src/widgets/upload/upload.widget.ts +++ b/packages/form/widgets/upload/widget.ts @@ -1,23 +1,80 @@ import { Component, OnInit, ViewEncapsulation } from '@angular/core'; +import { FormsModule } from '@angular/forms'; import { of } from 'rxjs'; +import { ControlUIWidget, DelonFormModule, SFValue, getData, toBool } from '@delon/form'; import { deepGet } from '@delon/util/other'; +import { NzButtonModule } from 'ng-zorro-antd/button'; import type { NzSafeAny } from 'ng-zorro-antd/core/types'; -import { NzModalService } from 'ng-zorro-antd/modal'; -import { NzUploadChangeParam, NzUploadFile } from 'ng-zorro-antd/upload'; +import { NzIconModule } from 'ng-zorro-antd/icon'; +import { NzImageService } from 'ng-zorro-antd/image'; +import { NzUploadChangeParam, NzUploadFile, NzUploadModule } from 'ng-zorro-antd/upload'; -import { SFUploadWidgetSchema } from './schema'; -import { SFValue } from '../../interface'; -import { getData, toBool } from '../../utils'; -import { ControlUIWidget } from '../../widget'; +import type { SFUploadWidgetSchema } from './schema'; @Component({ selector: 'sf-upload', - templateUrl: './upload.widget.html', + template: ` + + @switch (btnType) { + @case ('plus') { + +
+ } + @case ('drag') { +

+

+

+ } + @default { + + } + } +
+
`, preserveWhitespaces: false, - encapsulation: ViewEncapsulation.None + encapsulation: ViewEncapsulation.None, + standalone: true, + imports: [FormsModule, DelonFormModule, NzUploadModule, NzIconModule, NzButtonModule] }) export class UploadWidget extends ControlUIWidget implements OnInit { + static readonly KEY = 'upload'; + i: NzSafeAny; fileList: NzUploadFile[] = []; btnType = ''; @@ -130,9 +187,6 @@ export class UploadWidget extends ControlUIWidget implemen if (!_url) { return; } - this.injector.get(NzModalService).create({ - nzContent: ``, - nzFooter: null - }); + this.injector.get(NzImageService, null)?.preview([{ src: _url }]); }; } diff --git a/packages/mock/docs/getting-started.en-US.md b/packages/mock/docs/getting-started.en-US.md index a4af7026e4..bda158aba4 100644 --- a/packages/mock/docs/getting-started.en-US.md +++ b/packages/mock/docs/getting-started.en-US.md @@ -35,7 +35,18 @@ Please refer to [global-config.module.ts](https://github.com/ng-alain/ng-alain/b | `[delay]` | `number` | `300` | Request delay, unit is milliseconds | ✅ | | `[force]` | `boolean` | `false` | Whether to force all requests to Mock, `true` means to return a 404 error directly when the requested URL does not exist, `false` means to send a real HTTP request when the request is missed | ✅ | | `[log]` | `boolean` | `true` | Whether to print Mock request information, make up for the browser without Network information; it will output [👽Mock] when hit | ✅ | -| `[executeOtherInterceptors]` | `boolean` | `true` | Whether continue to call other interceptor `intercept` method after mock rule hit | ✅ | | `[copy]` | `boolean` | `true` | Whether to return copy data | ✅ | -> **Lazy modules** need to import `forChild`, You can import `forChild` in the `SharedModule`. +### Why is it only valid for development environment? + +Mock is not real data, and most scenarios are for development local or test environments; therefore, Mock modules and rule data should not be included in the production environment. + +Of course, you can also put the `provideMockConfig` of `environment.ts` under `environment.prod.ts` so that the production environment also uses this rule, just like https://ng-alain.github.io/ng- Like alain/, some mock requests are needed to ensure the environment runs. + +```ts +import { provideMockConfig } from '@delon/mock'; +import * as MOCKDATA from '../../_mock'; +export const environment = { + providers: [provideMockConfig({ data: MOCKDATA })], +} as Environment; +``` diff --git a/packages/mock/docs/getting-started.zh-CN.md b/packages/mock/docs/getting-started.zh-CN.md index 2c8859293b..df87b565e1 100644 --- a/packages/mock/docs/getting-started.zh-CN.md +++ b/packages/mock/docs/getting-started.zh-CN.md @@ -35,23 +35,18 @@ npm i --save-dev @delon/mock | `[delay]` | `number` | `300` | 请求延迟,单位:毫秒 | ✅ | | `[force]` | `boolean` | `false` | 是否强制所有请求都Mock,`true` 表示当请求的URL不存在时直接返回 404 错误,`false` 表示未命中时发送真实HTTP请求 | ✅ | | `[log]` | `boolean` | `true` | 是否打印 Mock 请求信息,弥补浏览器无Network信息;当请求经过 Mock 会接收【👽Mock】 | ✅ | -| `[executeOtherInterceptors]` | `boolean` | `true` | 是否拦截命中后继续调用后续拦截器的 `intercept` 方法 | ✅ | | `[copy]` | `boolean` | `true` | 是否返回副本数据 | ✅ | -> 若**懒模块**还需要导入 `forChild` 确保HTTP拦截器有效,一般可以直接在 SharedModule 直接使用 `forChild`。 - ### 为什么只对开发环境有效? -Mock 并非是真实数据,大部分场景是针对开发本地或测试环境;所以在生产环境中不应该包括 Mock 模块以及规则数据。因此上述才会根据 `!environment.production` 依据环境来决定是否加载 `DelonMockModule`。 +Mock 并非是真实数据,大部分场景是针对开发本地或测试环境;所以在生产环境中不应该包括 Mock 模块以及规则数据。 -当然,你依然可以在生产环境也使用这种规则,就像 //ng-alain.github.io/ng-alain/ 一样,需要一些模拟请求来保证环境的运行。 +当然,也可以将 `environment.ts` 的 `provideMockConfig` 放到 `environment.prod.ts` 下,使得生产环境也使用这种规则,就像 https://ng-alain.github.io/ng-alain/ 一样,需要一些模拟请求来保证环境的运行。 ```ts -import { DelonMockModule } from '@delon/mock'; +import { provideMockConfig } from '@delon/mock'; import * as MOCKDATA from '../../_mock'; -@NgModule({ - imports: [ - DelonMockModule.forRoot({ data: MOCKDATA }) - ] -}) +export const environment = { + providers: [provideMockConfig({ data: MOCKDATA })], +} as Environment; ``` diff --git a/packages/mock/docs/rule.en-US.md b/packages/mock/docs/rule.en-US.md index d5477a96b2..83ed10f328 100644 --- a/packages/mock/docs/rule.en-US.md +++ b/packages/mock/docs/rule.en-US.md @@ -36,7 +36,7 @@ export const USERS = { ## Value -Supports three types: `Object`, `Array`, `(req: MockRequest) => any`. +Supports three types: `Object`, `Array`, `(req: MockRequest) => any | Observable | Promise`. ```ts import { MockStatusError } from '@delon/mock'; @@ -51,7 +51,14 @@ export const USERS = { // Support HttpResponse '/http': (req: MockRequest) => new HttpResponse({ body: 'Body', headers: new HttpHeaders({ 'token': '1' }) }), // Send Status Error - '/404': () => { throw new MockStatusError(404); } + '/404': () => { throw new MockStatusError(404); }, + // Support Observable + '/obs': () => of(1), + // Support Promise + '/promise': async () => { + await delay(10); + return 1; + } }; ``` @@ -88,7 +95,14 @@ export const USERS = { // Send Status Error '/404': () => { throw new MockStatusError(404); }, // Regular expressions need to be wrapped with `()` - '/data/(.*)': (req: MockRequest) => req + '/data/(.*)': (req: MockRequest) => req, + // Support Observable + '/obs': () => of(1), + // Support Promise + '/promise': async () => { + await delay(10); + return 1; + } }; ``` diff --git a/packages/mock/docs/rule.zh-CN.md b/packages/mock/docs/rule.zh-CN.md index 494e83a498..af33541052 100644 --- a/packages/mock/docs/rule.zh-CN.md +++ b/packages/mock/docs/rule.zh-CN.md @@ -36,7 +36,7 @@ export const USERS = { ## Value 响应内容 -响应内容支持三种类型:`Object`、`Array`、`(req: MockRequest) => any`。 +响应内容支持三种类型:`Object`、`Array`、`(req: MockRequest) => any | Observable | Promise`。 ```ts import { MockStatusError } from '@delon/mock'; @@ -51,7 +51,14 @@ export const USERS = { // 支持返回完整的 HttpResponse '/http': (req: MockRequest) => new HttpResponse({ body: 'Body', headers: new HttpHeaders({ 'token': '1' }) }), // 发送 Status 错误 - '/404': () => { throw new MockStatusError(404); } + '/404': () => { throw new MockStatusError(404); }, + // 支持 Observable + '/obs': () => of(1), + // 支持 Promise + '/promise': async () => { + await delay(10); + return 1; + } }; ``` @@ -88,7 +95,14 @@ export const USERS = { // 发送 Status 错误 '/404': () => { throw new MockStatusError(404); }, // 使用 () 表示:正则表达式 - '/data/(.*)': (req: MockRequest) => req + '/data/(.*)': (req: MockRequest) => req, + // 支持 Observable + '/obs': () => of(1), + // 支持 Promise + '/promise': async () => { + await delay(10); + return 1; + } }; ``` diff --git a/packages/mock/public_api.ts b/packages/mock/public_api.ts index fc4791eec1..cf1e52f61d 100644 --- a/packages/mock/public_api.ts +++ b/packages/mock/public_api.ts @@ -2,4 +2,5 @@ export * from './src/interface'; export * from './src/status.error'; export * from './src/mock.service'; export * from './src/mock.interceptor'; -export * from './src/mock.module'; +export * from './src/provide'; +export * from './src/utils'; diff --git a/packages/mock/src/interface.ts b/packages/mock/src/interface.ts index 69aa6f2e6e..0fe80031c0 100644 --- a/packages/mock/src/interface.ts +++ b/packages/mock/src/interface.ts @@ -1,7 +1,10 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { HttpRequest } from '@angular/common/http'; +import { type HttpRequest } from '@angular/common/http'; +import type { Observable } from 'rxjs'; -export class MockOptions { +export type MockCallback = any | Observable | Promise; + +export interface MockOptions { data?: any; } @@ -16,7 +19,7 @@ export interface MockCachedRule { segments: string[]; - callback(req: MockRequest): any; + callback(req: MockRequest): MockCallback; } export interface MockRule { @@ -29,7 +32,7 @@ export interface MockRule { /** 路由参数 */ params?: any; - callback(req: MockRequest): any; + callback(req: MockRequest): MockCallback; } export interface MockRequest { diff --git a/packages/mock/src/mock.config.ts b/packages/mock/src/mock.config.ts index c4918b7a1c..35b1161af5 100644 --- a/packages/mock/src/mock.config.ts +++ b/packages/mock/src/mock.config.ts @@ -3,6 +3,5 @@ import type { AlainMockConfig } from '@delon/util/config'; export const MOCK_DEFULAT_CONFIG: AlainMockConfig = { delay: 300, force: false, - log: true, - executeOtherInterceptors: true + log: true }; diff --git a/packages/mock/src/mock.interceptor.spec.ts b/packages/mock/src/mock.interceptor.spec.ts index f94c7be99e..aaad6dd3d1 100644 --- a/packages/mock/src/mock.interceptor.spec.ts +++ b/packages/mock/src/mock.interceptor.spec.ts @@ -1,28 +1,20 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { - HttpClient, - HttpEvent, - HttpHandler, - HttpHeaders, - HttpInterceptor, - HttpRequest, - HttpResponse, - HTTP_INTERCEPTORS -} from '@angular/common/http'; -import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; -import { Component, NgModule, Type } from '@angular/core'; -import { fakeAsync, TestBed, tick } from '@angular/core/testing'; -import { Router, RouterModule } from '@angular/router'; -import { RouterTestingModule } from '@angular/router/testing'; -import { Observable, map } from 'rxjs'; +import { HttpClient, HttpHeaders, HttpResponse, provideHttpClient, withInterceptors } from '@angular/common/http'; +import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing'; +import { Component, Type } from '@angular/core'; +import { TestBed } from '@angular/core/testing'; +import { provideRouter } from '@angular/router'; +import { lastValueFrom, of } from 'rxjs'; import * as Mock from 'mockjs'; -import { AlainMockConfig, ALAIN_CONFIG } from '@delon/util/config'; +import { AlainMockConfig, provideAlainConfig } from '@delon/util/config'; import { MockRequest } from './interface'; -import { DelonMockModule } from './mock.module'; +import { mockInterceptor } from './mock.interceptor'; +import { provideMockConfig } from './provide'; import { MockStatusError } from './status.error'; +import { delay, r } from './utils'; const USER_LIST = { users: [1, 2], a: 0 }; const DATA = { @@ -41,41 +33,34 @@ const DATA = { }, '/500': () => { throw new Error('500'); + }, + '/obs': () => of(r(1, 1)), + '/promise': async () => { + await delay(10); + return 'a'; } } }; -let otherRes = new HttpResponse(); -class OtherInterceptor implements HttpInterceptor { - intercept(req: HttpRequest, next: HttpHandler): Observable> { - return next.handle(req.clone()).pipe(map(() => otherRes)); - } -} - describe('mock: interceptor', () => { let http: HttpClient; let httpMock: HttpTestingController; - function genModule( - data: any, - options: AlainMockConfig, - imports: any[] = [], - spyConsole: boolean = true, - providers?: any[] - ): void { + function genModule(data: any, options: AlainMockConfig, spyConsole: boolean = true): void { TestBed.configureTestingModule({ declarations: [RootComponent], - imports: [ - HttpClientTestingModule, - RouterTestingModule.withRoutes([ + providers: [ + provideHttpClient(withInterceptors([mockInterceptor])), + provideHttpClientTesting(), + provideRouter([ { path: 'lazy', loadChildren: jasmine.createSpy('expected') } ]), - DelonMockModule.forRoot({ data }) - ].concat(imports), - providers: ([{ provide: ALAIN_CONFIG, useValue: { mock: options } }] as any[]).concat(providers || []) + provideAlainConfig({ mock: options }), + provideMockConfig({ data }) + ] }); http = TestBed.inject(HttpClient); httpMock = TestBed.inject(HttpTestingController as Type); @@ -86,8 +71,8 @@ describe('mock: interceptor', () => { } describe('[default]', () => { - beforeEach(() => genModule(DATA, { executeOtherInterceptors: false, delay: 1 })); - it('should be init', (done: () => void) => { + beforeEach(() => genModule(DATA, { delay: 1 })); + it('should be init', done => { http.get('/users').subscribe((res: any) => { expect(res).not.toBeNull(); expect(res.users).not.toBeNull(); @@ -198,6 +183,15 @@ describe('mock: interceptor', () => { done(); }); }); + it('should be return a observable', () => { + http.get('/obs').subscribe(res => { + expect(res).toBe(1); + }); + }); + it('should be return a promise', async () => { + const res = await lastValueFrom(http.get('/promise')); + expect(res).toBe('a'); + }); }); describe('[disabled log]', () => { @@ -223,62 +217,6 @@ describe('mock: interceptor', () => { }); }); }); - - describe('[lazy module]', () => { - beforeEach(() => genModule(DATA, { delay: 1 })); - - it('should work', fakeAsync(() => { - @Component({ - selector: 'lazy', - template: '' - }) - class LayoutComponent {} - - @Component({ - selector: 'child', - template: 'length-{{res.users.length}}' - }) - class ChildComponent { - res: any = {}; - constructor(HTTP: HttpClient) { - HTTP.get('/users').subscribe(res => (this.res = res)); - } - } - - @NgModule({ - declarations: [LayoutComponent, ChildComponent], - imports: [DelonMockModule.forChild(), RouterModule.forChild([{ path: 'child', component: ChildComponent }])] - }) - class LazyModule {} - - const fixture = TestBed.createComponent(RootComponent); - fixture.detectChanges(); - - const router = TestBed.inject(Router); - router.resetConfig([{ path: 'lazy', loadChildren: () => LazyModule }]); - router.navigateByUrl(`/lazy/child`); - tick(500); - fixture.detectChanges(); - const text = (fixture.nativeElement as HTMLElement).textContent; - expect(text).toContain('length-2'); - })); - }); - describe('[executeOtherInterceptors]', () => { - beforeEach(() => { - genModule(DATA, { delay: 1, executeOtherInterceptors: true }, [], true, [ - { provide: HTTP_INTERCEPTORS, useClass: OtherInterceptor, multi: true } - ]); - }); - - it('should working', done => { - otherRes = new HttpResponse({ body: { a: 1 } }); - http.get('/users').subscribe((res: any) => { - expect(res).not.toBeNull(); - expect(res.a).toBe(1); - done(); - }); - }); - }); }); @Component({ diff --git a/packages/mock/src/mock.interceptor.ts b/packages/mock/src/mock.interceptor.ts index f58fea69b5..0fe22fe182 100644 --- a/packages/mock/src/mock.interceptor.ts +++ b/packages/mock/src/mock.interceptor.ts @@ -1,17 +1,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { - HttpBackend, - HttpErrorResponse, - HttpEvent, - HttpHandler, - HttpInterceptor, - HttpRequest, - HttpResponse, - HttpResponseBase, - HTTP_INTERCEPTORS -} from '@angular/common/http'; -import { Injectable, Injector } from '@angular/core'; -import { Observable, of, throwError, delay } from 'rxjs'; +import { HttpErrorResponse, HttpResponse, HttpResponseBase, HttpInterceptorFn } from '@angular/common/http'; +import { inject } from '@angular/core'; +import { Observable, of, throwError, delay, isObservable, from, map, switchMap } from 'rxjs'; import { deepCopy } from '@delon/util/other'; @@ -19,109 +9,87 @@ import { MockRequest } from './interface'; import { MockService } from './mock.service'; import { MockStatusError } from './status.error'; -class HttpMockInterceptorHandler implements HttpHandler { - constructor( - private next: HttpHandler, - private interceptor: HttpInterceptor - ) {} - - handle(req: HttpRequest): Observable> { - return this.interceptor.intercept(req, this.next); +export const mockInterceptor: HttpInterceptorFn = (req, next) => { + const src = inject(MockService); + const config = src.config; + const rule = src.getRule(req.method, req.url.split('?')[0]); + if (!rule && !config.force) { + return next(req); } -} - -@Injectable() -export class MockInterceptor implements HttpInterceptor { - constructor(private injector: Injector) {} - intercept(req: HttpRequest, next: HttpHandler): Observable> { - const src = this.injector.get(MockService); - const config = src.config; - const rule = src.getRule(req.method, req.url.split('?')[0]); - if (!rule && !config.force) { - return next.handle(req); - } - - let res: any; - switch (typeof rule!.callback) { - case 'function': - const mockRequest: MockRequest = { - original: req, - body: req.body, - queryString: {}, - headers: {}, - params: rule!.params - }; - const urlParams = req.url.split('?'); - if (urlParams.length > 1) { - urlParams[1].split('&').forEach(item => { - const itemArr = item.split('='); - const key = itemArr[0]; - const value = itemArr[1]; - // is array - if (Object.keys(mockRequest.queryString).includes(key)) { - if (!Array.isArray(mockRequest.queryString[key])) { - mockRequest.queryString[key] = [mockRequest.queryString[key]]; - } - mockRequest.queryString[key].push(value); - } else { - mockRequest.queryString[key] = value; + let res$: Observable; + switch (typeof rule!.callback) { + case 'function': + const mockRequest: MockRequest = { + original: req, + body: req.body, + queryString: {}, + headers: {}, + params: rule!.params + }; + const urlParams = req.url.split('?'); + if (urlParams.length > 1) { + urlParams[1].split('&').forEach(item => { + const itemArr = item.split('='); + const key = itemArr[0]; + const value = itemArr[1]; + // is array + if (Object.keys(mockRequest.queryString).includes(key)) { + if (!Array.isArray(mockRequest.queryString[key])) { + mockRequest.queryString[key] = [mockRequest.queryString[key]]; } - }); - } - req.params.keys().forEach(key => (mockRequest.queryString[key] = req.params.get(key))); - req.headers.keys().forEach(key => (mockRequest.headers[key] = req.headers.get(key))); + mockRequest.queryString[key].push(value); + } else { + mockRequest.queryString[key] = value; + } + }); + } + req.params.keys().forEach(key => (mockRequest.queryString[key] = req.params.get(key))); + req.headers.keys().forEach(key => (mockRequest.headers[key] = req.headers.get(key))); - try { - res = rule!.callback.call(this, mockRequest); - } catch (e: any) { - res = new HttpErrorResponse({ + try { + const fnRes = rule!.callback.call(this, mockRequest); + res$ = isObservable(fnRes) ? fnRes : from(Promise.resolve(fnRes)); + } catch (e: any) { + res$ = of( + new HttpErrorResponse({ url: req.url, headers: req.headers, status: e instanceof MockStatusError ? e.status : 400, statusText: e.statusText || 'Unknown Error', error: e.error - }); - } - break; - default: - res = rule!.callback; - break; - } - - if (!(res instanceof HttpResponseBase)) { - res = new HttpResponse({ - status: 200, - url: req.url, - body: res - }); - } - - if (res.body) { - res.body = deepCopy(res.body); - } - - if (config.log) { - console.log(`%c👽${req.method}->${req.urlWithParams}->request`, 'background:#000;color:#bada55', req); - console.log(`%c👽${req.method}->${req.urlWithParams}->response`, 'background:#000;color:#bada55', res); - } - - const res$ = res instanceof HttpErrorResponse ? throwError(() => res) : of(res); - - if (config.executeOtherInterceptors) { - const interceptors = this.injector.get(HTTP_INTERCEPTORS, []); - const lastInterceptors = interceptors.slice(interceptors.indexOf(this) + 1); - if (lastInterceptors.length > 0) { - const chain = lastInterceptors.reduceRight( - (_next, _interceptor) => new HttpMockInterceptorHandler(_next, _interceptor), - { - handle: () => res$ - } as HttpBackend + }) ); - return chain.handle(req).pipe(delay(config.delay!)); } - } - - return res$.pipe(delay(config.delay!)); + break; + default: + res$ = of(rule!.callback); + break; } -} + + res$ = res$.pipe( + map(res => + res instanceof HttpResponseBase + ? res + : new HttpResponse({ + status: 200, + url: req.url, + body: deepCopy(res) + }) + ), + map((res: HttpResponseBase) => { + const anyRes: any = res; + if (anyRes.body) { + anyRes.body = deepCopy(anyRes.body); + } + if (config.log) { + console.log(`%c👽${req.method}->${req.urlWithParams}->request`, 'background:#000;color:#bada55', req); + console.log(`%c👽${req.method}->${req.urlWithParams}->response`, 'background:#000;color:#bada55', res); + } + return res; + }), + switchMap((res: HttpResponseBase) => (res instanceof HttpErrorResponse ? throwError(() => res) : of(res))) + ); + + return res$.pipe(delay(config.delay!)); +}; diff --git a/packages/mock/src/mock.module.ts b/packages/mock/src/mock.module.ts deleted file mode 100644 index 8d53b84976..0000000000 --- a/packages/mock/src/mock.module.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { HTTP_INTERCEPTORS } from '@angular/common/http'; -import { ModuleWithProviders, NgModule } from '@angular/core'; - -import { MockOptions } from './interface'; -import { MockInterceptor } from './mock.interceptor'; - -@NgModule({}) -export class DelonMockModule { - static forRoot(options?: MockOptions): ModuleWithProviders { - return { - ngModule: DelonMockModule, - providers: [ - { provide: MockOptions, useValue: options }, - { provide: HTTP_INTERCEPTORS, useClass: MockInterceptor, multi: true } - ] - }; - } - - static forChild(): ModuleWithProviders { - return { - ngModule: DelonMockModule, - providers: [{ provide: HTTP_INTERCEPTORS, useClass: MockInterceptor, multi: true }] - }; - } -} diff --git a/packages/mock/src/mock.service.spec.ts b/packages/mock/src/mock.service.spec.ts index 915f09df86..1befc1f43a 100644 --- a/packages/mock/src/mock.service.spec.ts +++ b/packages/mock/src/mock.service.spec.ts @@ -3,11 +3,11 @@ import { TestBed } from '@angular/core/testing'; import * as Mock from 'mockjs'; -import { AlainMockConfig, ALAIN_CONFIG } from '@delon/util/config'; +import { AlainMockConfig, provideAlainConfig } from '@delon/util/config'; import { MockOptions, MockRequest, MockRule } from './interface'; import { MockService } from './mock.service'; -import { DelonMockModule } from '../index'; +import { provideMockConfig } from './provide'; const DATA = { USERS: { @@ -31,8 +31,7 @@ describe('mock: service', () => { function genModule(options: AlainMockConfig, mockOptions?: MockOptions): void { TestBed.configureTestingModule({ - imports: [DelonMockModule.forRoot(mockOptions)], - providers: [{ provide: ALAIN_CONFIG, useValue: { mock: options } }] + providers: [provideAlainConfig({ mock: options }), provideMockConfig(mockOptions)] }); srv = TestBed.inject(MockService); spyOn(console, 'log'); diff --git a/packages/mock/src/mock.service.ts b/packages/mock/src/mock.service.ts index 7f4e1402c9..2188653a6d 100644 --- a/packages/mock/src/mock.service.ts +++ b/packages/mock/src/mock.service.ts @@ -1,17 +1,18 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { Injectable, OnDestroy } from '@angular/core'; +import { Inject, Injectable, OnDestroy, Optional } from '@angular/core'; import { AlainConfigService, AlainMockConfig } from '@delon/util/config'; import { MockCachedRule, MockOptions, MockRule } from './interface'; import { MOCK_DEFULAT_CONFIG } from './mock.config'; +import { DELON_MOCK_CONFIG } from './provide'; @Injectable({ providedIn: 'root' }) export class MockService implements OnDestroy { private cached: MockCachedRule[] = []; readonly config: AlainMockConfig; - constructor(cogSrv: AlainConfigService, options: MockOptions) { + constructor(cogSrv: AlainConfigService, @Optional() @Inject(DELON_MOCK_CONFIG) options?: MockOptions) { this.config = cogSrv.merge('mock', MOCK_DEFULAT_CONFIG)!; this.setData(options?.data); } diff --git a/packages/mock/src/provide.ts b/packages/mock/src/provide.ts new file mode 100644 index 0000000000..c6cbab6408 --- /dev/null +++ b/packages/mock/src/provide.ts @@ -0,0 +1,9 @@ +import { EnvironmentProviders, InjectionToken, makeEnvironmentProviders } from '@angular/core'; + +import { MockOptions } from './interface'; + +export const DELON_MOCK_CONFIG = new InjectionToken('alain-mock-config'); + +export function provideMockConfig(config?: MockOptions): EnvironmentProviders { + return makeEnvironmentProviders([{ provide: DELON_MOCK_CONFIG, useValue: config }]); +} diff --git a/packages/mock/src/utils.ts b/packages/mock/src/utils.ts new file mode 100644 index 0000000000..c5a936226a --- /dev/null +++ b/packages/mock/src/utils.ts @@ -0,0 +1,13 @@ +/** + * Used to simulate delays + */ +export function delay(ms: number): Promise { + return new Promise(resolve => setTimeout(resolve, ms)); +} + +/** + * Return a random number + */ +export function r(min = 1, max = 100): number { + return Math.floor(Math.random() * (max - min + 1) + min); +} diff --git a/packages/testing/src/delay.ts b/packages/testing/src/delay.ts new file mode 100644 index 0000000000..11c61e00f7 --- /dev/null +++ b/packages/testing/src/delay.ts @@ -0,0 +1,9 @@ +/** + * Will complete after a time delay + * + * @example + * it('Test', async () => { + * await delay(100); + * }); + */ +export const delay = (ms?: number): Promise => new Promise(res => setTimeout(res, ms ?? 0)); diff --git a/packages/theme/docs/demo/table-rep.md b/packages/theme/docs/demo/table-rep.md index 592e9d86eb..4c75418d76 100644 --- a/packages/theme/docs/demo/table-rep.md +++ b/packages/theme/docs/demo/table-rep.md @@ -16,6 +16,8 @@ Mobile screen view effect. ```ts import { Component } from '@angular/core'; +import { NzTableModule } from 'ng-zorro-antd/table'; + @Component({ selector: 'app-demo', template: ` @@ -28,7 +30,8 @@ import { Component } from '@angular/core'; - + @for (data of basicTable.data; track $index) { + Name {{ data.name }} @@ -42,9 +45,12 @@ import { Component } from '@angular/core'; {{ data.address }} + } `, + standalone: true, + imports: [NzTableModule] }) export class DemoComponent { dataSet = [ @@ -52,20 +58,20 @@ export class DemoComponent { key: '1', name: 'John Brown', age: 32, - address: 'New York No. 1 Lake Park', + address: 'New York No. 1 Lake Park' }, { key: '2', name: 'Jim Green', age: 42, - address: 'London No. 1 Lake Park', + address: 'London No. 1 Lake Park' }, { key: '3', name: 'Joe Black', age: 32, - address: 'Sidney No. 1 Lake Park', - }, + address: 'Sidney No. 1 Lake Park' + } ]; } ``` diff --git a/packages/theme/layout-default/index.en-US.md b/packages/theme/layout-default/index.en-US.md index b3043035b4..2fe48b7439 100644 --- a/packages/theme/layout-default/index.en-US.md +++ b/packages/theme/layout-default/index.en-US.md @@ -69,7 +69,9 @@ import { environment } from '@env/environment';
- + @if (showSettingDrawer) { + + } `, }) diff --git a/packages/theme/layout-default/index.zh-CN.md b/packages/theme/layout-default/index.zh-CN.md index 32f11bf487..97eebc49df 100644 --- a/packages/theme/layout-default/index.zh-CN.md +++ b/packages/theme/layout-default/index.zh-CN.md @@ -69,7 +69,9 @@ import { environment } from '@env/environment'; - + @if (showSettingDrawer) { + + } `, }) diff --git a/packages/theme/layout-default/layout-header.component.ts b/packages/theme/layout-default/layout-header.component.ts index e6ddd1450d..7e398730a0 100644 --- a/packages/theme/layout-default/layout-header.component.ts +++ b/packages/theme/layout-default/layout-header.component.ts @@ -28,30 +28,38 @@ interface LayoutDefaultHeaderItem { selector: 'layout-default-header', template: ` -
  • - -
  • + @for (i of ls; track $index) { +
  • + +
  • + }
      -
    • -
      - -
      -
    • + @if (!opt.hideAside && opt.showHeaderCollapse) { +
    • +
      + +
      +
    • + }
    -
    - -
    + @if (middle.length > 0) { +
    + +
    + }
    diff --git a/packages/theme/layout-default/layout-nav.component.html b/packages/theme/layout-default/layout-nav.component.html index 76342cac65..2622f909e8 100644 --- a/packages/theme/layout-default/layout-nav.component.html +++ b/packages/theme/layout-default/layout-nav.component.html @@ -1,73 +1,86 @@ - - - - - - - + @if (i) { + @switch (i.type) { + @case ('icon') { + + } + @case ('iconfont') { + + } + @case ('img') { + + } + @case ('svg') { + + } + @default { + + } + } + } - - - + + + + } + + @if (i.badge) { + + } + @if (i.children.length > 0) { + + } + + } + } diff --git a/packages/theme/layout-default/layout-nav.component.spec.ts b/packages/theme/layout-default/layout-nav.component.spec.ts index 753d61095d..3fb5e88dd6 100644 --- a/packages/theme/layout-default/layout-nav.component.spec.ts +++ b/packages/theme/layout-default/layout-nav.component.spec.ts @@ -590,8 +590,8 @@ describe('theme: layout-default-nav', () => { const el = body ? document.querySelector(cls) : dl.query(By.css(cls)) - ? dl.query(By.css(cls)).nativeElement - : null; + ? dl.query(By.css(cls)).nativeElement + : null; return el ? (el as T) : null; } checkText(cls: string, value: NzSafeAny): void { diff --git a/packages/theme/layout-default/layout.component.ts b/packages/theme/layout-default/layout.component.ts index 28faa79d54..7ce18e789c 100644 --- a/packages/theme/layout-default/layout.component.ts +++ b/packages/theme/layout-default/layout.component.ts @@ -35,24 +35,36 @@ import { LayoutDefaultOptions } from './types'; selector: 'layout-default', exportAs: 'layoutDefault', template: ` -
    - -
    -
    -
    - - - -
    - diff --git a/src/app/shared/components/footer/footer.component.ts b/src/app/shared/components/footer/footer.component.ts index d90424fac4..75419b24e7 100644 --- a/src/app/shared/components/footer/footer.component.ts +++ b/src/app/shared/components/footer/footer.component.ts @@ -1,12 +1,16 @@ import { Platform } from '@angular/cdk/platform'; +import { NgStyle } from '@angular/common'; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Inject, Input, NgZone, OnInit } from '@angular/core'; +import { RouterLink } from '@angular/router'; import { LoadingService } from '@delon/abc/loading'; -import { ALAIN_I18N_TOKEN } from '@delon/theme'; +import { ALAIN_I18N_TOKEN, I18nPipe } from '@delon/theme'; import { copy } from '@delon/util/browser'; import { BooleanInput, InputBoolean } from '@delon/util/decorator'; import { LazyService } from '@delon/util/other'; +import { NzColor, NzColorPickerModule } from 'ng-zorro-antd/color-picker'; import type { NzSafeAny } from 'ng-zorro-antd/core/types'; +import { NzGridModule } from 'ng-zorro-antd/grid'; import { NzIconService } from 'ng-zorro-antd/icon'; import { NzMessageService } from 'ng-zorro-antd/message'; @@ -20,7 +24,9 @@ import { I18NService } from '@core'; '[class.footer__dark]': 'true', '[class.footer__small]': 'small' }, - changeDetection: ChangeDetectionStrategy.OnPush + changeDetection: ChangeDetectionStrategy.OnPush, + standalone: true, + imports: [NzGridModule, NzColorPickerModule, NgStyle, I18nPipe, RouterLink] }) export class FooterComponent implements OnInit { static ngAcceptInputType_small: BooleanInput; @@ -61,17 +67,18 @@ export class FooterComponent implements OnInit { document.getElementsByTagName('head')[0].appendChild(node); } - changeColor(res: NzSafeAny): void { + changeColor(res: { color: NzColor; format: string }): void { const changeColor = (): void => { this.ngZone.runOutsideAngular(() => { + const hex = res.color.toHexString(); (window as NzSafeAny).less .modifyVars({ - '@primary-color': res.color.hex + '@primary-color': hex }) .then(() => { window.scrollTo(0, 0); this.ngZone.run(() => { - this.color = res.color.hex; + this.color = hex; this.iconSrv.twoToneColor.primaryColor = this.color; this.msg.success(this.i18n.fanyi('app.footer.primary-color-changed')); this.cdr.detectChanges(); @@ -87,12 +94,9 @@ export class FooterComponent implements OnInit { if (this.lessLoaded) { changeColor(); } else { - (window as NzSafeAny).less = { - async: true, - javascriptEnabled: true - }; this.lazy.loadScript(lessUrl).then(() => { this.lessLoaded = true; + (window as NzSafeAny).less.options.javascriptEnabled = true; changeColor(); }); } diff --git a/src/app/shared/components/icon/icon.component.ts b/src/app/shared/components/icon/icon.component.ts index 92bbc8ca25..079408fd85 100644 --- a/src/app/shared/components/icon/icon.component.ts +++ b/src/app/shared/components/icon/icon.component.ts @@ -1,6 +1,7 @@ import { Component, Input } from '@angular/core'; import { InputBoolean } from '@delon/util/decorator'; +import { NzIconModule } from 'ng-zorro-antd/icon'; @Component({ selector: 'nz-icon', @@ -13,7 +14,9 @@ import { InputBoolean } from '@delon/util/decorator'; [nzTwotoneColor]="twoToneColor" [nzIconfont]="iconfont" > - ` + `, + standalone: true, + imports: [NzIconModule] }) export class IconComponent { @Input() diff --git a/src/app/shared/components/index.ts b/src/app/shared/components/index.ts new file mode 100644 index 0000000000..aedbbe1b67 --- /dev/null +++ b/src/app/shared/components/index.ts @@ -0,0 +1,10 @@ +export * from './footer/footer.component'; +export * from './dialog/modal.component'; +export * from './dialog/drawer.component'; +export * from './dialog/sf.component'; +export * from './match-router/match-router.directive'; +export * from './route-transfer/route-transfer.directive'; +export * from './code-box/code-box.component'; +export * from './edit-button/edit-button.component'; +export * from './docs/docs.component'; +export * from './content/content.component'; diff --git a/src/app/shared/components/main-menu/main-menu.component.html b/src/app/shared/components/main-menu/main-menu.component.html index 040de18964..608e06f0de 100644 --- a/src/app/shared/components/main-menu/main-menu.component.html +++ b/src/app/shared/components/main-menu/main-menu.component.html @@ -1,23 +1,28 @@
      -
    • -
        -
      • - - {{ item.title }} - {{ item.subtitle }} - LIB - -
      • -
      -
    • + @for (group of menus; track $index) { +
    • +
        + @for (item of group.list; track $index) { +
      • + + {{ item.title }} + {{ item.subtitle }} + @if (item.lib) { + LIB + } + +
      • + } +
      +
    • + }
    diff --git a/src/app/shared/components/main-menu/main-menu.component.ts b/src/app/shared/components/main-menu/main-menu.component.ts index b2bf6e977f..b8977a8896 100644 --- a/src/app/shared/components/main-menu/main-menu.component.ts +++ b/src/app/shared/components/main-menu/main-menu.component.ts @@ -10,16 +10,23 @@ import { inject } from '@angular/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { RouterLink, RouterLinkActive } from '@angular/router'; import { ALAIN_I18N_TOKEN } from '@delon/theme'; +import { NzBadgeModule } from 'ng-zorro-antd/badge'; import type { NzSafeAny } from 'ng-zorro-antd/core/types'; +import { NzMenuModule } from 'ng-zorro-antd/menu'; +import { NzTagModule } from 'ng-zorro-antd/tag'; +import { NzToolTipModule } from 'ng-zorro-antd/tooltip'; import { I18NService, MetaService } from '@core'; @Component({ selector: 'main-menu, [main-menu]', templateUrl: './main-menu.component.html', - changeDetection: ChangeDetectionStrategy.OnPush + changeDetection: ChangeDetectionStrategy.OnPush, + standalone: true, + imports: [RouterLink, RouterLinkActive, NzToolTipModule, NzBadgeModule, NzTagModule, NzMenuModule] }) export class MainMenuComponent implements OnInit { private destroy$ = inject(DestroyRef); diff --git a/src/app/shared/components/match-router/match-router.directive.ts b/src/app/shared/components/match-router/match-router.directive.ts index a621fecbdf..705effe270 100644 --- a/src/app/shared/components/match-router/match-router.directive.ts +++ b/src/app/shared/components/match-router/match-router.directive.ts @@ -8,7 +8,8 @@ export interface MatchRouterOptions { } @Directive({ - selector: '[matchRouter]' + selector: '[matchRouter]', + standalone: true }) export class MatchRouterDirective implements AfterViewInit, OnDestroy { private _options!: MatchRouterOptions; diff --git a/src/app/shared/components/route-transfer/route-transfer.directive.ts b/src/app/shared/components/route-transfer/route-transfer.directive.ts index a89cf8b264..6daf702e63 100644 --- a/src/app/shared/components/route-transfer/route-transfer.directive.ts +++ b/src/app/shared/components/route-transfer/route-transfer.directive.ts @@ -8,7 +8,8 @@ import { NzMessageService } from 'ng-zorro-antd/message'; import { I18NService } from '@core'; @Directive({ - selector: '[routeTransfer]' + selector: '[routeTransfer]', + standalone: true }) export class RouteTransferDirective { constructor( diff --git a/src/app/shared/index.ts b/src/app/shared/index.ts index 76f78e8877..76630ed5ed 100644 --- a/src/app/shared/index.ts +++ b/src/app/shared/index.ts @@ -1,3 +1,2 @@ -export * from './components/dialog/modal.component'; -export * from './components/dialog/drawer.component'; -export * from './components/dialog/sf.component'; +export * from './components/index'; +export * from './shared.module'; diff --git a/src/app/shared/json-schema/index.md b/src/app/shared/json-schema/index.md deleted file mode 100644 index 78a9c12c16..0000000000 --- a/src/app/shared/json-schema/index.md +++ /dev/null @@ -1 +0,0 @@ -# 建议统一在 `widgets` 目录下自定义小部件 diff --git a/src/app/shared/json-schema/json-schema.module.ts b/src/app/shared/json-schema/json-schema.module.ts deleted file mode 100644 index e4c106a0a4..0000000000 --- a/src/app/shared/json-schema/json-schema.module.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { NgModule } from '@angular/core'; - -import { DelonFormModule, WidgetRegistry } from '@delon/form'; - -import { TinymceWidget } from '../../../../packages/form/widgets-third/tinymce/tinymce.widget'; -import { SharedModule } from '../shared.module'; - -export const SCHEMA_THIRDS_COMPONENTS = [TinymceWidget]; - -@NgModule({ - declarations: SCHEMA_THIRDS_COMPONENTS, - imports: [SharedModule, DelonFormModule.forRoot()], - exports: [...SCHEMA_THIRDS_COMPONENTS] -}) -export class JsonSchemaModule { - constructor(widgetRegistry: WidgetRegistry) { - widgetRegistry.register(TinymceWidget.KEY, TinymceWidget); - } -} diff --git a/src/app/shared/shared-zorro.module.ts b/src/app/shared/shared-zorro.module.ts index 8cfe5bcb19..75e5f12e1a 100644 --- a/src/app/shared/shared-zorro.module.ts +++ b/src/app/shared/shared-zorro.module.ts @@ -26,6 +26,7 @@ import { NzMessageModule } from 'ng-zorro-antd/message'; import { NzModalModule } from 'ng-zorro-antd/modal'; import { NzPaginationModule } from 'ng-zorro-antd/pagination'; import { NzPopoverModule } from 'ng-zorro-antd/popover'; +import { NzQRCodeModule } from 'ng-zorro-antd/qr-code'; import { NzRadioModule } from 'ng-zorro-antd/radio'; import { NzResizableModule } from 'ng-zorro-antd/resizable'; import { NzSelectModule } from 'ng-zorro-antd/select'; @@ -77,5 +78,6 @@ export const SHARED_ZORRO_MODULES = [ NzUploadModule, NzPaginationModule, NzEmptyModule, - NzHighlightModule + NzHighlightModule, + NzQRCodeModule ]; diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index 5b37cd8a18..954273d1aa 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -4,7 +4,6 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { RouterModule } from '@angular/router'; import { GithubButtonModule } from 'ng-github-button'; -import { ColorSketchModule } from 'ngx-color/sketch'; import { HighlightJsModule } from 'ngx-highlight-js'; import { NgxTinymceModule } from 'ngx-tinymce'; @@ -42,7 +41,7 @@ const COMPONENTS = [ ...DIALOG_COMPONENTS ]; -const THIRDS = [HighlightJsModule, GithubButtonModule, NgxTinymceModule, ColorSketchModule]; +const THIRDS = [HighlightJsModule, GithubButtonModule, NgxTinymceModule]; @NgModule({ imports: [ @@ -50,15 +49,16 @@ const THIRDS = [HighlightJsModule, GithubButtonModule, NgxTinymceModule, ColorSk FormsModule, RouterModule, ReactiveFormsModule, - AlainThemeModule.forChild(), + AlainThemeModule, DelonACLModule, DelonFormModule, DelonCacheModule, ...SHARED_DELON_MODULES, ...SHARED_ZORRO_MODULES, - ...THIRDS + ...THIRDS, + ...COMPONENTS ], - declarations: COMPONENTS, + // declarations: COMPONENTS, exports: [ CommonModule, FormsModule, diff --git a/src/app/shared/st-widget/img.widget.ts b/src/app/shared/st-widget/img.ts similarity index 56% rename from src/app/shared/st-widget/img.widget.ts rename to src/app/shared/st-widget/img.ts index 2f37918a33..b1bc0ccdd6 100644 --- a/src/app/shared/st-widget/img.widget.ts +++ b/src/app/shared/st-widget/img.ts @@ -1,6 +1,8 @@ -import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { ChangeDetectionStrategy, Component, OnChanges, SimpleChanges } from '@angular/core'; +import { NzImageModule } from 'ng-zorro-antd/image'; import { NzMessageService } from 'ng-zorro-antd/message'; +import { NzToolTipModule } from 'ng-zorro-antd/tooltip'; @Component({ selector: 'st-widget-img', @@ -18,15 +20,21 @@ import { NzMessageService } from 'ng-zorro-antd/message'; host: { '(click)': 'show()' }, - changeDetection: ChangeDetectionStrategy.OnPush + changeDetection: ChangeDetectionStrategy.OnPush, + standalone: true, + imports: [NzToolTipModule, NzImageModule] }) -export class STImgWidget { +export class STImgWidget implements OnChanges { static readonly KEY = 'img'; img!: string; constructor(private msg: NzMessageService) {} + ngOnChanges(changes: SimpleChanges): void { + console.log(changes); + } + show(): void { this.msg.info(`正在打开大图${this.img}……`); } diff --git a/src/app/shared/st-widget/index.ts b/src/app/shared/st-widget/index.ts new file mode 100644 index 0000000000..faa01fd417 --- /dev/null +++ b/src/app/shared/st-widget/index.ts @@ -0,0 +1,3 @@ +import { STImgWidget } from './img'; + +export const ST_WIDGETS = [{ KEY: STImgWidget.KEY, type: STImgWidget }]; diff --git a/src/app/shared/st-widget/st-widget.module.ts b/src/app/shared/st-widget/st-widget.module.ts deleted file mode 100644 index 71f288eb7d..0000000000 --- a/src/app/shared/st-widget/st-widget.module.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { NgModule } from '@angular/core'; - -import { STWidgetRegistry } from '@delon/abc/st'; - -import { STImgWidget } from './img.widget'; -import { SharedModule } from '../shared.module'; - -export const STWIDGET_COMPONENTS = [STImgWidget]; - -@NgModule({ - declarations: STWIDGET_COMPONENTS, - imports: [SharedModule], - exports: [...STWIDGET_COMPONENTS] -}) -export class STWidgetModule { - constructor(widgetRegistry: STWidgetRegistry) { - widgetRegistry.register(STImgWidget.KEY, STImgWidget); - } -} diff --git a/src/assets/screenshot/data.webp b/src/assets/screenshot/data.webp new file mode 100644 index 0000000000..5f8992d5a2 Binary files /dev/null and b/src/assets/screenshot/data.webp differ diff --git a/src/dev/demo.component.ts b/src/dev/demo.component.ts index ea36e6b5a5..2d89ea8801 100644 --- a/src/dev/demo.component.ts +++ b/src/dev/demo.component.ts @@ -1,11 +1,42 @@ import { Component } from '@angular/core'; +import { CookieOptions, CookieService } from '@delon/util/browser'; +import { NzButtonModule } from 'ng-zorro-antd/button'; +import { NzMessageService } from 'ng-zorro-antd/message'; + @Component({ selector: 'app-demo', - template: `` + template: ` +

    Result: {{ value || 'NULL' }}

    + + + + + `, + standalone: true, + imports: [NzButtonModule] }) export class DemoComponent { - _n(): void { - console.log('n'); + key = 'test-key'; + value?: string; + + constructor( + private cookieSrv: CookieService, + private msg: NzMessageService + ) { + this.get(); + } + + get(): void { + this.value = this.cookieSrv.get(this.key)!; + } + + set(options?: CookieOptions): void { + this.cookieSrv.put(this.key, (+new Date()).toString(), options); + this.msg.success(`Success`); + } + + remove(): void { + this.cookieSrv.remove(this.key); } } diff --git a/src/dev/home/home.component.ts b/src/dev/home/home.component.ts index e074c5d6c4..302b4db104 100644 --- a/src/dev/home/home.component.ts +++ b/src/dev/home/home.component.ts @@ -1,6 +1,9 @@ -import { Component } from '@angular/core'; +import { JsonPipe } from '@angular/common'; +import { Component, inject } from '@angular/core'; +import { PageHeaderModule } from '@delon/abc/page-header'; import { LayoutDefaultService } from '@delon/theme/layout-default'; +import { NzButtonModule } from 'ng-zorro-antd/button'; @Component({ selector: 'dev-home', @@ -15,13 +18,15 @@ import { LayoutDefaultService } from '@delon/theme/layout-default'; - +
    - ` + `, + standalone: true, + imports: [JsonPipe, PageHeaderModule, NzButtonModule] }) export class DevHomeComponent { - constructor(public srv: LayoutDefaultService) {} + srv = inject(LayoutDefaultService); } diff --git a/src/dev/layout.component.ts b/src/dev/layout.component.ts index 9b9d1a522c..2a2db9d190 100644 --- a/src/dev/layout.component.ts +++ b/src/dev/layout.component.ts @@ -1,5 +1,6 @@ +import { UpperCasePipe } from '@angular/common'; import { Component, Inject, OnInit } from '@angular/core'; -import { Router } from '@angular/router'; +import { Router, RouterOutlet } from '@angular/router'; import { AppstoreOutline, @@ -22,11 +23,15 @@ import { // #region icons -import { ReuseCustomContextMenu } from '@delon/abc/reuse-tab'; +import { ReuseCustomContextMenu, ReuseTabComponent } from '@delon/abc/reuse-tab'; import { ALAIN_I18N_TOKEN, Menu, MenuService, RTLService, SettingsService, User } from '@delon/theme'; -import { LayoutDefaultOptions } from '@delon/theme/layout-default'; +import { LayoutDefaultModule, LayoutDefaultOptions } from '@delon/theme/layout-default'; +import { SettingDrawerModule } from '@delon/theme/setting-drawer'; import { deepCopy } from '@delon/util/other'; -import { NzIconService } from 'ng-zorro-antd/icon'; +import { NzAvatarModule } from 'ng-zorro-antd/avatar'; +import { NzDropDownModule } from 'ng-zorro-antd/dropdown'; +import { NzIconModule, NzIconService } from 'ng-zorro-antd/icon'; +import { NzMenuModule } from 'ng-zorro-antd/menu'; import { NzMessageService } from 'ng-zorro-antd/message'; import { I18NService, LangType } from '@core'; @@ -62,14 +67,11 @@ const ICONS = [ - - {{ m.label }} - + @for (m of topMenus; track $index) { + + {{ m.label }} + + } {{ rtl.nextDir | uppercase }} @@ -100,7 +102,18 @@ const ICONS = [ host: { '[class.alain-default]': 'true' }, - preserveWhitespaces: false + standalone: true, + imports: [ + UpperCasePipe, + LayoutDefaultModule, + NzIconModule, + NzDropDownModule, + NzAvatarModule, + NzMenuModule, + ReuseTabComponent, + RouterOutlet, + SettingDrawerModule + ] }) export class DevLayoutComponent implements OnInit { options: LayoutDefaultOptions = { diff --git a/src/dev/lazy/layout.component.ts b/src/dev/lazy/layout.component.ts index 7fca67e7c8..d6402a89f1 100644 --- a/src/dev/lazy/layout.component.ts +++ b/src/dev/lazy/layout.component.ts @@ -1,7 +1,11 @@ +import { JsonPipe } from '@angular/common'; import { Component } from '@angular/core'; +import { RouterOutlet } from '@angular/router'; import { format } from 'date-fns'; +import { PageHeaderModule } from '@delon/abc/page-header'; + @Component({ selector: 'dev-lazy-layout', template: ` @@ -10,7 +14,9 @@ import { format } from 'date-fns';
    - ` + `, + standalone: true, + imports: [RouterOutlet, PageHeaderModule, JsonPipe] }) export class DevLazyLayoutComponent { now = format(new Date(), 'yyyy-MM-dd HH:mm:ss'); diff --git a/src/dev/lazy/lazy.module.ts b/src/dev/lazy/router.ts similarity index 60% rename from src/dev/lazy/lazy.module.ts rename to src/dev/lazy/router.ts index 6a8ec31492..fdb436e8e1 100644 --- a/src/dev/lazy/lazy.module.ts +++ b/src/dev/lazy/router.ts @@ -1,13 +1,9 @@ -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; +import { Routes } from '@angular/router'; import { DevLazyLayoutComponent } from './layout.component'; import { DevLazyPageComponent } from './test.component'; -import { SharedModule } from '../../app/shared/shared.module'; -const COMPONENTS = [DevLazyLayoutComponent, DevLazyPageComponent]; - -const routes: Routes = [ +export const routes: Routes = [ { path: '', component: DevLazyLayoutComponent, @@ -22,9 +18,3 @@ const routes: Routes = [ ] } ]; - -@NgModule({ - imports: [SharedModule, RouterModule.forChild(routes)], - declarations: COMPONENTS -}) -export class DevLazyModule {} diff --git a/src/dev/lazy/test.component.ts b/src/dev/lazy/test.component.ts index 401302598b..24d04a8829 100644 --- a/src/dev/lazy/test.component.ts +++ b/src/dev/lazy/test.component.ts @@ -1,3 +1,4 @@ +import { JsonPipe } from '@angular/common'; import { Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; @@ -10,7 +11,9 @@ import { format } from 'date-fns';

    first: {{ first | json }},now: {{ now | json }}

    id: {{ id | json }}

    page: {{ route.url | json }} - ` + `, + standalone: true, + imports: [JsonPipe] }) export class DevLazyPageComponent implements OnInit { first = format(new Date(), 'yyyy-MM-dd HH:mm:ss'); @@ -20,7 +23,6 @@ export class DevLazyPageComponent implements OnInit { constructor(public route: ActivatedRoute) {} ngOnInit(): void { - console.log('ngoninit'); this.route.params.subscribe(params => (this.id = +params.id)); } diff --git a/src/dev/list/layout.component.ts b/src/dev/list/layout.component.ts index bcbf68b06f..3db9c26de5 100644 --- a/src/dev/list/layout.component.ts +++ b/src/dev/list/layout.component.ts @@ -1,8 +1,11 @@ +import { JsonPipe } from '@angular/common'; import { Component, OnDestroy, OnInit } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; +import { ActivatedRoute, RouterOutlet } from '@angular/router'; import { format } from 'date-fns'; +import { PageHeaderModule } from '@delon/abc/page-header'; + @Component({ selector: 'dev-list-layout', template: ` @@ -13,7 +16,9 @@ import { format } from 'date-fns';
    - ` + `, + standalone: true, + imports: [PageHeaderModule, JsonPipe, RouterOutlet] }) export class DevLayoutListComponent implements OnInit, OnDestroy { first = format(new Date(), 'yyyy-MM-dd HH:mm:ss'); diff --git a/src/dev/list/list-item.component.ts b/src/dev/list/list-item.component.ts index 8ab4ff4b02..4904f33932 100644 --- a/src/dev/list/list-item.component.ts +++ b/src/dev/list/list-item.component.ts @@ -1,3 +1,4 @@ +import { JsonPipe } from '@angular/common'; import { Component, OnDestroy, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; @@ -10,7 +11,9 @@ import { format } from 'date-fns';

    first: {{ first | json }},now: {{ now | json }}

    id: {{ id | json }}

    page: {{ route.url | json }} - ` + `, + standalone: true, + imports: [JsonPipe] }) export class DevListItemComponent implements OnInit, OnDestroy { first = format(new Date(), 'yyyy-MM-dd HH:mm:ss'); diff --git a/src/dev/list/list.component.ts b/src/dev/list/list.component.ts index 7e078ab402..fe2db77730 100644 --- a/src/dev/list/list.component.ts +++ b/src/dev/list/list.component.ts @@ -1,3 +1,4 @@ +import { JsonPipe } from '@angular/common'; import { Component, OnDestroy, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; @@ -10,7 +11,9 @@ import { format } from 'date-fns';

    first: {{ first | json }},now: {{ now | json }}

    id: {{ id | json }}

    page: {{ route.url | json }} - ` + `, + standalone: true, + imports: [JsonPipe] }) export class DevListComponent implements OnInit, OnDestroy { first = format(new Date(), 'yyyy-MM-dd HH:mm:ss'); diff --git a/src/dev/list/list.module.ts b/src/dev/list/list.module.ts deleted file mode 100644 index 2d709c3b63..0000000000 --- a/src/dev/list/list.module.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; - -import { DevLayoutListComponent } from './layout.component'; -import { DevListItemComponent } from './list-item.component'; -import { DevListComponent } from './list.component'; -import { SharedModule } from '../../app/shared/shared.module'; - -const COMPONENTS = [DevLayoutListComponent, DevListComponent, DevListItemComponent]; - -const routes: Routes = [ - { - path: '', - component: DevLayoutListComponent, - children: [ - { path: '', component: DevListComponent }, - { path: 'item', component: DevListItemComponent } - ] - } -]; - -@NgModule({ - imports: [SharedModule, RouterModule.forChild(routes)], - declarations: COMPONENTS -}) -export class DevListModule {} diff --git a/src/dev/list/router.ts b/src/dev/list/router.ts new file mode 100644 index 0000000000..d164c55db6 --- /dev/null +++ b/src/dev/list/router.ts @@ -0,0 +1,16 @@ +import { Routes } from '@angular/router'; + +import { DevLayoutListComponent } from './layout.component'; +import { DevListItemComponent } from './list-item.component'; +import { DevListComponent } from './list.component'; + +export const routes: Routes = [ + { + path: '', + component: DevLayoutListComponent, + children: [ + { path: '', component: DevListComponent }, + { path: 'item', component: DevListItemComponent } + ] + } +]; diff --git a/src/dev/pages/page.component.ts b/src/dev/pages/page.component.ts index 2c99bae464..d3f644ad21 100644 --- a/src/dev/pages/page.component.ts +++ b/src/dev/pages/page.component.ts @@ -1,8 +1,11 @@ +import { JsonPipe } from '@angular/common'; import { Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { format } from 'date-fns'; +import { PageHeaderModule } from '@delon/abc/page-header'; + @Component({ selector: 'dev-page', template: ` @@ -10,7 +13,9 @@ import { format } from 'date-fns';

    first: {{ first | json }},now: {{ now | json }}

    id: {{ id | json }}

    page: {{ route.url | json }} - ` + `, + standalone: true, + imports: [PageHeaderModule, JsonPipe] }) export class DevPageComponent implements OnInit { first = format(new Date(), 'yyyy-MM-dd HH:mm:ss'); @@ -20,7 +25,6 @@ export class DevPageComponent implements OnInit { constructor(public route: ActivatedRoute) {} ngOnInit(): void { - console.log('ngoninit'); this.route.params.subscribe(params => (this.id = +params.id)); } diff --git a/src/dev/dev.module.ts b/src/dev/router.ts similarity index 57% rename from src/dev/dev.module.ts rename to src/dev/router.ts index 9a274cc28c..dbf94ec4d8 100644 --- a/src/dev/dev.module.ts +++ b/src/dev/router.ts @@ -1,17 +1,11 @@ -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; - -import { LayoutDefaultModule } from '@delon/theme/layout-default'; +import { Routes } from '@angular/router'; import { DemoComponent } from './demo.component'; import { DevHomeComponent } from './home/home.component'; import { DevLayoutComponent } from './layout.component'; import { DevPageComponent } from './pages/page.component'; -import { SharedModule } from '../app/shared/shared.module'; - -const COMPONENTS = [DevLayoutComponent, DevHomeComponent, DevPageComponent, DemoComponent]; -const routes: Routes = [ +export const routes: Routes = [ { path: 'demo', component: DemoComponent @@ -31,14 +25,11 @@ const routes: Routes = [ { path: 'l8', component: DevPageComponent }, { path: 'login', component: DevPageComponent }, { path: 'view/:id', component: DevPageComponent }, - { path: 'lazy', loadChildren: () => import('./lazy/lazy.module').then(m => m.DevLazyModule) }, - { path: 'list', loadChildren: () => import('./list/list.module').then(m => m.DevListModule) } + { + path: 'lazy', + loadChildren: () => import('./lazy/router').then(m => m.routes) + }, + { path: 'list', loadChildren: () => import('./list/router').then(m => m.routes) } ] } ]; - -@NgModule({ - imports: [SharedModule, RouterModule.forChild(routes), LayoutDefaultModule], - declarations: COMPONENTS -}) -export class DevTestModule {} diff --git a/src/environments/environment.ts b/src/environments/environment.ts index 00313f1664..ffe8aed766 100644 --- a/src/environments/environment.ts +++ b/src/environments/environment.ts @@ -1,8 +1,3 @@ -// The file contents for the current environment will overwrite these during build. -// The build system defaults to the dev environment which uses `environment.ts`, but if you do -// `ng build --env=prod` then `environment.prod.ts` will be used instead. -// The list of which env maps to which file can be found in `angular-cli.json`. - export const environment = { production: false }; diff --git a/src/index.html b/src/index.html index b6b5210b96..8b7a4a0af8 100644 --- a/src/index.html +++ b/src/index.html @@ -46,13 +46,16 @@ + - -
    loading...
    -
    + +
    Loading...
    @@ -66,7 +69,6 @@

    KEEPING LOAD...

    if (!!navigator.userAgent.match(/Trident/g) || !!navigator.userAgent.match(/MSIE/g)) { document.querySelector('#_ie').style.display = 'block'; } -