├── apps ├── .gitkeep ├── server │ ├── src │ │ ├── assets │ │ │ └── .gitkeep │ │ ├── app │ │ │ ├── orm │ │ │ │ ├── prisma-service.token.ts │ │ │ │ ├── prisma.const.ts │ │ │ │ ├── prisma-adapter.ts │ │ │ │ └── prisma.module.ts │ │ │ ├── socket │ │ │ │ ├── socket-gateway.token.ts │ │ │ │ ├── cud-message.interface.ts │ │ │ │ ├── socket.service.ts │ │ │ │ ├── socket.gateway.ts │ │ │ │ └── socket.module.ts │ │ │ ├── docs │ │ │ │ ├── doc-out-dto.interface.ts │ │ │ │ ├── doc-in-dto.interface.ts │ │ │ │ └── docs.module.ts │ │ │ ├── top │ │ │ │ ├── top-dto.interface.ts │ │ │ │ └── top.module.ts │ │ │ ├── functions │ │ │ │ ├── with-created-date.interface.ts │ │ │ │ ├── to-id-type.function.ts │ │ │ │ └── id-to-string.function.ts │ │ │ ├── lists │ │ │ │ ├── lists-dto.interface.ts │ │ │ │ └── lists.module.ts │ │ │ ├── folders │ │ │ │ ├── folders-dto.interface.ts │ │ │ │ └── folders.module.ts │ │ │ ├── locations │ │ │ │ ├── location-dto.interface.ts │ │ │ │ └── locations.module.ts │ │ │ ├── sprint-folders │ │ │ │ ├── sprint-folders-dto.interface.ts │ │ │ │ └── sprint-folders.module.ts │ │ │ └── departments │ │ │ │ ├── departments.module.ts │ │ │ │ └── department-dto.interface.ts │ │ └── main.ts │ ├── tsconfig.editor.json │ ├── webpack.config.js │ ├── tsconfig.app.json │ ├── tsconfig.json │ ├── tsconfig.spec.json │ └── jest.config.ts ├── documentation │ ├── src │ │ ├── assets │ │ │ ├── .gitkeep │ │ │ └── icons │ │ │ │ └── github.svg │ │ ├── app │ │ │ ├── app.component.css │ │ │ ├── header-template.html │ │ │ ├── home │ │ │ │ ├── index.md │ │ │ │ └── ng-doc.page.ts │ │ │ ├── api │ │ │ │ ├── ng-doc.category.ts │ │ │ │ ├── changes │ │ │ │ │ ├── index.md │ │ │ │ │ └── ng-doc.page.ts │ │ │ │ ├── smart-ngrx │ │ │ │ │ └── ng-doc.api.ts │ │ │ │ └── smart-signals │ │ │ │ │ └── ng-doc.api.ts │ │ │ ├── using-smart-ng-rx │ │ │ │ ├── ng-doc.category.ts │ │ │ │ ├── intro │ │ │ │ │ └── ng-doc.page.ts │ │ │ │ ├── crud-support │ │ │ │ │ ├── updates │ │ │ │ │ │ └── ng-doc.page.ts │ │ │ │ │ ├── deleting │ │ │ │ │ │ └── ng-doc.page.ts │ │ │ │ │ ├── create │ │ │ │ │ │ └── ng-doc.page.ts │ │ │ │ │ ├── virtual-ids │ │ │ │ │ │ └── ng-doc.page.ts │ │ │ │ │ ├── retrieving │ │ │ │ │ │ └── ng-doc.page.ts │ │ │ │ │ └── ng-doc.category.ts │ │ │ │ ├── web-sockets │ │ │ │ │ └── ng-doc.page.ts │ │ │ │ ├── child-fields │ │ │ │ │ └── ng-doc.page.ts │ │ │ │ ├── mark-and-delete │ │ │ │ │ ├── intro │ │ │ │ │ │ └── ng-doc.page.ts │ │ │ │ │ ├── mark-and-delete-init │ │ │ │ │ │ └── ng-doc.page.ts │ │ │ │ │ ├── ng-doc.category.ts │ │ │ │ │ ├── entity-mark-and-delete │ │ │ │ │ │ └── ng-doc.page.ts │ │ │ │ │ └── global-mark-and-delete │ │ │ │ │ │ ├── ng-doc.page.ts │ │ │ │ │ │ └── index.md │ │ │ │ ├── error-handling │ │ │ │ │ ├── ng-doc.page.ts │ │ │ │ │ └── index.md │ │ │ │ ├── smart-selector │ │ │ │ │ └── ng-doc.page.ts │ │ │ │ ├── child-definition │ │ │ │ │ └── ng-doc.page.ts │ │ │ │ ├── effects-service │ │ │ │ │ └── ng-doc.page.ts │ │ │ │ ├── injection-token │ │ │ │ │ └── ng-doc.page.ts │ │ │ │ ├── top-level-selectors │ │ │ │ │ └── ng-doc.page.ts │ │ │ │ ├── entity-definitions │ │ │ │ │ └── ng-doc.page.ts │ │ │ │ ├── entity-registration │ │ │ │ │ └── ng-doc.page.ts │ │ │ │ ├── global-registration │ │ │ │ │ └── ng-doc.page.ts │ │ │ │ └── using-with-existing-selectors │ │ │ │ │ └── ng-doc.page.ts │ │ │ ├── ngrx-demo-walkthrough │ │ │ │ ├── ng-doc.category.ts │ │ │ │ ├── adding-rows │ │ │ │ │ └── ng-doc.page.ts │ │ │ │ ├── deleting-rows │ │ │ │ │ └── ng-doc.page.ts │ │ │ │ ├── introduction │ │ │ │ │ └── ng-doc.page.ts │ │ │ │ ├── selectors │ │ │ │ │ └── ng-doc.page.ts │ │ │ │ ├── tree-component │ │ │ │ │ └── ng-doc.page.ts │ │ │ │ ├── effects-service │ │ │ │ │ └── ng-doc.page.ts │ │ │ │ ├── retrieving-data │ │ │ │ │ └── ng-doc.page.ts │ │ │ │ ├── updating-fields │ │ │ │ │ └── ng-doc.page.ts │ │ │ │ ├── setup-client-side │ │ │ │ │ └── ng-doc.page.ts │ │ │ │ ├── database-structure │ │ │ │ │ └── ng-doc.page.ts │ │ │ │ └── processing-websocket-messages │ │ │ │ │ └── ng-doc.page.ts │ │ │ ├── using-smart-signals │ │ │ │ ├── ng-doc.category.ts │ │ │ │ ├── intro │ │ │ │ │ └── ng-doc.page.ts │ │ │ │ ├── crud-support │ │ │ │ │ ├── deleting │ │ │ │ │ │ └── ng-doc.page.ts │ │ │ │ │ ├── updates │ │ │ │ │ │ └── ng-doc.page.ts │ │ │ │ │ ├── create │ │ │ │ │ │ └── ng-doc.page.ts │ │ │ │ │ ├── virtual-ids │ │ │ │ │ │ └── ng-doc.page.ts │ │ │ │ │ ├── retrieving │ │ │ │ │ │ └── ng-doc.page.ts │ │ │ │ │ └── ng-doc.category.ts │ │ │ │ ├── web-sockets │ │ │ │ │ └── ng-doc.page.ts │ │ │ │ ├── child-fields │ │ │ │ │ └── ng-doc.page.ts │ │ │ │ ├── mark-and-delete │ │ │ │ │ ├── intro │ │ │ │ │ │ └── ng-doc.page.ts │ │ │ │ │ ├── mark-and-delete-init │ │ │ │ │ │ └── ng-doc.page.ts │ │ │ │ │ ├── entity-mark-and-delete │ │ │ │ │ │ └── ng-doc.page.ts │ │ │ │ │ ├── ng-doc.category.ts │ │ │ │ │ └── global-mark-and-delete │ │ │ │ │ │ ├── ng-doc.page.ts │ │ │ │ │ │ └── index.md │ │ │ │ ├── smart-signals │ │ │ │ │ └── ng-doc.page.ts │ │ │ │ ├── top-level-signals │ │ │ │ │ └── ng-doc.page.ts │ │ │ │ ├── effects-service │ │ │ │ │ └── ng-doc.page.ts │ │ │ │ ├── error-handling │ │ │ │ │ ├── ng-doc.page.ts │ │ │ │ │ └── index.md │ │ │ │ ├── injection-token │ │ │ │ │ └── ng-doc.page.ts │ │ │ │ ├── child-definition │ │ │ │ │ └── ng-doc.page.ts │ │ │ │ ├── entity-definitions │ │ │ │ │ └── ng-doc.page.ts │ │ │ │ ├── entity-registration │ │ │ │ │ └── ng-doc.page.ts │ │ │ │ ├── global-registration │ │ │ │ │ └── ng-doc.page.ts │ │ │ │ └── using-with-existing-signals │ │ │ │ │ └── ng-doc.page.ts │ │ │ ├── signals-demo-walkthrough │ │ │ │ ├── ng-doc.category.ts │ │ │ │ ├── adding-rows │ │ │ │ │ └── ng-doc.page.ts │ │ │ │ ├── deleting-rows │ │ │ │ │ └── ng-doc.page.ts │ │ │ │ ├── introduction │ │ │ │ │ └── ng-doc.page.ts │ │ │ │ ├── retrieving-data │ │ │ │ │ └── ng-doc.page.ts │ │ │ │ ├── smart-signals │ │ │ │ │ └── ng-doc.page.ts │ │ │ │ ├── tree-component │ │ │ │ │ └── ng-doc.page.ts │ │ │ │ ├── updating-fields │ │ │ │ │ └── ng-doc.page.ts │ │ │ │ ├── effects-service │ │ │ │ │ └── ng-doc.page.ts │ │ │ │ ├── setup-client-side │ │ │ │ │ └── ng-doc.page.ts │ │ │ │ ├── database-structure │ │ │ │ │ └── ng-doc.page.ts │ │ │ │ └── processing-websocket-messages │ │ │ │ │ └── ng-doc.page.ts │ │ │ ├── app.server.module.ts │ │ │ ├── app.component.ts │ │ │ └── app.component.html │ │ ├── main.server.ts │ │ ├── favicon.ico │ │ ├── test-setup.ts │ │ ├── styles.scss │ │ ├── main.ts │ │ └── index.html │ ├── eslint.config.js │ ├── tsconfig.editor.json │ ├── tsconfig.server.json │ ├── tsconfig.spec.json │ ├── ng-doc.config.ts │ └── tsconfig.app.json ├── demo-ngrx-classic │ ├── src │ │ ├── assets │ │ │ └── .gitkeep │ │ ├── app │ │ │ ├── routes │ │ │ │ ├── home │ │ │ │ │ ├── home.component.css │ │ │ │ │ └── home.component.ts │ │ │ │ ├── tree-standard │ │ │ │ │ ├── feature.const.ts │ │ │ │ │ ├── tree.component.css │ │ │ │ │ ├── store │ │ │ │ │ │ ├── tree-standard-state2.interface.ts │ │ │ │ │ │ ├── locations │ │ │ │ │ │ │ ├── location.actions.ts │ │ │ │ │ │ │ ├── selectors │ │ │ │ │ │ │ │ ├── select-location-entities.selectors.ts │ │ │ │ │ │ │ │ └── select-locations.selectors.ts │ │ │ │ │ │ │ └── standard-locations-definition.ts │ │ │ │ │ │ ├── current-location │ │ │ │ │ │ │ ├── current-location.actions.ts │ │ │ │ │ │ │ ├── current-location.selector.ts │ │ │ │ │ │ │ └── current-location-standard.reducer.ts │ │ │ │ │ │ ├── top │ │ │ │ │ │ │ ├── select-top-entities.selectors.ts │ │ │ │ │ │ │ └── standard-top-definition.const.ts │ │ │ │ │ │ ├── department │ │ │ │ │ │ │ └── select-departments.selector.ts │ │ │ │ │ │ ├── selectors │ │ │ │ │ │ │ └── select-tree-standard-state2.selectors.ts │ │ │ │ │ │ └── department-children │ │ │ │ │ │ │ └── department-child.selector.ts │ │ │ │ │ └── tree.component.html │ │ │ │ ├── tree-no-dirty │ │ │ │ │ ├── tree-no-dirty.component.css │ │ │ │ │ ├── store │ │ │ │ │ │ ├── tree-no-dirty-state2.interface.ts │ │ │ │ │ │ ├── department-children │ │ │ │ │ │ │ └── department-child.selector.ts │ │ │ │ │ │ ├── current-location │ │ │ │ │ │ │ ├── current-location.actions.ts │ │ │ │ │ │ │ ├── current-location.selector.ts │ │ │ │ │ │ │ └── current-location-no-dirty.reducer.ts │ │ │ │ │ │ ├── selectors │ │ │ │ │ │ │ └── select-tree-no-dirty-state2.selectors.ts │ │ │ │ │ │ ├── department │ │ │ │ │ │ │ ├── select-departments.selectors.ts │ │ │ │ │ │ │ └── select-departments-children.selectors.ts │ │ │ │ │ │ ├── top │ │ │ │ │ │ │ ├── select-top-entities.selectors.ts │ │ │ │ │ │ │ ├── select-top-locations.selectors.ts │ │ │ │ │ │ │ └── no-dirty-top-definition.const.ts │ │ │ │ │ │ └── locations │ │ │ │ │ │ │ ├── location.actions.ts │ │ │ │ │ │ │ ├── selectors │ │ │ │ │ │ │ ├── select-location-entities.selectors.ts │ │ │ │ │ │ │ ├── select-locations.selectors.ts │ │ │ │ │ │ │ └── select-locations-departments.selectors.ts │ │ │ │ │ │ │ └── no-dirty-locations-definition.ts │ │ │ │ │ └── tree-no-dirty.component.html │ │ │ │ ├── tree-no-refresh │ │ │ │ │ ├── tree-no-refresh.component.css │ │ │ │ │ ├── store │ │ │ │ │ │ ├── tree-no-refresh-state2.interface.ts │ │ │ │ │ │ ├── mark-and-delete-init.ts │ │ │ │ │ │ ├── locations │ │ │ │ │ │ │ ├── location.actions.ts │ │ │ │ │ │ │ └── selectors │ │ │ │ │ │ │ │ ├── select-location-entities.selectors.ts │ │ │ │ │ │ │ │ ├── select-locations.selectors.ts │ │ │ │ │ │ │ │ └── select-locations-departments.selectors.ts │ │ │ │ │ │ ├── department-children │ │ │ │ │ │ │ └── department-child.selector.ts │ │ │ │ │ │ ├── current-location │ │ │ │ │ │ │ ├── current-location.actions.ts │ │ │ │ │ │ │ ├── current-location.selector.ts │ │ │ │ │ │ │ └── current-location-no-refresh.reducer.ts │ │ │ │ │ │ ├── selectors │ │ │ │ │ │ │ └── select-tree-no-refresh-state2.selectors.ts │ │ │ │ │ │ ├── department │ │ │ │ │ │ │ └── select-departments.selector.ts │ │ │ │ │ │ └── top │ │ │ │ │ │ │ ├── select-top-entities.selectors.ts │ │ │ │ │ │ │ └── select-top-locations.selectors.ts │ │ │ │ │ └── tree-no-refresh.component.html │ │ │ │ └── tree-no-remove │ │ │ │ │ ├── tree-no-remove.component.css │ │ │ │ │ ├── store │ │ │ │ │ ├── tree-no-remove-state2.interface.ts │ │ │ │ │ ├── locations │ │ │ │ │ │ ├── location.actions.ts │ │ │ │ │ │ └── selectors │ │ │ │ │ │ │ ├── select-location-entities.selectors.ts │ │ │ │ │ │ │ ├── select-locations.selectors.ts │ │ │ │ │ │ │ └── select-locations-departments.selectors.ts │ │ │ │ │ ├── department-children │ │ │ │ │ │ └── select-department-children.selectors.ts │ │ │ │ │ ├── top │ │ │ │ │ │ ├── select-top-entities.selector.ts │ │ │ │ │ │ ├── select-top-locations.selectors.ts │ │ │ │ │ │ └── no-remove-top-definition.const.ts │ │ │ │ │ ├── current-location │ │ │ │ │ │ ├── current-location.actions.ts │ │ │ │ │ │ ├── select-current-location.selectors.ts │ │ │ │ │ │ └── current-location-no-remove.reducer.ts │ │ │ │ │ ├── selectors │ │ │ │ │ │ └── select-tree-no-remove-state2.selectors.ts │ │ │ │ │ └── department │ │ │ │ │ │ ├── select-department.selectors.ts │ │ │ │ │ │ └── select-departments-children.selectors.ts │ │ │ │ │ └── tree-no-remove.component.html │ │ │ ├── shared │ │ │ │ ├── form-state.interface.ts │ │ │ │ ├── docs │ │ │ │ │ ├── doc.interface.ts │ │ │ │ │ └── docs.service.ts │ │ │ │ ├── lists │ │ │ │ │ ├── list.interface.ts │ │ │ │ │ └── lists.service.ts │ │ │ │ ├── folders │ │ │ │ │ ├── folder.interface.ts │ │ │ │ │ └── folders.service.ts │ │ │ │ ├── sprint-folders │ │ │ │ │ ├── sprint-folder.interface.ts │ │ │ │ │ └── sprint-folders.service.ts │ │ │ │ ├── entities │ │ │ │ │ ├── top-entity.type.ts │ │ │ │ │ ├── location-entity.type.ts │ │ │ │ │ ├── department-entity.type.ts │ │ │ │ │ └── department-child-entity.type.ts │ │ │ │ ├── top │ │ │ │ │ ├── top-effects.service-token.ts │ │ │ │ │ └── top.interface.ts │ │ │ │ ├── locations │ │ │ │ │ ├── location-effects.service-token.ts │ │ │ │ │ └── location.interface.ts │ │ │ │ ├── department │ │ │ │ │ ├── department-effects.service-token.ts │ │ │ │ │ └── department.interface.ts │ │ │ │ ├── functions │ │ │ │ │ └── add-is-dirty.function.ts │ │ │ │ ├── department-children │ │ │ │ │ ├── filter-ids.function.ts │ │ │ │ │ ├── department-child-effects.service-token.ts │ │ │ │ │ ├── department-child.interface.ts │ │ │ │ │ └── update-id-function.ts │ │ │ │ └── components │ │ │ │ │ ├── node-editor │ │ │ │ │ ├── node-editor.component.html │ │ │ │ │ └── node-editor.component.css │ │ │ │ │ └── tree │ │ │ │ │ ├── common-source-node.interface.ts │ │ │ │ │ ├── tree-node.interface.ts │ │ │ │ │ └── tree.component.css │ │ │ └── error-handler │ │ │ │ └── error-handler.service.ts │ │ ├── favicon.ico │ │ ├── test-setup.ts │ │ ├── styles.css │ │ └── main.ts │ ├── e2e │ │ ├── tsconfig.json │ │ ├── common │ │ │ └── common.test.ts │ │ └── helpers │ │ │ ├── get-store-feature.ts │ │ │ ├── get-feature-entity.ts │ │ │ └── get-store-state.ts │ ├── proxy.conf.json │ ├── tsconfig.editor.json │ ├── tsconfig.app.json │ └── tsconfig.spec.json └── demo-ngrx-signals │ ├── src │ ├── assets │ │ └── .gitkeep │ ├── app │ │ ├── routes │ │ │ ├── home │ │ │ │ ├── home.component.css │ │ │ │ └── home.component.ts │ │ │ ├── tree-no-dirty │ │ │ │ ├── store │ │ │ │ │ ├── current-location │ │ │ │ │ │ └── test-setup.ts │ │ │ │ │ ├── tree-standard-state2.interface.ts │ │ │ │ │ ├── top │ │ │ │ │ │ ├── select-top-entities.selectors.ts │ │ │ │ │ │ └── no-dirty-signals-top-definition.const.ts │ │ │ │ │ ├── selectors │ │ │ │ │ │ └── select-tree-standard-signals-state.selectors.ts │ │ │ │ │ ├── department │ │ │ │ │ │ └── select-departments.selector.ts │ │ │ │ │ ├── locations │ │ │ │ │ │ └── selectors │ │ │ │ │ │ │ ├── select-location-entities.selectors.ts │ │ │ │ │ │ │ └── select-locations.selectors.ts │ │ │ │ │ ├── department-children │ │ │ │ │ │ └── department-child.selector.ts │ │ │ │ │ └── tree-standard-state.interface.ts │ │ │ │ ├── feature.const.ts │ │ │ │ ├── tree.component.css │ │ │ │ └── tree.component.html │ │ │ ├── tree-standard │ │ │ │ ├── store │ │ │ │ │ ├── current-location │ │ │ │ │ │ └── test-setup.ts │ │ │ │ │ ├── tree-standard-state2.interface.ts │ │ │ │ │ ├── top │ │ │ │ │ │ ├── select-top-entities.selectors.ts │ │ │ │ │ │ └── standard-signals-top-definition.const.ts │ │ │ │ │ ├── selectors │ │ │ │ │ │ └── select-tree-standard-signals-state.selectors.ts │ │ │ │ │ ├── department │ │ │ │ │ │ └── select-departments.selector.ts │ │ │ │ │ ├── locations │ │ │ │ │ │ ├── selectors │ │ │ │ │ │ │ ├── select-location-entities.selectors.ts │ │ │ │ │ │ │ └── select-locations.selectors.ts │ │ │ │ │ │ └── standard-signals-locations-definition.ts │ │ │ │ │ ├── department-children │ │ │ │ │ │ └── department-child.selector.ts │ │ │ │ │ └── tree-standard-state.interface.ts │ │ │ │ ├── feature.const.ts │ │ │ │ ├── tree.component.css │ │ │ │ └── tree.component.html │ │ │ ├── tree-no-refresh │ │ │ │ ├── store │ │ │ │ │ ├── current-location │ │ │ │ │ │ └── test-setup.ts │ │ │ │ │ ├── tree-standard-state2.interface.ts │ │ │ │ │ ├── mark-and-delete-init.ts │ │ │ │ │ ├── top │ │ │ │ │ │ ├── select-top-entities.selectors.ts │ │ │ │ │ │ └── no-refresh-signals-top-definition.const.ts │ │ │ │ │ ├── selectors │ │ │ │ │ │ └── select-tree-standard-signals-state.selectors.ts │ │ │ │ │ ├── department │ │ │ │ │ │ └── select-departments.selector.ts │ │ │ │ │ ├── locations │ │ │ │ │ │ └── selectors │ │ │ │ │ │ │ ├── select-location-entities.selectors.ts │ │ │ │ │ │ │ └── select-locations.selectors.ts │ │ │ │ │ ├── department-children │ │ │ │ │ │ └── department-child.selector.ts │ │ │ │ │ └── tree-standard-state.interface.ts │ │ │ │ ├── feature.const.ts │ │ │ │ ├── tree.component.css │ │ │ │ └── tree.component.html │ │ │ └── tree-no-remove │ │ │ │ ├── store │ │ │ │ ├── current-location │ │ │ │ │ └── test-setup.ts │ │ │ │ ├── tree-standard-state2.interface.ts │ │ │ │ ├── top │ │ │ │ │ ├── select-top-entities.selectors.ts │ │ │ │ │ └── no-remove-signals-top-definition.const.ts │ │ │ │ ├── selectors │ │ │ │ │ └── select-tree-standard-signals-state.selectors.ts │ │ │ │ ├── department │ │ │ │ │ └── select-departments.selector.ts │ │ │ │ ├── locations │ │ │ │ │ └── selectors │ │ │ │ │ │ ├── select-location-entities.selectors.ts │ │ │ │ │ │ └── select-locations.selectors.ts │ │ │ │ ├── department-children │ │ │ │ │ └── department-child.selector.ts │ │ │ │ └── tree-standard-state.interface.ts │ │ │ │ ├── feature.const.ts │ │ │ │ ├── tree.component.css │ │ │ │ └── tree.component.html │ │ ├── shared │ │ │ ├── form-state.interface.ts │ │ │ ├── docs │ │ │ │ ├── doc.interface.ts │ │ │ │ └── docs.service.ts │ │ │ ├── lists │ │ │ │ ├── list.interface.ts │ │ │ │ └── lists.service.ts │ │ │ ├── folders │ │ │ │ ├── folder.interface.ts │ │ │ │ └── folders.service.ts │ │ │ ├── sprint-folders │ │ │ │ ├── sprint-folder.interface.ts │ │ │ │ └── sprint-folders.service.ts │ │ │ ├── entities │ │ │ │ ├── top-entity.type.ts │ │ │ │ ├── location-entity.type.ts │ │ │ │ ├── department-entity.type.ts │ │ │ │ └── department-child-entity.type.ts │ │ │ ├── top │ │ │ │ ├── top-effects.service-token.ts │ │ │ │ └── top.interface.ts │ │ │ ├── locations │ │ │ │ ├── location-effects.service-token.ts │ │ │ │ └── location.interface.ts │ │ │ ├── department │ │ │ │ ├── department-effects.service-token.ts │ │ │ │ └── department.interface.ts │ │ │ ├── functions │ │ │ │ └── add-is-dirty.function.ts │ │ │ ├── department-children │ │ │ │ ├── filter-ids.function.ts │ │ │ │ ├── department-child-effects.service-token.ts │ │ │ │ ├── department-child.interface.ts │ │ │ │ └── update-id-function.ts │ │ │ └── components │ │ │ │ ├── node-editor │ │ │ │ ├── node-editor.component.html │ │ │ │ └── node-editor.component.css │ │ │ │ └── tree │ │ │ │ ├── common-source-node.interface.ts │ │ │ │ ├── tree-node.interface.ts │ │ │ │ └── tree.component.css │ │ └── error-handler │ │ │ └── error-handler.service.ts │ ├── favicon.ico │ ├── test-setup.ts │ ├── styles.css │ └── main.ts │ ├── e2e │ ├── tsconfig.json │ └── common │ │ └── common.test.ts │ ├── proxy.conf.json │ ├── tsconfig.editor.json │ ├── tsconfig.app.json │ └── tsconfig.spec.json ├── libs ├── .gitkeep ├── e2e-common │ ├── README.md │ ├── tsconfig.lib.json │ ├── project.json │ ├── src │ │ ├── lib │ │ │ ├── locators │ │ │ │ ├── locate-home-link.ts │ │ │ │ ├── locate-first-tree-node.ts │ │ │ │ ├── locate-add-button.ts │ │ │ │ ├── locate-edit-field.ts │ │ │ │ ├── locate-edit-button.ts │ │ │ │ ├── locate-tree-label.ts │ │ │ │ ├── locate-delete-button.ts │ │ │ │ ├── locate-options.ts │ │ │ │ ├── locate-2nd-tree-node.ts │ │ │ │ ├── locate-add-menu-container.ts │ │ │ │ └── locate-add-menu-item.ts │ │ │ └── load-route.ts │ │ └── index.ts │ └── tsconfig.json ├── smart-signals │ ├── src │ │ ├── testing.ts │ │ ├── test-setup.ts │ │ └── types │ │ │ └── child-definition-signals.interface.ts │ ├── ng-package.json │ ├── tsconfig.lib.prod.json │ ├── README.md │ ├── tsconfig.spec.json │ ├── tsconfig.lib.json │ └── package.json ├── smart-core │ ├── src │ │ ├── types │ │ │ ├── update-str.interface.ts │ │ │ ├── virtual-array-contents.interface.ts │ │ │ ├── index-prop.interfaces.ts │ │ │ ├── indexes-prop.interface.ts │ │ │ ├── row-prop.interface.ts │ │ │ ├── effect-service.token.ts │ │ │ ├── row-proxy-delete.interface.ts │ │ │ ├── partial-array-definition.interface.ts │ │ │ ├── parent-info.interface.ts │ │ │ ├── smart-error-handler.interface.ts │ │ │ └── smart-validated-entity-definition.type.ts │ │ ├── common │ │ │ ├── is-proxy.const.ts │ │ │ ├── psi.const.ts │ │ │ ├── item-is-marked-for-deletion.function.ts │ │ │ ├── is-null-or-undefined.function.ts │ │ │ ├── assert.function.spec.ts │ │ │ ├── is-virtual-array-contents.function.ts │ │ │ └── for-next.function.ts │ │ ├── test-setup.ts │ │ ├── error-handler │ │ │ ├── smart-error-handler-token.const.ts │ │ │ ├── error-handler.class.ts │ │ │ ├── handle-error.function.ts │ │ │ └── error-handler-registry.class.ts │ │ ├── registrations │ │ │ └── feature-registry.class.ts │ │ ├── smart-selector │ │ │ └── new-row-registry.class.ts │ │ └── providers │ │ │ └── resolve-remove-time.function.ts │ ├── ng-package.json │ ├── tsconfig.lib.prod.json │ ├── README.md │ ├── tsconfig.spec.json │ ├── tsconfig.lib.json │ └── package.json └── smart-ngrx │ ├── src │ ├── dummy.spec.ts │ ├── types │ │ ├── id-prop.interface.ts │ │ ├── rows-prop.interface.ts │ │ ├── ids-prop.interface.ts │ │ ├── update-changes.interface.ts │ │ ├── smart-ngrx-row-base-selector.type.ts │ │ └── child-definition-classic.interface.ts │ ├── test-setup.ts │ ├── smart-selector │ │ └── parent-selector.type.ts │ └── tests │ │ └── functions │ │ ├── clear-state.function.ts │ │ └── create-store.function.ts │ ├── ng-package.json │ ├── tsconfig.lib.prod.json │ ├── README.md │ └── package.json ├── .prettierrc ├── .husky └── pre-commit ├── .npmrc ├── .prettierignore ├── jest.config.ts ├── prisma └── migrations │ └── migration_lock.toml ├── .hintrc ├── tools └── tsconfig.tools.json ├── .editorconfig ├── pnpm-workspace.yaml ├── project.json ├── .htmlhintrc ├── jest.preset.js ├── prisma.config.ts ├── .github ├── dependabot.yml └── workflows │ └── remove-issue-from-project-on-close.yml ├── .cursor └── rules │ └── no-done-in-specs.mdc ├── .jscpd.json ├── .env └── .verdaccio └── config.yml /apps/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /libs/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/server/src/assets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/documentation/src/assets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/assets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/assets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/documentation/src/app/app.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true 3 | } 4 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | pnpm lint-staged 2 | pnpm dupcheck 3 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/routes/home/home.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/routes/home/home.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/documentation/eslint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = []; 2 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | strict-peer-dependencies=false 2 | auto-install-peers=true 3 | -------------------------------------------------------------------------------- /apps/documentation/src/app/header-template.html: -------------------------------------------------------------------------------- 1 | {{ Metadata.description}} 2 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/routes/tree-no-dirty/store/current-location/test-setup.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/routes/tree-standard/store/current-location/test-setup.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/documentation/src/app/home/index.md: -------------------------------------------------------------------------------- 1 | {% include "../../../../../README.md" %} 2 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/routes/tree-no-refresh/store/current-location/test-setup.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/routes/tree-no-remove/store/current-location/test-setup.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/e2e/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.base.json" 3 | } 4 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/e2e/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.base.json" 3 | } 4 | -------------------------------------------------------------------------------- /libs/e2e-common/README.md: -------------------------------------------------------------------------------- 1 | # e2e-common 2 | 3 | This library was generated with [Nx](https://nx.dev). 4 | -------------------------------------------------------------------------------- /apps/documentation/src/main.server.ts: -------------------------------------------------------------------------------- 1 | export { AppServerModule as default } from './app/app.server.module'; 2 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/routes/tree-standard/feature.const.ts: -------------------------------------------------------------------------------- 1 | export const featureName = 'tree-standard'; 2 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/routes/tree-no-dirty/feature.const.ts: -------------------------------------------------------------------------------- 1 | export const featureName = 'tree-no-dirty'; 2 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/routes/tree-no-remove/feature.const.ts: -------------------------------------------------------------------------------- 1 | export const featureName = 'tree-no-remove'; 2 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/routes/tree-standard/feature.const.ts: -------------------------------------------------------------------------------- 1 | export const featureName = 'tree-standard'; 2 | -------------------------------------------------------------------------------- /apps/server/src/app/orm/prisma-service.token.ts: -------------------------------------------------------------------------------- 1 | export const prismaServiceToken = Symbol('prismaServiceToken'); 2 | -------------------------------------------------------------------------------- /apps/server/src/app/socket/socket-gateway.token.ts: -------------------------------------------------------------------------------- 1 | export const socketGatewayToken = Symbol('socketGatewayToken'); 2 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/routes/tree-no-refresh/feature.const.ts: -------------------------------------------------------------------------------- 1 | export const featureName = 'tree-no-refresh'; 2 | -------------------------------------------------------------------------------- /apps/documentation/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DaveMBush/SmartNgRX/HEAD/apps/documentation/src/favicon.ico -------------------------------------------------------------------------------- /apps/server/src/app/docs/doc-out-dto.interface.ts: -------------------------------------------------------------------------------- 1 | export interface DocOutDTO { 2 | did: string; 3 | name: string; 4 | } 5 | -------------------------------------------------------------------------------- /apps/server/src/app/top/top-dto.interface.ts: -------------------------------------------------------------------------------- 1 | export interface TopDTO { 2 | id: string; 3 | locations: string[]; 4 | } 5 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # Add files here to ignore them from prettier formatting 2 | /dist 3 | /coverage 4 | .angular 5 | pnpm-lock.yaml 6 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/routes/tree-standard/tree.component.css: -------------------------------------------------------------------------------- 1 | :host { 2 | display: block; 3 | height: 100%; 4 | } 5 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DaveMBush/SmartNgRX/HEAD/apps/demo-ngrx-classic/src/favicon.ico -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/routes/tree-no-dirty/tree.component.css: -------------------------------------------------------------------------------- 1 | :host { 2 | display: block; 3 | height: 100%; 4 | } 5 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/routes/tree-no-refresh/tree.component.css: -------------------------------------------------------------------------------- 1 | :host { 2 | display: block; 3 | height: 100%; 4 | } 5 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/routes/tree-no-remove/tree.component.css: -------------------------------------------------------------------------------- 1 | :host { 2 | display: block; 3 | height: 100%; 4 | } 5 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/routes/tree-standard/tree.component.css: -------------------------------------------------------------------------------- 1 | :host { 2 | display: block; 3 | height: 100%; 4 | } 5 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DaveMBush/SmartNgRX/HEAD/apps/demo-ngrx-signals/src/favicon.ico -------------------------------------------------------------------------------- /libs/smart-signals/src/testing.ts: -------------------------------------------------------------------------------- 1 | export * from './signal-facade/signals-facade'; 2 | export * from '@smarttools/smart-core'; 3 | -------------------------------------------------------------------------------- /libs/smart-core/src/types/update-str.interface.ts: -------------------------------------------------------------------------------- 1 | export interface UpdateStr { 2 | id: string; 3 | changes: Partial; 4 | } 5 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/proxy.conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "/api": { 3 | "target": "http://localhost:3000", 4 | "secure": false 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/routes/tree-no-dirty/tree-no-dirty.component.css: -------------------------------------------------------------------------------- 1 | :host { 2 | display: block; 3 | height: 100%; 4 | } 5 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/routes/tree-no-refresh/tree-no-refresh.component.css: -------------------------------------------------------------------------------- 1 | :host { 2 | display: block; 3 | height: 100%; 4 | } 5 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/routes/tree-no-remove/tree-no-remove.component.css: -------------------------------------------------------------------------------- 1 | :host { 2 | display: block; 3 | height: 100%; 4 | } 5 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/proxy.conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "/api": { 3 | "target": "http://localhost:3000", 4 | "secure": false 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/shared/form-state.interface.ts: -------------------------------------------------------------------------------- 1 | export interface FormState { 2 | label: string; 3 | childType: string; 4 | } 5 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/shared/form-state.interface.ts: -------------------------------------------------------------------------------- 1 | export interface FormState { 2 | label: string; 3 | childType: string; 4 | } 5 | -------------------------------------------------------------------------------- /libs/smart-ngrx/src/dummy.spec.ts: -------------------------------------------------------------------------------- 1 | describe('dummy', () => { 2 | it('should be true', () => { 3 | expect(true).toBe(true); 4 | }); 5 | }); 6 | -------------------------------------------------------------------------------- /libs/smart-core/src/types/virtual-array-contents.interface.ts: -------------------------------------------------------------------------------- 1 | export interface VirtualArrayContents { 2 | length: number; 3 | indexes: string[]; 4 | } 5 | -------------------------------------------------------------------------------- /jest.config.ts: -------------------------------------------------------------------------------- 1 | import { getJestProjectsAsync } from '@nx/jest'; 2 | 3 | export default async () => ({ 4 | projects: await getJestProjectsAsync(), 5 | }); 6 | -------------------------------------------------------------------------------- /apps/server/src/app/functions/with-created-date.interface.ts: -------------------------------------------------------------------------------- 1 | export interface WithCreatedDate { 2 | type: string; 3 | id: string; 4 | created: Date; 5 | } 6 | -------------------------------------------------------------------------------- /prisma/migrations/migration_lock.toml: -------------------------------------------------------------------------------- 1 | # Please do not edit this file manually 2 | # It should be added in your version-control system (i.e. Git) 3 | provider = "sqlite" -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/routes/tree-no-dirty/store/tree-no-dirty-state2.interface.ts: -------------------------------------------------------------------------------- 1 | export interface TreeNoDirtyState2 { 2 | currentLocation: string; 3 | } 4 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/routes/tree-no-remove/store/tree-no-remove-state2.interface.ts: -------------------------------------------------------------------------------- 1 | export interface TreeNoRemoveState2 { 2 | currentLocation: string; 3 | } 4 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/routes/tree-standard/store/tree-standard-state2.interface.ts: -------------------------------------------------------------------------------- 1 | export interface TreeStandardState2 { 2 | currentLocation: string; 3 | } 4 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/routes/tree-no-dirty/store/tree-standard-state2.interface.ts: -------------------------------------------------------------------------------- 1 | export interface TreeStandardState2 { 2 | currentLocationId: string; 3 | } 4 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/routes/tree-no-remove/store/tree-standard-state2.interface.ts: -------------------------------------------------------------------------------- 1 | export interface TreeStandardState2 { 2 | currentLocationId: string; 3 | } 4 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/routes/tree-standard/store/tree-standard-state2.interface.ts: -------------------------------------------------------------------------------- 1 | export interface TreeStandardState2 { 2 | currentLocationId: string; 3 | } 4 | -------------------------------------------------------------------------------- /apps/server/src/app/docs/doc-in-dto.interface.ts: -------------------------------------------------------------------------------- 1 | export interface DocInDTO { 2 | id?: string; 3 | name: string; 4 | parentId?: string; 5 | version: number; 6 | } 7 | -------------------------------------------------------------------------------- /apps/server/src/app/lists/lists-dto.interface.ts: -------------------------------------------------------------------------------- 1 | export interface ListDTO { 2 | id: string; 3 | parentId?: string; 4 | version: number; 5 | name: string; 6 | } 7 | -------------------------------------------------------------------------------- /libs/smart-core/src/common/is-proxy.const.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * A constant we use to determine if an object is an `ArrayProxy` 3 | */ 4 | export const isProxy = 'πisProxyπ'; 5 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/routes/tree-no-refresh/store/tree-no-refresh-state2.interface.ts: -------------------------------------------------------------------------------- 1 | export interface TreeNoRefreshState2 { 2 | currentLocation: string; 3 | } 4 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/routes/tree-no-refresh/store/tree-standard-state2.interface.ts: -------------------------------------------------------------------------------- 1 | export interface TreeStandardState2 { 2 | currentLocationId: string; 3 | } 4 | -------------------------------------------------------------------------------- /apps/server/src/app/folders/folders-dto.interface.ts: -------------------------------------------------------------------------------- 1 | export interface FolderDTO { 2 | id: string; 3 | parentId?: string; 4 | version: number; 5 | name: string; 6 | } 7 | -------------------------------------------------------------------------------- /apps/server/src/app/socket/cud-message.interface.ts: -------------------------------------------------------------------------------- 1 | export interface CUDMessage { 2 | ids: string[]; 3 | action: 'create' | 'delete' | 'update'; 4 | table: string; 5 | } 6 | -------------------------------------------------------------------------------- /apps/server/tsconfig.editor.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": ["src/**/*.ts"], 4 | "compilerOptions": { 5 | "types": ["jest", "node"] 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /apps/server/src/app/locations/location-dto.interface.ts: -------------------------------------------------------------------------------- 1 | export interface LocationDTO { 2 | id: string; 3 | name: string; 4 | version: number; 5 | departments: string[]; 6 | } 7 | -------------------------------------------------------------------------------- /libs/smart-core/src/common/psi.const.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * A constant we use as a separator when we use feature + entity names 3 | * as a key into a map. 4 | */ 5 | export const psi = 'ψ'; 6 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/tsconfig.editor.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": ["src/**/*.ts"], 4 | "compilerOptions": { 5 | "types": ["jest", "node"] 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/tsconfig.editor.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": ["src/**/*.ts"], 4 | "compilerOptions": { 5 | "types": ["jest", "node"] 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /apps/server/src/app/sprint-folders/sprint-folders-dto.interface.ts: -------------------------------------------------------------------------------- 1 | export interface SprintFolderDTO { 2 | id: string; 3 | parentId?: string; 4 | version: number; 5 | name: string; 6 | } 7 | -------------------------------------------------------------------------------- /.hintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "development" 4 | ], 5 | "hints": { 6 | "axe/forms": [ 7 | "default", 8 | { 9 | "label": "off" 10 | } 11 | ] 12 | } 13 | } -------------------------------------------------------------------------------- /apps/server/src/app/orm/prisma.const.ts: -------------------------------------------------------------------------------- 1 | import { PrismaClient } from '@prisma/client'; 2 | 3 | import { adapter } from './prisma-adapter'; 4 | 5 | export const prisma = new PrismaClient({ adapter }); 6 | -------------------------------------------------------------------------------- /libs/smart-ngrx/src/types/id-prop.interface.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Interface for `Actions` that take an id property 3 | * 4 | * @see `actionFactory` 5 | */ 6 | export interface IdProp { 7 | id: string; 8 | } 9 | -------------------------------------------------------------------------------- /libs/smart-core/src/test-setup.ts: -------------------------------------------------------------------------------- 1 | import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone'; 2 | setupZoneTestEnv({ 3 | errorOnUnknownElements: true, 4 | errorOnUnknownProperties: true, 5 | }); 6 | -------------------------------------------------------------------------------- /libs/smart-ngrx/src/test-setup.ts: -------------------------------------------------------------------------------- 1 | import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone'; 2 | setupZoneTestEnv({ 3 | errorOnUnknownElements: true, 4 | errorOnUnknownProperties: true, 5 | }); 6 | -------------------------------------------------------------------------------- /apps/documentation/src/test-setup.ts: -------------------------------------------------------------------------------- 1 | import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone'; 2 | setupZoneTestEnv({ 3 | errorOnUnknownElements: true, 4 | errorOnUnknownProperties: true, 5 | }); 6 | -------------------------------------------------------------------------------- /apps/documentation/tsconfig.editor.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": ["src/**/*.ts", "src/app/ng-doc.config.ts"], 4 | "compilerOptions": { 5 | "types": ["jest", "node"] 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /libs/smart-ngrx/src/types/rows-prop.interface.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Interface for `Actions` that take an array of rows 3 | * 4 | * @see `actionFactory` 5 | */ 6 | export interface RowsProp { 7 | rows: T[]; 8 | } 9 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/test-setup.ts: -------------------------------------------------------------------------------- 1 | import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone'; 2 | setupZoneTestEnv({ 3 | errorOnUnknownElements: true, 4 | errorOnUnknownProperties: true, 5 | }); 6 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/test-setup.ts: -------------------------------------------------------------------------------- 1 | import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone'; 2 | setupZoneTestEnv({ 3 | errorOnUnknownElements: true, 4 | errorOnUnknownProperties: true, 5 | }); 6 | -------------------------------------------------------------------------------- /libs/smart-core/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", 3 | "dest": "../../dist/libs/smart-core", 4 | "lib": { 5 | "entryFile": "src/index.ts" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /libs/smart-signals/src/test-setup.ts: -------------------------------------------------------------------------------- 1 | import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone'; 2 | 3 | setupZoneTestEnv({ 4 | errorOnUnknownElements: true, 5 | errorOnUnknownProperties: true, 6 | }); 7 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/routes/tree-no-refresh/store/mark-and-delete-init.ts: -------------------------------------------------------------------------------- 1 | export const markAndDelete = { 2 | markDirtyFetchesNew: false, 3 | markDirtyTime: 2 * 60 * 1000, 4 | removeTime: 4 * 60 * 1000, 5 | }; 6 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/shared/docs/doc.interface.ts: -------------------------------------------------------------------------------- 1 | import { SmartNgRXRowBase } from '@smarttools/smart-ngrx'; 2 | 3 | export interface Doc extends SmartNgRXRowBase { 4 | did: string; 5 | name: string; 6 | } 7 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/shared/lists/list.interface.ts: -------------------------------------------------------------------------------- 1 | import { SmartNgRXRowBase } from '@smarttools/smart-ngrx'; 2 | 3 | export interface List extends SmartNgRXRowBase { 4 | id: string; 5 | name: string; 6 | } 7 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/routes/tree-no-refresh/store/mark-and-delete-init.ts: -------------------------------------------------------------------------------- 1 | export const markAndDelete = { 2 | markDirtyFetchesNew: false, 3 | markDirtyTime: 2 * 60 * 1000, 4 | removeTime: 4 * 60 * 1000, 5 | }; 6 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/shared/docs/doc.interface.ts: -------------------------------------------------------------------------------- 1 | import { SmartNgRXRowBase } from '@smarttools/smart-signals'; 2 | 3 | export interface Doc extends SmartNgRXRowBase { 4 | did: string; 5 | name: string; 6 | } 7 | -------------------------------------------------------------------------------- /libs/smart-ngrx/src/types/ids-prop.interface.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Interface for `Actions` that take an array of ids 3 | * 4 | * @see `actionFactory` 5 | */ 6 | 7 | export interface IdsProp { 8 | ids: string[]; 9 | } 10 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/shared/folders/folder.interface.ts: -------------------------------------------------------------------------------- 1 | import { SmartNgRXRowBase } from '@smarttools/smart-ngrx'; 2 | 3 | export interface Folder extends SmartNgRXRowBase { 4 | id: string; 5 | name: string; 6 | } 7 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/shared/lists/list.interface.ts: -------------------------------------------------------------------------------- 1 | import { SmartNgRXRowBase } from '@smarttools/smart-signals'; 2 | 3 | export interface List extends SmartNgRXRowBase { 4 | id: string; 5 | name: string; 6 | } 7 | -------------------------------------------------------------------------------- /apps/documentation/src/styles.scss: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | @import '@ng-doc/app/styles/themes/dark.css'; 3 | 4 | .ng-doc-side-bar-wrapper { 5 | height: 100%; 6 | } 7 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/routes/tree-no-refresh/store/locations/location.actions.ts: -------------------------------------------------------------------------------- 1 | import { actionFactory } from '@smarttools/smart-ngrx'; 2 | 3 | export const locationActions = actionFactory('tree-no-refresh', 'locations'); 4 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/routes/tree-no-remove/store/locations/location.actions.ts: -------------------------------------------------------------------------------- 1 | import { actionFactory } from '@smarttools/smart-ngrx'; 2 | 3 | export const locationActions = actionFactory('tree-no-remove', 'locations'); 4 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/shared/folders/folder.interface.ts: -------------------------------------------------------------------------------- 1 | import { SmartNgRXRowBase } from '@smarttools/smart-signals'; 2 | 3 | export interface Folder extends SmartNgRXRowBase { 4 | id: string; 5 | name: string; 6 | } 7 | -------------------------------------------------------------------------------- /apps/documentation/src/app/home/ng-doc.page.ts: -------------------------------------------------------------------------------- 1 | import { NgDocPage } from '@ng-doc/core'; 2 | 3 | const HomePage: NgDocPage = { 4 | title: `Home`, 5 | mdFile: './index.md', 6 | order: 1, 7 | }; 8 | 9 | export default HomePage; 10 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/shared/sprint-folders/sprint-folder.interface.ts: -------------------------------------------------------------------------------- 1 | import { SmartNgRXRowBase } from '@smarttools/smart-ngrx'; 2 | 3 | export interface SprintFolder extends SmartNgRXRowBase { 4 | id: string; 5 | name: string; 6 | } 7 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/routes/tree-no-dirty/tree.component.html: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/routes/tree-no-refresh/tree.component.html: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/routes/tree-no-remove/tree.component.html: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/routes/tree-standard/tree.component.html: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/shared/sprint-folders/sprint-folder.interface.ts: -------------------------------------------------------------------------------- 1 | import { SmartNgRXRowBase } from '@smarttools/smart-signals'; 2 | 3 | export interface SprintFolder extends SmartNgRXRowBase { 4 | id: string; 5 | name: string; 6 | } 7 | -------------------------------------------------------------------------------- /libs/e2e-common/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "declaration": true, 6 | "types": ["node"] 7 | }, 8 | "include": ["src/**/*.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/routes/tree-standard/tree.component.html: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /apps/documentation/src/main.ts: -------------------------------------------------------------------------------- 1 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 2 | import { AppModule } from './app/app.module'; 3 | 4 | platformBrowserDynamic() 5 | .bootstrapModule(AppModule) 6 | .catch((err) => console.error(err)); 7 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/routes/tree-no-dirty/tree-no-dirty.component.html: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/styles.css: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | 3 | html, 4 | body { 5 | height: 100%; 6 | } 7 | body { 8 | margin: 0; 9 | font-family: Roboto, 'Helvetica Neue', sans-serif; 10 | } 11 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/styles.css: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | 3 | html, 4 | body { 5 | height: 100%; 6 | } 7 | body { 8 | margin: 0; 9 | font-family: Roboto, 'Helvetica Neue', sans-serif; 10 | } 11 | -------------------------------------------------------------------------------- /apps/server/src/app/orm/prisma-adapter.ts: -------------------------------------------------------------------------------- 1 | import 'dotenv/config'; 2 | 3 | import { PrismaBetterSqlite3 } from '@prisma/adapter-better-sqlite3'; 4 | 5 | export const adapter = new PrismaBetterSqlite3({ 6 | url: process.env.DATABASE_URL ?? 'file:../database.db', 7 | }); 8 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/routes/tree-no-refresh/tree-no-refresh.component.html: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/routes/tree-no-remove/tree-no-remove.component.html: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /apps/documentation/src/app/api/ng-doc.category.ts: -------------------------------------------------------------------------------- 1 | import { NgDocCategory } from '@ng-doc/core'; 2 | 3 | const APICategory: NgDocCategory = { 4 | title: 'API', 5 | route: 'apis', 6 | expandable: true, 7 | order: 6, 8 | }; 9 | 10 | export default APICategory; 11 | -------------------------------------------------------------------------------- /libs/smart-ngrx/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", 3 | "dest": "../../dist/libs/smart-ngrx", 4 | "lib": { 5 | "entryFile": "src/index.ts" 6 | }, 7 | "allowedNonPeerDependencies": ["@smarttools/smart-core"] 8 | } 9 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/routes/tree-standard/store/locations/location.actions.ts: -------------------------------------------------------------------------------- 1 | import { actionFactory } from '@smarttools/smart-ngrx'; 2 | 3 | import { featureName } from '../../feature.const'; 4 | 5 | export const locationActions = actionFactory(featureName, 'locations'); 6 | -------------------------------------------------------------------------------- /apps/server/src/app/functions/to-id-type.function.ts: -------------------------------------------------------------------------------- 1 | import { WithCreatedDate } from './with-created-date.interface'; 2 | 3 | export function toIdType( 4 | v: Omit, 5 | ): Omit { 6 | return { type: v.type, id: v.id }; 7 | } 8 | -------------------------------------------------------------------------------- /libs/smart-core/tsconfig.lib.prod.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.lib.json", 3 | "compilerOptions": { 4 | "declarationMap": false, 5 | "moduleResolution": "bundler" 6 | }, 7 | "angularCompilerOptions": { 8 | "compilationMode": "partial" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /libs/smart-ngrx/tsconfig.lib.prod.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.lib.json", 3 | "compilerOptions": { 4 | "declarationMap": false, 5 | "moduleResolution": "bundler" 6 | }, 7 | "angularCompilerOptions": { 8 | "compilationMode": "partial" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /libs/smart-core/src/types/index-prop.interfaces.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Interface for `Actions` that take an array of indexes 3 | * 4 | * @see `actionFactory` 5 | */ 6 | 7 | export interface IndexProp { 8 | index: number; 9 | parentId: string; 10 | childField: string; 11 | } 12 | -------------------------------------------------------------------------------- /libs/smart-signals/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", 3 | "dest": "../../dist/libs/smart-signals", 4 | "lib": { 5 | "entryFile": "src/index.ts" 6 | }, 7 | "allowedNonPeerDependencies": ["@smarttools/smart-core"] 8 | } 9 | -------------------------------------------------------------------------------- /libs/smart-signals/tsconfig.lib.prod.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.lib.json", 3 | "compilerOptions": { 4 | "declarationMap": false, 5 | "moduleResolution": "bundler" 6 | }, 7 | "angularCompilerOptions": { 8 | "compilationMode": "partial" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /libs/smart-core/src/types/indexes-prop.interface.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Interface for `Actions` that take an array of indexes 3 | * 4 | * @see `actionFactory` 5 | */ 6 | 7 | export interface IndexesProp { 8 | indexes: number[]; 9 | parentId: string; 10 | childField: string; 11 | } 12 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/routes/tree-no-dirty/store/department-children/department-child.selector.ts: -------------------------------------------------------------------------------- 1 | import { createSmartSelector } from '@smarttools/smart-ngrx'; 2 | 3 | export const selectDepartmentChildren = createSmartSelector( 4 | 'tree-no-dirty', 5 | 'departmentChildren', 6 | ); 7 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/shared/entities/top-entity.type.ts: -------------------------------------------------------------------------------- 1 | import { EntityState } from '@ngrx/entity'; 2 | import { SmartNgRXRowBase } from '@smarttools/smart-ngrx'; 3 | 4 | import { Top } from '../top/top.interface'; 5 | 6 | export type TopEntity = EntityState; 7 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/shared/entities/top-entity.type.ts: -------------------------------------------------------------------------------- 1 | import { EntityState } from '@ngrx/entity'; 2 | import { SmartNgRXRowBase } from '@smarttools/smart-signals'; 3 | 4 | import { Top } from '../top/top.interface'; 5 | 6 | export type TopEntity = EntityState; 7 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/routes/tree-no-refresh/store/department-children/department-child.selector.ts: -------------------------------------------------------------------------------- 1 | import { createSmartSelector } from '@smarttools/smart-ngrx'; 2 | 3 | export const selectDepartmentChildren = createSmartSelector( 4 | 'tree-no-refresh', 5 | 'departmentChildren', 6 | ); 7 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/routes/tree-no-remove/store/department-children/select-department-children.selectors.ts: -------------------------------------------------------------------------------- 1 | import { createSmartSelector } from '@smarttools/smart-ngrx'; 2 | 3 | export const selectDepartmentChildren = createSmartSelector( 4 | 'tree-no-remove', 5 | 'departmentChildren', 6 | ); 7 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/shared/top/top-effects.service-token.ts: -------------------------------------------------------------------------------- 1 | import { InjectionToken } from '@angular/core'; 2 | 3 | import { TopEffectsService } from './top-effects.service'; 4 | 5 | export const topEffectsServiceToken = new InjectionToken( 6 | 'TopEffectsService', 7 | ); 8 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/shared/top/top.interface.ts: -------------------------------------------------------------------------------- 1 | import { SmartNgRXRowBase } from '@smarttools/smart-ngrx'; 2 | 3 | import { Location } from '../locations/location.interface'; 4 | 5 | export interface Top extends SmartNgRXRowBase { 6 | id: string; 7 | locations: Location[] | string[]; 8 | } 9 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/shared/top/top-effects.service-token.ts: -------------------------------------------------------------------------------- 1 | import { InjectionToken } from '@angular/core'; 2 | 3 | import { TopEffectsService } from './top-effects.service'; 4 | 5 | export const topEffectsServiceToken = new InjectionToken( 6 | 'TopEffectsService', 7 | ); 8 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/shared/top/top.interface.ts: -------------------------------------------------------------------------------- 1 | import { SmartNgRXRowBase } from '@smarttools/smart-signals'; 2 | 3 | import { Location } from '../locations/location.interface'; 4 | 5 | export interface Top extends SmartNgRXRowBase { 6 | id: string; 7 | locations: Location[] | string[]; 8 | } 9 | -------------------------------------------------------------------------------- /apps/documentation/src/app/using-smart-ng-rx/ng-doc.category.ts: -------------------------------------------------------------------------------- 1 | import { NgDocCategory } from '@ng-doc/core'; 2 | 3 | const UsingSmartNgRXCategory: NgDocCategory = { 4 | title: 'Using @smarttools/smart-ngrx', 5 | expandable: true, 6 | order: 2, 7 | }; 8 | 9 | export default UsingSmartNgRXCategory; 10 | -------------------------------------------------------------------------------- /apps/server/webpack.config.js: -------------------------------------------------------------------------------- 1 | const { composePlugins, withNx } = require('@nx/webpack'); 2 | 3 | // Nx plugins for webpack. 4 | module.exports = composePlugins(withNx(), (config) => { 5 | // Update the webpack config as needed here. 6 | // e.g. `config.plugins.push(new MyPlugin())` 7 | return config; 8 | }); 9 | -------------------------------------------------------------------------------- /libs/smart-core/src/types/row-prop.interface.ts: -------------------------------------------------------------------------------- 1 | import { SmartNgRXRowBase } from './smart-ngrx-row-base.interface'; 2 | 3 | /** 4 | * Interface for `Actions` that take a row property 5 | * 6 | * @see `actionFactory` 7 | */ 8 | export interface RowProp { 9 | row: T; 10 | } 11 | -------------------------------------------------------------------------------- /apps/documentation/src/app/api/changes/index.md: -------------------------------------------------------------------------------- 1 | # API Changes 2 | 3 | ## @smarttools/smart-ngrx 4 | 5 | ### Version 1 -> Version 2 6 | 7 | - `SmartNgRXErrorHandler` -> `SmartErrorHandler` 8 | 9 | All other changes are internal 10 | 11 | ### Version 2.1.2 12 | 13 | Added and `add()` method to `SmartArray`. 14 | -------------------------------------------------------------------------------- /apps/documentation/src/app/ngrx-demo-walkthrough/ng-doc.category.ts: -------------------------------------------------------------------------------- 1 | import { NgDocCategory } from '@ng-doc/core'; 2 | 3 | const DemoWalkthroughNgRXCategory: NgDocCategory = { 4 | title: 'NgRX Demo Walkthrough', 5 | expandable: true, 6 | order: 4, 7 | }; 8 | 9 | export default DemoWalkthroughNgRXCategory; 10 | -------------------------------------------------------------------------------- /apps/documentation/tsconfig.server.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.app.json", 3 | "compilerOptions": { 4 | "outDir": "../../out-tsc/server", 5 | "target": "es2019", 6 | "types": ["node"], 7 | "moduleResolution": "bundler" 8 | }, 9 | "files": ["src/main.server.ts", "server.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /libs/smart-ngrx/src/types/update-changes.interface.ts: -------------------------------------------------------------------------------- 1 | import { UpdateStr } from '@smarttools/smart-core'; 2 | 3 | /** 4 | * Interface that defines the changes that need to be made to the store 5 | * when updating an entity 6 | */ 7 | export interface UpdateChanges { 8 | changes: UpdateStr[]; 9 | } 10 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/shared/entities/location-entity.type.ts: -------------------------------------------------------------------------------- 1 | import { EntityState } from '@ngrx/entity'; 2 | import { SmartNgRXRowBase } from '@smarttools/smart-ngrx'; 3 | 4 | import { Location } from '../locations/location.interface'; 5 | 6 | export type LocationEntity = EntityState; 7 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/shared/entities/location-entity.type.ts: -------------------------------------------------------------------------------- 1 | import { EntityState } from '@ngrx/entity'; 2 | import { SmartNgRXRowBase } from '@smarttools/smart-signals'; 3 | 4 | import { Location } from '../locations/location.interface'; 5 | 6 | export type LocationEntity = EntityState; 7 | -------------------------------------------------------------------------------- /apps/documentation/src/app/using-smart-signals/ng-doc.category.ts: -------------------------------------------------------------------------------- 1 | import { NgDocCategory } from '@ng-doc/core'; 2 | 3 | const UsingSmartSignalsCategory: NgDocCategory = { 4 | title: 'Using @smarttools/smart-signals', 5 | expandable: true, 6 | order: 3, 7 | }; 8 | 9 | export default UsingSmartSignalsCategory; 10 | -------------------------------------------------------------------------------- /apps/server/src/app/top/top.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | 3 | import { PrismaModule } from '../orm/prisma.module'; 4 | import { TopController } from './top.controller'; 5 | 6 | @Module({ 7 | imports: [PrismaModule], 8 | controllers: [TopController], 9 | }) 10 | export class TopModule {} 11 | -------------------------------------------------------------------------------- /libs/smart-core/src/error-handler/smart-error-handler-token.const.ts: -------------------------------------------------------------------------------- 1 | import { InjectionToken } from '@angular/core'; 2 | 3 | import { SmartErrorHandler } from '../types/smart-error-handler.interface'; 4 | 5 | export const smartErrorHandlerToken = new InjectionToken( 6 | 'SmartErrorHandlerToken', 7 | ); 8 | -------------------------------------------------------------------------------- /libs/smart-core/src/types/effect-service.token.ts: -------------------------------------------------------------------------------- 1 | import { InjectionToken } from '@angular/core'; 2 | 3 | import { EffectService } from './effect-service'; 4 | 5 | /** 6 | * Shorthand type for the token used to inject the effect service 7 | */ 8 | export type EffectServiceToken = InjectionToken>; 9 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/routes/tree-standard/store/current-location/current-location.actions.ts: -------------------------------------------------------------------------------- 1 | import { createActionGroup, props } from '@ngrx/store'; 2 | 3 | export const currentLocationActions = createActionGroup({ 4 | source: 'Current Location', 5 | events: { 6 | Set: props<{ id: string }>(), 7 | }, 8 | }); 9 | -------------------------------------------------------------------------------- /libs/smart-ngrx/README.md: -------------------------------------------------------------------------------- 1 | # @smarttools/SmartNgRX 2 | 3 | ## Installation 4 | 5 | To install this library, run: 6 | 7 | ```bash 8 | $ npm install @smarttools/smart-ngrx 9 | ``` 10 | 11 | ## Documentation 12 | 13 | All the documentation can be found at [SmartNgRX Documentation](https://davembush.github.io/SmartNgRX/) 14 | -------------------------------------------------------------------------------- /libs/smart-signals/README.md: -------------------------------------------------------------------------------- 1 | # @smarttools/SmartNgRX 2 | 3 | ## Installation 4 | 5 | To install this library, run: 6 | 7 | ```bash 8 | npm install @smarttools/smart-ngrx 9 | ``` 10 | 11 | ## Documentation 12 | 13 | All the documentation can be found at [SmartNgRX Documentation](https://davembush.github.io/SmartNgRX/) 14 | -------------------------------------------------------------------------------- /tools/tsconfig.tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.base.json", 3 | "compilerOptions": { 4 | "outDir": "../dist/out-tsc/tools", 5 | "rootDir": ".", 6 | "module": "commonjs", 7 | "target": "es5", 8 | "types": ["node"], 9 | "importHelpers": false 10 | }, 11 | "include": ["**/*.ts"] 12 | } 13 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/routes/tree-no-remove/store/top/select-top-entities.selector.ts: -------------------------------------------------------------------------------- 1 | import { createSmartSelector } from '@smarttools/smart-ngrx'; 2 | 3 | import { Top } from '../../../../shared/top/top.interface'; 4 | 5 | export const selectTopEntities = createSmartSelector( 6 | 'tree-no-remove', 7 | 'top', 8 | ); 9 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/routes/tree-standard/store/top/select-top-entities.selectors.ts: -------------------------------------------------------------------------------- 1 | import { createSmartSelector } from '@smarttools/smart-ngrx'; 2 | 3 | import { Top } from '../../../../shared/top/top.interface'; 4 | 5 | export const selectTopEntities = createSmartSelector( 6 | 'tree-standard', 7 | 'top', 8 | ); 9 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/shared/entities/department-entity.type.ts: -------------------------------------------------------------------------------- 1 | import { EntityState } from '@ngrx/entity'; 2 | import { SmartNgRXRowBase } from '@smarttools/smart-ngrx'; 3 | 4 | import { Department } from '../department/department.interface'; 5 | 6 | export type DepartmentEntity = EntityState; 7 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/shared/locations/location-effects.service-token.ts: -------------------------------------------------------------------------------- 1 | import { InjectionToken } from '@angular/core'; 2 | 3 | import { LocationEffectsService } from './location-effects.service'; 4 | 5 | export const locationEffectsServiceToken = 6 | new InjectionToken('LocationEffectsService'); 7 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/shared/locations/location-effects.service-token.ts: -------------------------------------------------------------------------------- 1 | import { InjectionToken } from '@angular/core'; 2 | 3 | import { LocationEffectsService } from './location-effects.service'; 4 | 5 | export const locationEffectsServiceToken = 6 | new InjectionToken('LocationEffectsService'); 7 | -------------------------------------------------------------------------------- /apps/documentation/src/app/signals-demo-walkthrough/ng-doc.category.ts: -------------------------------------------------------------------------------- 1 | import { NgDocCategory } from '@ng-doc/core'; 2 | 3 | const DemoWalkthroughSignalsCategory: NgDocCategory = { 4 | title: 'Signals Demo Walkthrough', 5 | expandable: true, 6 | order: 5, 7 | }; 8 | 9 | export default DemoWalkthroughSignalsCategory; 10 | -------------------------------------------------------------------------------- /libs/smart-core/src/error-handler/error-handler.class.ts: -------------------------------------------------------------------------------- 1 | import { SmartErrorHandler } from '../types/smart-error-handler.interface'; 2 | 3 | class ErrorHandler implements SmartErrorHandler { 4 | handleError(_: string, __: unknown): void { 5 | // noop 6 | } 7 | } 8 | 9 | export const errorHandler = new ErrorHandler(); 10 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/shared/entities/department-entity.type.ts: -------------------------------------------------------------------------------- 1 | import { EntityState } from '@ngrx/entity'; 2 | import { SmartNgRXRowBase } from '@smarttools/smart-signals'; 3 | 4 | import { Department } from '../department/department.interface'; 5 | 6 | export type DepartmentEntity = EntityState; 7 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | max_line_length = 80 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true 11 | 12 | [*.md] 13 | max_line_length = off 14 | trim_trailing_whitespace = false 15 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/routes/tree-no-dirty/store/current-location/current-location.actions.ts: -------------------------------------------------------------------------------- 1 | import { createActionGroup, props } from '@ngrx/store'; 2 | 3 | export const currentLocationActions = createActionGroup({ 4 | source: '[Tree No Dirty] Current Location', 5 | events: { 6 | Set: props<{ id: string }>(), 7 | }, 8 | }); 9 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/routes/tree-no-refresh/store/current-location/current-location.actions.ts: -------------------------------------------------------------------------------- 1 | import { createActionGroup, props } from '@ngrx/store'; 2 | 3 | export const currentLocationActions = createActionGroup({ 4 | source: 'Tree No Refresh Current Location', 5 | events: { 6 | Set: props<{ id: string }>(), 7 | }, 8 | }); 9 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/routes/tree-no-remove/store/current-location/current-location.actions.ts: -------------------------------------------------------------------------------- 1 | import { createActionGroup, props } from '@ngrx/store'; 2 | 3 | export const currentLocationActions = createActionGroup({ 4 | source: 'Tree No Refresh Current Location', 5 | events: { 6 | Set: props<{ id: string }>(), 7 | }, 8 | }); 9 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/shared/department/department-effects.service-token.ts: -------------------------------------------------------------------------------- 1 | import { InjectionToken } from '@angular/core'; 2 | 3 | import { DepartmentEffectsService } from './department-effects.service'; 4 | 5 | export const departmentEffectsServiceToken = 6 | new InjectionToken('DepartmentEffectsService'); 7 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/shared/department/department-effects.service-token.ts: -------------------------------------------------------------------------------- 1 | import { InjectionToken } from '@angular/core'; 2 | 3 | import { DepartmentEffectsService } from './department-effects.service'; 4 | 5 | export const departmentEffectsServiceToken = 6 | new InjectionToken('DepartmentEffectsService'); 7 | -------------------------------------------------------------------------------- /libs/smart-core/README.md: -------------------------------------------------------------------------------- 1 | # @smarttools/SmartNgRX 2 | 3 | ## Installation 4 | 5 | This library supports @smarttools/smart-ngrx and @smarttools/smart-signals which will install this as a dependency. 6 | 7 | ## Documentation 8 | 9 | All the documentation can be found at [SmartNgRX Documentation](https://davembush.github.io/SmartNgRX/) 10 | -------------------------------------------------------------------------------- /apps/documentation/src/app/api/changes/ng-doc.page.ts: -------------------------------------------------------------------------------- 1 | import { NgDocPage } from '@ng-doc/core'; 2 | import APICategory from '../ng-doc.category'; 3 | 4 | const ApiChangesPage: NgDocPage = { 5 | title: `API Changes`, 6 | mdFile: './index.md', 7 | order: 4, 8 | category: APICategory, 9 | }; 10 | 11 | export default ApiChangesPage; 12 | -------------------------------------------------------------------------------- /libs/e2e-common/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "e2e-common", 3 | "$schema": "../../node_modules/nx/schemas/project-schema.json", 4 | "sourceRoot": "libs/e2e-common/src", 5 | "projectType": "library", 6 | "tags": ["scope:e2e-common"], 7 | "targets": { 8 | "lint": { 9 | "executor": "@nx/eslint:lint" 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /libs/smart-core/src/types/row-proxy-delete.interface.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Provides an interface we can use to access the delete method on a row 3 | * once it has been wrapped with a `RowProxy` 4 | */ 5 | export interface RowProxyDelete { 6 | /** 7 | * This deletes the row from the store and the server 8 | */ 9 | delete?(): void; 10 | } 11 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/e2e/common/common.test.ts: -------------------------------------------------------------------------------- 1 | import { test } from '@playwright/test'; 2 | import { commonTests, websocketTests } from '@smarttools/e2e-common'; 3 | 4 | test.describe('ngrx-classic', () => { 5 | commonTests(); 6 | websocketTests(); 7 | test('dummy test', () => { 8 | test.expect(true).toBe(true); 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/routes/tree-no-dirty/store/selectors/select-tree-no-dirty-state2.selectors.ts: -------------------------------------------------------------------------------- 1 | import { createFeatureSelector } from '@ngrx/store'; 2 | 3 | import { TreeNoDirtyState2 } from '../tree-no-dirty-state2.interface'; 4 | 5 | export const selectTreeNoDirtyState2 = 6 | createFeatureSelector('tree-no-dirty2'); 7 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/shared/functions/add-is-dirty.function.ts: -------------------------------------------------------------------------------- 1 | import { DepartmentChild } from '../department-children/department-child.interface'; 2 | 3 | export function addIsDirty(rows: DepartmentChild[]): DepartmentChild[] { 4 | return rows.map(function addIsDirtyMapRow(row) { 5 | row.isDirty = false; 6 | return row; 7 | }); 8 | } 9 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/e2e/common/common.test.ts: -------------------------------------------------------------------------------- 1 | import { test } from '@playwright/test'; 2 | import { commonTests, websocketTests } from '@smarttools/e2e-common'; 3 | 4 | test.describe('ngrx-signals', () => { 5 | commonTests(); 6 | websocketTests(); 7 | test('dummy test', () => { 8 | test.expect(true).toBe(true); 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/shared/functions/add-is-dirty.function.ts: -------------------------------------------------------------------------------- 1 | import { DepartmentChild } from '../department-children/department-child.interface'; 2 | 3 | export function addIsDirty(rows: DepartmentChild[]): DepartmentChild[] { 4 | return rows.map(function addIsDirtyMapRow(row) { 5 | row.isDirty = false; 6 | return row; 7 | }); 8 | } 9 | -------------------------------------------------------------------------------- /apps/server/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "module": "commonjs", 6 | "types": ["node"], 7 | "target": "es2021" 8 | }, 9 | "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"], 10 | "include": ["src/**/*.ts"] 11 | } 12 | -------------------------------------------------------------------------------- /apps/server/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "files": [], 4 | "include": [], 5 | "references": [ 6 | { 7 | "path": "./tsconfig.app.json" 8 | }, 9 | { 10 | "path": "./tsconfig.spec.json" 11 | } 12 | ], 13 | "compilerOptions": { 14 | "esModuleInterop": true 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | onlyBuiltDependencies: 2 | - '@nestjs/core' 3 | - '@parcel/watcher' 4 | - '@prisma/client' 5 | - '@prisma/engines' 6 | - '@swc/core' 7 | - better-sqlite3 8 | - core-js 9 | - cypress 10 | - esbuild 11 | - lmdb 12 | - msgpackr-extract 13 | - nx 14 | - oxc-resolver 15 | - prisma 16 | - unrs-resolver 17 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/routes/tree-no-remove/store/selectors/select-tree-no-remove-state2.selectors.ts: -------------------------------------------------------------------------------- 1 | import { createFeatureSelector } from '@ngrx/store'; 2 | 3 | import { TreeNoRemoveState2 } from '../tree-no-remove-state2.interface'; 4 | 5 | export const selectTreeNoRemoveState2 = 6 | createFeatureSelector('tree-no-remove2'); 7 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/shared/docs/docs.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | import { CommonService } from '../department-children/common-service.class'; 4 | 5 | @Injectable({ providedIn: 'root' }) 6 | export class DocsService extends CommonService { 7 | constructor() { 8 | super('./api/docs'); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/shared/locations/location.interface.ts: -------------------------------------------------------------------------------- 1 | import { SmartNgRXRowBase } from '@smarttools/smart-ngrx'; 2 | 3 | import { Department } from '../department/department.interface'; 4 | 5 | export interface Location extends SmartNgRXRowBase { 6 | id: string; 7 | name: string; 8 | departments: Department[] | string[]; 9 | } 10 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/routes/tree-no-dirty/store/top/select-top-entities.selectors.ts: -------------------------------------------------------------------------------- 1 | import { createSmartSignal } from '@smarttools/smart-signals'; 2 | 3 | import { Top } from '../../../../shared/top/top.interface'; 4 | import { featureName } from '../../feature.const'; 5 | export const selectTopEntities = createSmartSignal(featureName, 'top'); 6 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/routes/tree-no-refresh/store/top/select-top-entities.selectors.ts: -------------------------------------------------------------------------------- 1 | import { createSmartSignal } from '@smarttools/smart-signals'; 2 | 3 | import { Top } from '../../../../shared/top/top.interface'; 4 | import { featureName } from '../../feature.const'; 5 | export const selectTopEntities = createSmartSignal(featureName, 'top'); 6 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/routes/tree-no-remove/store/top/select-top-entities.selectors.ts: -------------------------------------------------------------------------------- 1 | import { createSmartSignal } from '@smarttools/smart-signals'; 2 | 3 | import { Top } from '../../../../shared/top/top.interface'; 4 | import { featureName } from '../../feature.const'; 5 | export const selectTopEntities = createSmartSignal(featureName, 'top'); 6 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/routes/tree-standard/store/top/select-top-entities.selectors.ts: -------------------------------------------------------------------------------- 1 | import { createSmartSignal } from '@smarttools/smart-signals'; 2 | 3 | import { Top } from '../../../../shared/top/top.interface'; 4 | import { featureName } from '../../feature.const'; 5 | export const selectTopEntities = createSmartSignal(featureName, 'top'); 6 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/shared/docs/docs.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | import { CommonService } from '../department-children/common-service.class'; 4 | 5 | @Injectable({ providedIn: 'root' }) 6 | export class DocsService extends CommonService { 7 | constructor() { 8 | super('./api/docs'); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /apps/server/src/app/locations/locations.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | 3 | import { PrismaModule } from '../orm/prisma.module'; 4 | import { LocationsController } from './locations.controller'; 5 | 6 | @Module({ 7 | imports: [PrismaModule], 8 | controllers: [LocationsController], 9 | }) 10 | export class LocationsModule {} 11 | -------------------------------------------------------------------------------- /apps/server/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "module": "commonjs", 6 | "types": ["jest", "node"] 7 | }, 8 | "include": [ 9 | "jest.config.ts", 10 | "src/**/*.test.ts", 11 | "src/**/*.spec.ts", 12 | "src/**/*.d.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/routes/tree-no-refresh/store/selectors/select-tree-no-refresh-state2.selectors.ts: -------------------------------------------------------------------------------- 1 | import { createFeatureSelector } from '@ngrx/store'; 2 | 3 | import { TreeNoRefreshState2 } from '../tree-no-refresh-state2.interface'; 4 | 5 | export const selectTreeNoRefreshState2 = 6 | createFeatureSelector('tree-no-refresh2'); 7 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/shared/department-children/filter-ids.function.ts: -------------------------------------------------------------------------------- 1 | export function filterIds(ids: string[], prefix: string): string[] { 2 | return ids 3 | .filter(function filterIdsFilter(id) { 4 | return id.startsWith(prefix); 5 | }) 6 | .map(function filterIdsMap(id) { 7 | return id.split(':')[1]; 8 | }); 9 | } 10 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/shared/lists/lists.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | import { CommonService } from '../department-children/common-service.class'; 4 | 5 | @Injectable({ providedIn: 'root' }) 6 | export class ListsService extends CommonService { 7 | constructor() { 8 | super('./api/lists'); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/shared/department-children/filter-ids.function.ts: -------------------------------------------------------------------------------- 1 | export function filterIds(ids: string[], prefix: string): string[] { 2 | return ids 3 | .filter(function filterIdsFilter(id) { 4 | return id.startsWith(prefix); 5 | }) 6 | .map(function filterIdsMap(id) { 7 | return id.split(':')[1]; 8 | }); 9 | } 10 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/shared/lists/lists.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | import { CommonService } from '../department-children/common-service.class'; 4 | 5 | @Injectable({ providedIn: 'root' }) 6 | export class ListsService extends CommonService { 7 | constructor() { 8 | super('./api/lists'); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/shared/locations/location.interface.ts: -------------------------------------------------------------------------------- 1 | import { SmartNgRXRowBase } from '@smarttools/smart-signals'; 2 | 3 | import { Department } from '../department/department.interface'; 4 | 5 | export interface Location extends SmartNgRXRowBase { 6 | id: string; 7 | name: string; 8 | departments: Department[] | string[]; 9 | } 10 | -------------------------------------------------------------------------------- /apps/documentation/src/app/using-smart-ng-rx/intro/ng-doc.page.ts: -------------------------------------------------------------------------------- 1 | import { NgDocPage } from '@ng-doc/core'; 2 | import UsingSmartNgRXCategory from '../ng-doc.category'; 3 | 4 | const IntroPage: NgDocPage = { 5 | title: `Intro`, 6 | mdFile: './index.md', 7 | order: 1, 8 | category: UsingSmartNgRXCategory, 9 | }; 10 | 11 | export default IntroPage; 12 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/shared/folders/folders.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | import { CommonService } from '../department-children/common-service.class'; 4 | 5 | @Injectable({ providedIn: 'root' }) 6 | export class FoldersService extends CommonService { 7 | constructor() { 8 | super('./api/folders'); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/shared/folders/folders.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | import { CommonService } from '../department-children/common-service.class'; 4 | 5 | @Injectable({ providedIn: 'root' }) 6 | export class FoldersService extends CommonService { 7 | constructor() { 8 | super('./api/folders'); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/routes/tree-no-dirty/store/department/select-departments.selectors.ts: -------------------------------------------------------------------------------- 1 | import { createSmartSelector } from '@smarttools/smart-ngrx'; 2 | 3 | import { Department } from '../../../../shared/department/department.interface'; 4 | 5 | export const selectDepartments = createSmartSelector( 6 | 'tree-no-dirty', 7 | 'departments', 8 | ); 9 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/routes/tree-no-remove/store/department/select-department.selectors.ts: -------------------------------------------------------------------------------- 1 | import { createSmartSelector } from '@smarttools/smart-ngrx'; 2 | 3 | import { Department } from '../../../../shared/department/department.interface'; 4 | 5 | export const selectDepartments = createSmartSelector( 6 | 'tree-no-remove', 7 | 'departments', 8 | ); 9 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/routes/tree-standard/store/department/select-departments.selector.ts: -------------------------------------------------------------------------------- 1 | import { createSmartSelector } from '@smarttools/smart-ngrx'; 2 | 3 | import { Department } from '../../../../shared/department/department.interface'; 4 | 5 | export const selectDepartments = createSmartSelector( 6 | 'tree-standard', 7 | 'departments', 8 | ); 9 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "types": [], 6 | "moduleResolution": "bundler" 7 | }, 8 | "files": ["src/main.ts"], 9 | "include": ["src/**/*.d.ts"], 10 | "exclude": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts"] 11 | } 12 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "types": [], 6 | "moduleResolution": "bundler" 7 | }, 8 | "files": ["src/main.ts"], 9 | "include": ["src/**/*.d.ts"], 10 | "exclude": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts"] 11 | } 12 | -------------------------------------------------------------------------------- /apps/documentation/src/app/using-smart-signals/intro/ng-doc.page.ts: -------------------------------------------------------------------------------- 1 | import { NgDocPage } from '@ng-doc/core'; 2 | import UsingSmartSignalsCategory from '../ng-doc.category'; 3 | 4 | const IntroPage: NgDocPage = { 5 | title: `Intro`, 6 | mdFile: './index.md', 7 | order: 1, 8 | category: UsingSmartSignalsCategory, 9 | }; 10 | 11 | export default IntroPage; 12 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/routes/tree-no-refresh/store/department/select-departments.selector.ts: -------------------------------------------------------------------------------- 1 | import { createSmartSelector } from '@smarttools/smart-ngrx'; 2 | 3 | import { Department } from '../../../../shared/department/department.interface'; 4 | 5 | export const selectDepartments = createSmartSelector( 6 | 'tree-no-refresh', 7 | 'departments', 8 | ); 9 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/shared/entities/department-child-entity.type.ts: -------------------------------------------------------------------------------- 1 | import { EntityState } from '@ngrx/entity'; 2 | import { SmartNgRXRowBase } from '@smarttools/smart-ngrx'; 3 | 4 | import { DepartmentChild } from '../department-children/department-child.interface'; 5 | 6 | export type DepartmentChildEntity = EntityState< 7 | DepartmentChild & SmartNgRXRowBase 8 | >; 9 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/shared/entities/department-child-entity.type.ts: -------------------------------------------------------------------------------- 1 | import { EntityState } from '@ngrx/entity'; 2 | import { SmartNgRXRowBase } from '@smarttools/smart-signals'; 3 | 4 | import { DepartmentChild } from '../department-children/department-child.interface'; 5 | 6 | export type DepartmentChildEntity = EntityState< 7 | DepartmentChild & SmartNgRXRowBase 8 | >; 9 | -------------------------------------------------------------------------------- /apps/documentation/src/app/using-smart-ng-rx/crud-support/updates/ng-doc.page.ts: -------------------------------------------------------------------------------- 1 | import { NgDocPage } from '@ng-doc/core'; 2 | import UsingSmartNgRXCategory from '../ng-doc.category'; 3 | 4 | const UpdatesPage: NgDocPage = { 5 | title: `Updates`, 6 | mdFile: './index.md', 7 | order: 3, 8 | category: UsingSmartNgRXCategory, 9 | }; 10 | 11 | export default UpdatesPage; 12 | -------------------------------------------------------------------------------- /apps/documentation/src/app/using-smart-signals/crud-support/deleting/ng-doc.page.ts: -------------------------------------------------------------------------------- 1 | import { NgDocPage } from '@ng-doc/core'; 2 | import CrudSupportCategory from '../ng-doc.category'; 3 | 4 | const CreatePage: NgDocPage = { 5 | title: `Delete a Row`, 6 | mdFile: './index.md', 7 | order: 5, 8 | category: CrudSupportCategory, 9 | }; 10 | 11 | export default CreatePage; 12 | -------------------------------------------------------------------------------- /apps/documentation/src/app/using-smart-signals/crud-support/updates/ng-doc.page.ts: -------------------------------------------------------------------------------- 1 | import { NgDocPage } from '@ng-doc/core'; 2 | import CrudSupportCategory from '../ng-doc.category'; 3 | 4 | const UpdatesPage: NgDocPage = { 5 | title: `Updates`, 6 | mdFile: './index.md', 7 | order: 3, 8 | category: CrudSupportCategory, 9 | }; 10 | 11 | export default UpdatesPage; 12 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/error-handler/error-handler.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { SmartErrorHandler } from '@smarttools/smart-ngrx'; 3 | 4 | @Injectable() 5 | export class ErrorHandlerService implements SmartErrorHandler { 6 | handleError(message: string, error: unknown): void { 7 | console.error(message, error); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/routes/tree-standard/store/locations/selectors/select-location-entities.selectors.ts: -------------------------------------------------------------------------------- 1 | import { createSmartSelector } from '@smarttools/smart-ngrx'; 2 | 3 | import { Location } from '../../../../../shared/locations/location.interface'; 4 | 5 | export const selectLocationEntities = createSmartSelector( 6 | 'tree-standard', 7 | 'locations', 8 | ); 9 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/error-handler/error-handler.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { SmartErrorHandler } from '@smarttools/smart-signals'; 3 | 4 | @Injectable() 5 | export class ErrorHandlerService implements SmartErrorHandler { 6 | handleError(message: string, error: unknown): void { 7 | console.error(message, error); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /apps/documentation/src/app/ngrx-demo-walkthrough/adding-rows/ng-doc.page.ts: -------------------------------------------------------------------------------- 1 | import { NgDocPage } from '@ng-doc/core'; 2 | import DemoWalkthroughNgRXCategory from '../ng-doc.category'; 3 | 4 | const IntroPage: NgDocPage = { 5 | title: `Adding Rows`, 6 | mdFile: './index.md', 7 | order: 9, 8 | category: DemoWalkthroughNgRXCategory, 9 | }; 10 | 11 | export default IntroPage; 12 | -------------------------------------------------------------------------------- /apps/documentation/src/app/using-smart-ng-rx/crud-support/deleting/ng-doc.page.ts: -------------------------------------------------------------------------------- 1 | import { NgDocPage } from '@ng-doc/core'; 2 | import UsingSmartNgRXCategory from '../ng-doc.category'; 3 | 4 | const CreatePage: NgDocPage = { 5 | title: `Delete a Row`, 6 | mdFile: './index.md', 7 | order: 5, 8 | category: UsingSmartNgRXCategory, 9 | }; 10 | 11 | export default CreatePage; 12 | -------------------------------------------------------------------------------- /apps/documentation/src/app/using-smart-ng-rx/web-sockets/ng-doc.page.ts: -------------------------------------------------------------------------------- 1 | import { NgDocPage } from '@ng-doc/core'; 2 | import UsingSmartNgRXCategory from '../ng-doc.category'; 3 | 4 | const WebSocketsPage: NgDocPage = { 5 | title: `WebSockets`, 6 | mdFile: './index.md', 7 | order: 14, 8 | category: UsingSmartNgRXCategory, 9 | }; 10 | 11 | export default WebSocketsPage; 12 | -------------------------------------------------------------------------------- /apps/documentation/src/app/using-smart-signals/crud-support/create/ng-doc.page.ts: -------------------------------------------------------------------------------- 1 | import { NgDocPage } from '@ng-doc/core'; 2 | import CrudSupportCategory from '../ng-doc.category'; 3 | 4 | const CreatePage: NgDocPage = { 5 | title: `Create (Add) a Row`, 6 | mdFile: './index.md', 7 | order: 4, 8 | category: CrudSupportCategory, 9 | }; 10 | 11 | export default CreatePage; 12 | -------------------------------------------------------------------------------- /apps/documentation/src/app/using-smart-signals/crud-support/virtual-ids/ng-doc.page.ts: -------------------------------------------------------------------------------- 1 | import { NgDocPage } from '@ng-doc/core'; 2 | import CrudSupportCategory from '../ng-doc.category'; 3 | 4 | const UpdatesPage: NgDocPage = { 5 | title: `Virtual IDs`, 6 | mdFile: './index.md', 7 | order: 2, 8 | category: CrudSupportCategory, 9 | }; 10 | 11 | export default UpdatesPage; 12 | -------------------------------------------------------------------------------- /libs/e2e-common/src/lib/locators/locate-home-link.ts: -------------------------------------------------------------------------------- 1 | import { Page } from '@playwright/test'; 2 | 3 | /** 4 | * Locates the home link in the navigation bar. 5 | * 6 | * @param page The Playwright page object. 7 | * 8 | * @returns A locator for the home link. 9 | */ 10 | export function locateHomeLink(page: Page) { 11 | return page.locator('a[href="/home"]').first(); 12 | } 13 | -------------------------------------------------------------------------------- /apps/documentation/src/app/ngrx-demo-walkthrough/deleting-rows/ng-doc.page.ts: -------------------------------------------------------------------------------- 1 | import { NgDocPage } from '@ng-doc/core'; 2 | import DemoWalkthroughNgRXCategory from '../ng-doc.category'; 3 | 4 | const IntroPage: NgDocPage = { 5 | title: `Deleting Rows`, 6 | mdFile: './index.md', 7 | order: 10, 8 | category: DemoWalkthroughNgRXCategory, 9 | }; 10 | 11 | export default IntroPage; 12 | -------------------------------------------------------------------------------- /apps/documentation/src/app/ngrx-demo-walkthrough/introduction/ng-doc.page.ts: -------------------------------------------------------------------------------- 1 | import { NgDocPage } from '@ng-doc/core'; 2 | import DemoWalkthroughNgRXCategory from '../ng-doc.category'; 3 | 4 | const IntroPage: NgDocPage = { 5 | title: `Introduction`, 6 | mdFile: './index.md', 7 | order: 1, 8 | category: DemoWalkthroughNgRXCategory, 9 | }; 10 | 11 | export default IntroPage; 12 | -------------------------------------------------------------------------------- /apps/documentation/src/app/ngrx-demo-walkthrough/selectors/ng-doc.page.ts: -------------------------------------------------------------------------------- 1 | import { NgDocPage } from '@ng-doc/core'; 2 | import DemoWalkthroughNgRXCategory from '../ng-doc.category'; 3 | 4 | const SelectorsPage: NgDocPage = { 5 | title: `Selectors`, 6 | mdFile: './index.md', 7 | order: 6, 8 | category: DemoWalkthroughNgRXCategory, 9 | }; 10 | 11 | export default SelectorsPage; 12 | -------------------------------------------------------------------------------- /apps/documentation/src/app/using-smart-ng-rx/child-fields/ng-doc.page.ts: -------------------------------------------------------------------------------- 1 | import { NgDocPage } from '@ng-doc/core'; 2 | import UsingSmartNgRXCategory from '../ng-doc.category'; 3 | 4 | const ChildFieldsPage: NgDocPage = { 5 | title: `Child Fields`, 6 | mdFile: './index.md', 7 | order: 11, 8 | category: UsingSmartNgRXCategory, 9 | }; 10 | 11 | export default ChildFieldsPage; 12 | -------------------------------------------------------------------------------- /apps/documentation/src/app/using-smart-ng-rx/crud-support/create/ng-doc.page.ts: -------------------------------------------------------------------------------- 1 | import { NgDocPage } from '@ng-doc/core'; 2 | import UsingSmartNgRXCategory from '../ng-doc.category'; 3 | 4 | const CreatePage: NgDocPage = { 5 | title: `Create (Add) a Row`, 6 | mdFile: './index.md', 7 | order: 4, 8 | category: UsingSmartNgRXCategory, 9 | }; 10 | 11 | export default CreatePage; 12 | -------------------------------------------------------------------------------- /apps/documentation/src/app/using-smart-ng-rx/crud-support/virtual-ids/ng-doc.page.ts: -------------------------------------------------------------------------------- 1 | import { NgDocPage } from '@ng-doc/core'; 2 | import UsingSmartNgRXCategory from '../ng-doc.category'; 3 | 4 | const UpdatesPage: NgDocPage = { 5 | title: `Virtual IDs`, 6 | mdFile: './index.md', 7 | order: 2, 8 | category: UsingSmartNgRXCategory, 9 | }; 10 | 11 | export default UpdatesPage; 12 | -------------------------------------------------------------------------------- /apps/documentation/src/app/using-smart-ng-rx/mark-and-delete/intro/ng-doc.page.ts: -------------------------------------------------------------------------------- 1 | import { NgDocPage } from '@ng-doc/core'; 2 | import MarkAndDeleteCategory from '../ng-doc.category'; 3 | 4 | const ProxyChildPage: NgDocPage = { 5 | title: `Introduction`, 6 | mdFile: './index.md', 7 | order: 1, 8 | category: MarkAndDeleteCategory, 9 | }; 10 | 11 | export default ProxyChildPage; 12 | -------------------------------------------------------------------------------- /apps/documentation/src/app/using-smart-signals/web-sockets/ng-doc.page.ts: -------------------------------------------------------------------------------- 1 | import { NgDocPage } from '@ng-doc/core'; 2 | import UsingSmartSignalsCategory from '../ng-doc.category'; 3 | 4 | const WebSocketsPage: NgDocPage = { 5 | title: `WebSockets`, 6 | mdFile: './index.md', 7 | order: 14, 8 | category: UsingSmartSignalsCategory, 9 | }; 10 | 11 | export default WebSocketsPage; 12 | -------------------------------------------------------------------------------- /libs/smart-ngrx/src/smart-selector/parent-selector.type.ts: -------------------------------------------------------------------------------- 1 | import { EntityState } from '@ngrx/entity'; 2 | import { MemoizedSelector } from '@ngrx/store'; 3 | 4 | /** 5 | * Shorthand for the type of the parentSelector. 6 | * 7 | * @see `createInnerSmartSelector` 8 | * @see `createSmartSelector` 9 | */ 10 | export type ParentSelector

= MemoizedSelector>; 11 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/shared/sprint-folders/sprint-folders.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | import { CommonService } from '../department-children/common-service.class'; 4 | 5 | @Injectable({ providedIn: 'root' }) 6 | export class SprintFoldersService extends CommonService { 7 | constructor() { 8 | super('./api/sprintFolders'); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/shared/sprint-folders/sprint-folders.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | import { CommonService } from '../department-children/common-service.class'; 4 | 5 | @Injectable({ providedIn: 'root' }) 6 | export class SprintFoldersService extends CommonService { 7 | constructor() { 8 | super('./api/sprintFolders'); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /apps/documentation/src/app/app.server.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { ServerModule } from '@angular/platform-server'; 3 | 4 | import { AppModule } from './app.module'; 5 | import { AppComponent } from './app.component'; 6 | 7 | @NgModule({ 8 | imports: [AppModule, ServerModule], 9 | bootstrap: [AppComponent], 10 | }) 11 | export class AppServerModule {} 12 | -------------------------------------------------------------------------------- /apps/documentation/src/app/signals-demo-walkthrough/adding-rows/ng-doc.page.ts: -------------------------------------------------------------------------------- 1 | import { NgDocPage } from '@ng-doc/core'; 2 | import DemoWalkthroughSignalsCategory from '../ng-doc.category'; 3 | 4 | const IntroPage: NgDocPage = { 5 | title: `Adding Rows`, 6 | mdFile: './index.md', 7 | order: 9, 8 | category: DemoWalkthroughSignalsCategory, 9 | }; 10 | 11 | export default IntroPage; 12 | -------------------------------------------------------------------------------- /apps/documentation/src/app/using-smart-ng-rx/crud-support/retrieving/ng-doc.page.ts: -------------------------------------------------------------------------------- 1 | import { NgDocPage } from '@ng-doc/core'; 2 | import UsingSmartNgRXCategory from '../ng-doc.category'; 3 | 4 | const UpdatesPage: NgDocPage = { 5 | title: `Retrieving Rows`, 6 | mdFile: './index.md', 7 | order: 1, 8 | category: UsingSmartNgRXCategory, 9 | }; 10 | 11 | export default UpdatesPage; 12 | -------------------------------------------------------------------------------- /apps/documentation/src/app/using-smart-ng-rx/error-handling/ng-doc.page.ts: -------------------------------------------------------------------------------- 1 | import { NgDocPage } from '@ng-doc/core'; 2 | import UsingSmartNgRXCategory from '../ng-doc.category'; 3 | 4 | const ErrorHandlingPage: NgDocPage = { 5 | title: `Error Handling`, 6 | mdFile: './index.md', 7 | order: 15, 8 | category: UsingSmartNgRXCategory, 9 | }; 10 | 11 | export default ErrorHandlingPage; 12 | -------------------------------------------------------------------------------- /apps/documentation/src/app/using-smart-ng-rx/smart-selector/ng-doc.page.ts: -------------------------------------------------------------------------------- 1 | import { NgDocPage } from '@ng-doc/core'; 2 | import UsingSmartNgRXCategory from '../ng-doc.category'; 3 | 4 | const SmartSelectorPage: NgDocPage = { 5 | title: `Smart Selector`, 6 | mdFile: './index.md', 7 | order: 8, 8 | category: UsingSmartNgRXCategory, 9 | }; 10 | 11 | export default SmartSelectorPage; 12 | -------------------------------------------------------------------------------- /apps/documentation/src/app/using-smart-signals/child-fields/ng-doc.page.ts: -------------------------------------------------------------------------------- 1 | import { NgDocPage } from '@ng-doc/core'; 2 | import UsingSmartSignalsCategory from '../ng-doc.category'; 3 | 4 | const ChildFieldsPage: NgDocPage = { 5 | title: `Child Fields`, 6 | mdFile: './index.md', 7 | order: 11, 8 | category: UsingSmartSignalsCategory, 9 | }; 10 | 11 | export default ChildFieldsPage; 12 | -------------------------------------------------------------------------------- /apps/documentation/src/app/using-smart-signals/crud-support/retrieving/ng-doc.page.ts: -------------------------------------------------------------------------------- 1 | import { NgDocPage } from '@ng-doc/core'; 2 | import CrudSupportCategory from '../ng-doc.category'; 3 | 4 | const RetrievingPage: NgDocPage = { 5 | title: `Retrieving Rows`, 6 | mdFile: './index.md', 7 | order: 1, 8 | category: CrudSupportCategory, 9 | }; 10 | 11 | export default RetrievingPage; 12 | -------------------------------------------------------------------------------- /apps/documentation/src/app/using-smart-signals/mark-and-delete/intro/ng-doc.page.ts: -------------------------------------------------------------------------------- 1 | import { NgDocPage } from '@ng-doc/core'; 2 | import MarkAndDeleteCategory from '../ng-doc.category'; 3 | 4 | const ProxyChildPage: NgDocPage = { 5 | title: `Introduction`, 6 | mdFile: './index.md', 7 | order: 1, 8 | category: MarkAndDeleteCategory, 9 | }; 10 | 11 | export default ProxyChildPage; 12 | -------------------------------------------------------------------------------- /project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@smart-ngrx/source", 3 | "$schema": "node_modules/nx/schemas/project-schema.json", 4 | "targets": { 5 | "local-registry": { 6 | "executor": "@nx/js:verdaccio", 7 | "options": { 8 | "port": 4873, 9 | "config": ".verdaccio/config.yml", 10 | "storage": "tmp/local-registry/storage" 11 | } 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/shared/components/node-editor/node-editor.component.html: -------------------------------------------------------------------------------- 1 | 12 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/shared/department-children/department-child-effects.service-token.ts: -------------------------------------------------------------------------------- 1 | import { InjectionToken } from '@angular/core'; 2 | 3 | import { DepartmentChildEffectsService } from './department-child-effects.service'; 4 | 5 | export const departmentChildEffectsServiceToken = 6 | new InjectionToken( 7 | 'DepartmentChildEffectsService', 8 | ); 9 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/shared/components/node-editor/node-editor.component.html: -------------------------------------------------------------------------------- 1 | 12 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/shared/department-children/department-child-effects.service-token.ts: -------------------------------------------------------------------------------- 1 | import { InjectionToken } from '@angular/core'; 2 | 3 | import { DepartmentChildEffectsService } from './department-child-effects.service'; 4 | 5 | export const departmentChildEffectsServiceToken = 6 | new InjectionToken( 7 | 'DepartmentChildEffectsService', 8 | ); 9 | -------------------------------------------------------------------------------- /apps/documentation/src/app/signals-demo-walkthrough/deleting-rows/ng-doc.page.ts: -------------------------------------------------------------------------------- 1 | import { NgDocPage } from '@ng-doc/core'; 2 | import DemoWalkthroughSignalsCategory from '../ng-doc.category'; 3 | 4 | const IntroPage: NgDocPage = { 5 | title: `Deleting Rows`, 6 | mdFile: './index.md', 7 | order: 10, 8 | category: DemoWalkthroughSignalsCategory, 9 | }; 10 | 11 | export default IntroPage; 12 | -------------------------------------------------------------------------------- /apps/documentation/src/app/signals-demo-walkthrough/introduction/ng-doc.page.ts: -------------------------------------------------------------------------------- 1 | import { NgDocPage } from '@ng-doc/core'; 2 | import DemoWalkthroughSignalsCategory from '../ng-doc.category'; 3 | 4 | const IntroPage: NgDocPage = { 5 | title: `Introduction`, 6 | mdFile: './index.md', 7 | order: 1, 8 | category: DemoWalkthroughSignalsCategory, 9 | }; 10 | 11 | export default IntroPage; 12 | -------------------------------------------------------------------------------- /apps/documentation/src/app/using-smart-ng-rx/child-definition/ng-doc.page.ts: -------------------------------------------------------------------------------- 1 | import { NgDocPage } from '@ng-doc/core'; 2 | import UsingSmartNgRXCategory from '../ng-doc.category'; 3 | 4 | const ChildDefinitionPage: NgDocPage = { 5 | title: `ChildDefinition`, 6 | mdFile: './index.md', 7 | order: 7, 8 | category: UsingSmartNgRXCategory, 9 | }; 10 | 11 | export default ChildDefinitionPage; 12 | -------------------------------------------------------------------------------- /apps/documentation/src/app/using-smart-ng-rx/effects-service/ng-doc.page.ts: -------------------------------------------------------------------------------- 1 | import { NgDocPage } from '@ng-doc/core'; 2 | import UsingSmartNgRXCategory from '../ng-doc.category'; 3 | 4 | const EffectsServicePage: NgDocPage = { 5 | title: `Effects Service`, 6 | mdFile: './index.md', 7 | order: 2, 8 | category: UsingSmartNgRXCategory, 9 | }; 10 | 11 | export default EffectsServicePage; 12 | -------------------------------------------------------------------------------- /apps/documentation/src/app/using-smart-ng-rx/injection-token/ng-doc.page.ts: -------------------------------------------------------------------------------- 1 | import { NgDocPage } from '@ng-doc/core'; 2 | import UsingSmartNgRXCategory from '../ng-doc.category'; 3 | 4 | const InjectionTokenPage: NgDocPage = { 5 | title: `Injection Token`, 6 | mdFile: './index.md', 7 | order: 3, 8 | category: UsingSmartNgRXCategory, 9 | }; 10 | 11 | export default InjectionTokenPage; 12 | -------------------------------------------------------------------------------- /apps/documentation/src/app/using-smart-ng-rx/top-level-selectors/ng-doc.page.ts: -------------------------------------------------------------------------------- 1 | import { NgDocPage } from '@ng-doc/core'; 2 | import UsingSmartNgRXCategory from '../ng-doc.category'; 3 | 4 | const TopSelectorPage: NgDocPage = { 5 | title: `Top Level Selectors`, 6 | mdFile: './index.md', 7 | order: 9, 8 | category: UsingSmartNgRXCategory, 9 | }; 10 | 11 | export default TopSelectorPage; 12 | -------------------------------------------------------------------------------- /apps/documentation/src/app/using-smart-signals/smart-signals/ng-doc.page.ts: -------------------------------------------------------------------------------- 1 | import { NgDocPage } from '@ng-doc/core'; 2 | import UsingSmartSignalsCategory from '../ng-doc.category'; 3 | 4 | const SmartSelectorPage: NgDocPage = { 5 | title: `Smart Signals`, 6 | mdFile: './index.md', 7 | order: 8, 8 | category: UsingSmartSignalsCategory, 9 | }; 10 | 11 | export default SmartSelectorPage; 12 | -------------------------------------------------------------------------------- /apps/documentation/src/app/using-smart-signals/top-level-signals/ng-doc.page.ts: -------------------------------------------------------------------------------- 1 | import { NgDocPage } from '@ng-doc/core'; 2 | import UsingSmartNgRXCategory from '../ng-doc.category'; 3 | 4 | const TopSelectorPage: NgDocPage = { 5 | title: `Top Level Signals`, 6 | mdFile: './index.md', 7 | order: 9, 8 | category: UsingSmartNgRXCategory, 9 | }; 10 | 11 | export default TopSelectorPage; 12 | -------------------------------------------------------------------------------- /apps/server/src/app/docs/docs.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | 3 | import { PrismaModule } from '../orm/prisma.module'; 4 | import { SocketModule } from '../socket/socket.module'; 5 | import { DocsController } from './docs.controller'; 6 | 7 | @Module({ 8 | imports: [PrismaModule, SocketModule], 9 | controllers: [DocsController], 10 | }) 11 | export class DocsModule {} 12 | -------------------------------------------------------------------------------- /libs/e2e-common/src/lib/locators/locate-first-tree-node.ts: -------------------------------------------------------------------------------- 1 | import { Page } from '@playwright/test'; 2 | 3 | /** 4 | * Locates the first tree node in the tree. 5 | * 6 | * @param page The Playwright page object. 7 | * 8 | * @returns A locator for the first tree node. 9 | */ 10 | export function locateFirstTreeNode(page: Page) { 11 | return page.locator('mat-tree-node').first(); 12 | } 13 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/e2e/helpers/get-store-feature.ts: -------------------------------------------------------------------------------- 1 | import { Page } from '@playwright/test'; 2 | 3 | import { getStoreState } from './get-store-state'; 4 | 5 | export async function getStoreFeature( 6 | page: Page, 7 | selector: string, 8 | ): Promise> { 9 | const state = await getStoreState(page); 10 | return state[selector] as Record; 11 | } 12 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/routes/tree-standard/store/selectors/select-tree-standard-state2.selectors.ts: -------------------------------------------------------------------------------- 1 | import { createFeatureSelector } from '@ngrx/store'; 2 | 3 | import { featureName } from '../../feature.const'; 4 | import { TreeStandardState2 } from '../tree-standard-state2.interface'; 5 | 6 | export const selectTreeStandardState2 = 7 | createFeatureSelector(featureName + '2'); 8 | -------------------------------------------------------------------------------- /apps/documentation/src/app/using-smart-ng-rx/crud-support/ng-doc.category.ts: -------------------------------------------------------------------------------- 1 | import { NgDocCategory } from '@ng-doc/core'; 2 | import UsingSmartNgRXCategory from '../ng-doc.category'; 3 | 4 | const CrudSupportCategory: NgDocCategory = { 5 | title: 'CRUD Support', 6 | expandable: true, 7 | order: 12, 8 | category: UsingSmartNgRXCategory, 9 | }; 10 | 11 | export default CrudSupportCategory; 12 | -------------------------------------------------------------------------------- /apps/documentation/src/app/using-smart-signals/effects-service/ng-doc.page.ts: -------------------------------------------------------------------------------- 1 | import { NgDocPage } from '@ng-doc/core'; 2 | import UsingSmartSignalsCategory from '../ng-doc.category'; 3 | 4 | const EffectsServicePage: NgDocPage = { 5 | title: `Effects Service`, 6 | mdFile: './index.md', 7 | order: 2, 8 | category: UsingSmartSignalsCategory, 9 | }; 10 | 11 | export default EffectsServicePage; 12 | -------------------------------------------------------------------------------- /apps/documentation/src/app/using-smart-signals/error-handling/ng-doc.page.ts: -------------------------------------------------------------------------------- 1 | import { NgDocPage } from '@ng-doc/core'; 2 | import UsingSmartSignalsCategory from '../ng-doc.category'; 3 | 4 | const ErrorHandlingPage: NgDocPage = { 5 | title: `Error Handling`, 6 | mdFile: './index.md', 7 | order: 15, 8 | category: UsingSmartSignalsCategory, 9 | }; 10 | 11 | export default ErrorHandlingPage; 12 | -------------------------------------------------------------------------------- /apps/documentation/src/app/using-smart-signals/injection-token/ng-doc.page.ts: -------------------------------------------------------------------------------- 1 | import { NgDocPage } from '@ng-doc/core'; 2 | import UsingSmartSignalsCategory from '../ng-doc.category'; 3 | 4 | const InjectionTokenPage: NgDocPage = { 5 | title: `Injection Token`, 6 | mdFile: './index.md', 7 | order: 3, 8 | category: UsingSmartSignalsCategory, 9 | }; 10 | 11 | export default InjectionTokenPage; 12 | -------------------------------------------------------------------------------- /libs/e2e-common/src/lib/locators/locate-add-button.ts: -------------------------------------------------------------------------------- 1 | import { Locator, Page } from '@playwright/test'; 2 | 3 | /** 4 | * Locates the add button in the tree node. 5 | * 6 | * @param page The Playwright page object. 7 | * 8 | * @returns A locator for the add button. 9 | */ 10 | export function locateAddButton(page: Page): Locator { 11 | return page.locator('button[aria-label="add"]'); 12 | } 13 | -------------------------------------------------------------------------------- /libs/e2e-common/src/lib/locators/locate-edit-field.ts: -------------------------------------------------------------------------------- 1 | import { Locator, Page } from '@playwright/test'; 2 | 3 | /** 4 | * Locates the edit field in the node editor. 5 | * 6 | * @param page The Playwright page object. 7 | * 8 | * @returns A locator for the edit field. 9 | */ 10 | export function locateEditField(page: Page): Locator { 11 | return page.locator('dmb-node-editor input'); 12 | } 13 | -------------------------------------------------------------------------------- /libs/smart-core/src/registrations/feature-registry.class.ts: -------------------------------------------------------------------------------- 1 | class FeatureRegistry { 2 | private featureSet = new Set(); 3 | 4 | registerFeature(feature: string): void { 5 | this.featureSet.add(feature); 6 | } 7 | 8 | hasFeature(feature: string): boolean { 9 | return this.featureSet.has(feature); 10 | } 11 | } 12 | 13 | export const featureRegistry = new FeatureRegistry(); 14 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/shared/components/tree/common-source-node.interface.ts: -------------------------------------------------------------------------------- 1 | import { 2 | RowProxyDelete, 3 | SmartArray, 4 | SmartNgRXRowBase, 5 | } from '@smarttools/smart-ngrx'; 6 | export interface CommonSourceNode extends SmartNgRXRowBase, RowProxyDelete { 7 | id: string; 8 | type?: string; 9 | name: string; 10 | children: SmartArray; 11 | } 12 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/routes/tree-no-dirty/store/selectors/select-tree-standard-signals-state.selectors.ts: -------------------------------------------------------------------------------- 1 | import { createFeatureSelector } from '@ngrx/store'; 2 | 3 | import { featureName } from '../../feature.const'; 4 | import { TreeStandardState } from '../tree-standard-state.interface'; 5 | 6 | export const selectTreeStandardSignalsState = 7 | createFeatureSelector(featureName); 8 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/routes/tree-no-remove/store/selectors/select-tree-standard-signals-state.selectors.ts: -------------------------------------------------------------------------------- 1 | import { createFeatureSelector } from '@ngrx/store'; 2 | 3 | import { featureName } from '../../feature.const'; 4 | import { TreeStandardState } from '../tree-standard-state.interface'; 5 | 6 | export const selectTreeStandardSignalsState = 7 | createFeatureSelector(featureName); 8 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/routes/tree-standard/store/selectors/select-tree-standard-signals-state.selectors.ts: -------------------------------------------------------------------------------- 1 | import { createFeatureSelector } from '@ngrx/store'; 2 | 3 | import { featureName } from '../../feature.const'; 4 | import { TreeStandardState } from '../tree-standard-state.interface'; 5 | 6 | export const selectTreeStandardSignalsState = 7 | createFeatureSelector(featureName); 8 | -------------------------------------------------------------------------------- /apps/documentation/src/app/ngrx-demo-walkthrough/tree-component/ng-doc.page.ts: -------------------------------------------------------------------------------- 1 | import { NgDocPage } from '@ng-doc/core'; 2 | import DemoWalkthroughNgRXCategory from '../ng-doc.category'; 3 | 4 | const TreeComponentPage: NgDocPage = { 5 | title: `Tree Component`, 6 | mdFile: './index.md', 7 | order: 7, 8 | category: DemoWalkthroughNgRXCategory, 9 | }; 10 | 11 | export default TreeComponentPage; 12 | -------------------------------------------------------------------------------- /apps/documentation/src/app/using-smart-ng-rx/entity-definitions/ng-doc.page.ts: -------------------------------------------------------------------------------- 1 | import { NgDocPage } from '@ng-doc/core'; 2 | import UsingSmartNgRXCategory from '../ng-doc.category'; 3 | 4 | const EntityDefinitionPage: NgDocPage = { 5 | title: `Entity Definitions`, 6 | mdFile: './index.md', 7 | order: 4, 8 | category: UsingSmartNgRXCategory, 9 | }; 10 | 11 | export default EntityDefinitionPage; 12 | -------------------------------------------------------------------------------- /apps/documentation/src/app/using-smart-ng-rx/entity-registration/ng-doc.page.ts: -------------------------------------------------------------------------------- 1 | import { NgDocPage } from '@ng-doc/core'; 2 | import UsingSmartNgRXCategory from '../ng-doc.category'; 3 | 4 | const RegisterFeaturePage: NgDocPage = { 5 | title: `Entity Registration`, 6 | mdFile: './index.md', 7 | order: 6, 8 | category: UsingSmartNgRXCategory, 9 | }; 10 | 11 | export default RegisterFeaturePage; 12 | -------------------------------------------------------------------------------- /apps/documentation/src/app/using-smart-ng-rx/global-registration/ng-doc.page.ts: -------------------------------------------------------------------------------- 1 | import { NgDocPage } from '@ng-doc/core'; 2 | import UsingSmartNgRXCategory from '../ng-doc.category'; 3 | 4 | const RegisterFeaturePage: NgDocPage = { 5 | title: `Global Registration`, 6 | mdFile: './index.md', 7 | order: 5, 8 | category: UsingSmartNgRXCategory, 9 | }; 10 | 11 | export default RegisterFeaturePage; 12 | -------------------------------------------------------------------------------- /apps/documentation/src/app/using-smart-ng-rx/mark-and-delete/mark-and-delete-init/ng-doc.page.ts: -------------------------------------------------------------------------------- 1 | import { NgDocPage } from '@ng-doc/core'; 2 | import MarkAndDeleteCategory from '../ng-doc.category'; 3 | 4 | const ProxyChildPage: NgDocPage = { 5 | title: `MarkAndDeleteInit`, 6 | mdFile: './index.md', 7 | order: 2, 8 | category: MarkAndDeleteCategory, 9 | }; 10 | 11 | export default ProxyChildPage; 12 | -------------------------------------------------------------------------------- /apps/documentation/src/app/using-smart-signals/child-definition/ng-doc.page.ts: -------------------------------------------------------------------------------- 1 | import { NgDocPage } from '@ng-doc/core'; 2 | import UsingSmartSignalsCategory from '../ng-doc.category'; 3 | 4 | const ChildDefinitionPage: NgDocPage = { 5 | title: `ChildDefinition`, 6 | mdFile: './index.md', 7 | order: 7, 8 | category: UsingSmartSignalsCategory, 9 | }; 10 | 11 | export default ChildDefinitionPage; 12 | -------------------------------------------------------------------------------- /apps/documentation/src/app/using-smart-signals/crud-support/ng-doc.category.ts: -------------------------------------------------------------------------------- 1 | import { NgDocCategory } from '@ng-doc/core'; 2 | import UsingSmartSignalsCategory from '../ng-doc.category'; 3 | 4 | const CrudSupportCategory: NgDocCategory = { 5 | title: 'CRUD Support', 6 | expandable: true, 7 | order: 12, 8 | category: UsingSmartSignalsCategory, 9 | }; 10 | 11 | export default CrudSupportCategory; 12 | -------------------------------------------------------------------------------- /apps/server/src/app/lists/lists.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | 3 | import { PrismaModule } from '../orm/prisma.module'; 4 | import { SocketModule } from '../socket/socket.module'; 5 | import { ListsController } from './lists.controller'; 6 | 7 | @Module({ 8 | imports: [PrismaModule, SocketModule], 9 | controllers: [ListsController], 10 | }) 11 | export class ListsModule {} 12 | -------------------------------------------------------------------------------- /libs/e2e-common/src/lib/locators/locate-edit-button.ts: -------------------------------------------------------------------------------- 1 | import { Locator, Page } from '@playwright/test'; 2 | 3 | /** 4 | * Locates the edit button in the tree node. 5 | * 6 | * @param page The Playwright page object. 7 | * 8 | * @returns A locator for the edit button. 9 | */ 10 | export function locateEditButton(page: Page): Locator { 11 | return page.locator('button[aria-label="edit"]'); 12 | } 13 | -------------------------------------------------------------------------------- /libs/e2e-common/src/lib/locators/locate-tree-label.ts: -------------------------------------------------------------------------------- 1 | import { Locator } from '@playwright/test'; 2 | 3 | /** 4 | * Locates the tree label in the tree node. 5 | * 6 | * @param tree The tree node that wraps the label. 7 | * 8 | * @returns A locator for the tree label. 9 | */ 10 | export function locateTreeLabel(tree: Locator): Locator { 11 | return tree.locator('span.mdc-button__label'); 12 | } 13 | -------------------------------------------------------------------------------- /libs/smart-ngrx/src/tests/functions/clear-state.function.ts: -------------------------------------------------------------------------------- 1 | import { MockStore } from '@ngrx/store/testing'; 2 | import { castTo } from '@smarttools/smart-core'; 3 | 4 | import { store } from '../../smart-selector/store.function'; 5 | 6 | /** 7 | * Used to clear the state of the store during testing 8 | */ 9 | export function clearState(): void { 10 | castTo(store()).setState({}); 11 | } 12 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/routes/tree-standard/store/department-children/department-child.selector.ts: -------------------------------------------------------------------------------- 1 | import { createSmartSelector } from '@smarttools/smart-ngrx'; 2 | 3 | import { DepartmentChild } from '../../../../shared/department-children/department-child.interface'; 4 | 5 | export const selectDepartmentChildren = createSmartSelector( 6 | 'tree-standard', 7 | 'departmentChildren', 8 | ); 9 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/routes/tree-no-refresh/store/selectors/select-tree-standard-signals-state.selectors.ts: -------------------------------------------------------------------------------- 1 | import { createFeatureSelector } from '@ngrx/store'; 2 | 3 | import { featureName } from '../../feature.const'; 4 | import { TreeStandardState } from '../tree-standard-state.interface'; 5 | 6 | export const selectTreeStandardSignalsState = 7 | createFeatureSelector(featureName); 8 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/shared/components/tree/common-source-node.interface.ts: -------------------------------------------------------------------------------- 1 | import { 2 | RowProxyDelete, 3 | SmartArray, 4 | SmartNgRXRowBase, 5 | } from '@smarttools/smart-signals'; 6 | export interface CommonSourceNode extends SmartNgRXRowBase, RowProxyDelete { 7 | id: string; 8 | type?: string; 9 | name: string; 10 | children: SmartArray; 11 | } 12 | -------------------------------------------------------------------------------- /apps/documentation/src/app/ngrx-demo-walkthrough/effects-service/ng-doc.page.ts: -------------------------------------------------------------------------------- 1 | import { NgDocPage } from '@ng-doc/core'; 2 | import DemoWalkthroughNgRXCategory from '../ng-doc.category'; 3 | 4 | const EffectsServicesPage: NgDocPage = { 5 | title: `Effects Services`, 6 | mdFile: './index.md', 7 | order: 5, 8 | category: DemoWalkthroughNgRXCategory, 9 | }; 10 | 11 | export default EffectsServicesPage; 12 | -------------------------------------------------------------------------------- /apps/documentation/src/app/ngrx-demo-walkthrough/retrieving-data/ng-doc.page.ts: -------------------------------------------------------------------------------- 1 | import { NgDocPage } from '@ng-doc/core'; 2 | import DemoWalkthroughNgRXCategory from '../ng-doc.category'; 3 | 4 | const RetrievingDataPage: NgDocPage = { 5 | title: `Retrieving Data`, 6 | mdFile: './index.md', 7 | order: 3, 8 | category: DemoWalkthroughNgRXCategory, 9 | }; 10 | 11 | export default RetrievingDataPage; 12 | -------------------------------------------------------------------------------- /apps/documentation/src/app/ngrx-demo-walkthrough/updating-fields/ng-doc.page.ts: -------------------------------------------------------------------------------- 1 | import { NgDocPage } from '@ng-doc/core'; 2 | import DemoWalkthroughNgRXCategory from '../ng-doc.category'; 3 | 4 | const UpdatingFieldsPage: NgDocPage = { 5 | title: `Updating Fields`, 6 | mdFile: './index.md', 7 | order: 8, 8 | category: DemoWalkthroughNgRXCategory, 9 | }; 10 | 11 | export default UpdatingFieldsPage; 12 | -------------------------------------------------------------------------------- /apps/documentation/src/app/using-smart-ng-rx/mark-and-delete/ng-doc.category.ts: -------------------------------------------------------------------------------- 1 | import { NgDocCategory } from '@ng-doc/core'; 2 | import UsingSmartNgRXCategory from '../ng-doc.category'; 3 | 4 | const MarkAndDeleteCategory: NgDocCategory = { 5 | title: 'Mark and Delete', 6 | expandable: true, 7 | order: 10, 8 | category: UsingSmartNgRXCategory, 9 | }; 10 | 11 | export default MarkAndDeleteCategory; 12 | -------------------------------------------------------------------------------- /apps/documentation/src/app/using-smart-signals/entity-definitions/ng-doc.page.ts: -------------------------------------------------------------------------------- 1 | import { NgDocPage } from '@ng-doc/core'; 2 | import UsingSmartSignalsCategory from '../ng-doc.category'; 3 | 4 | const EntityDefinitionPage: NgDocPage = { 5 | title: `Entity Definitions`, 6 | mdFile: './index.md', 7 | order: 4, 8 | category: UsingSmartSignalsCategory, 9 | }; 10 | 11 | export default EntityDefinitionPage; 12 | -------------------------------------------------------------------------------- /apps/documentation/src/app/using-smart-signals/entity-registration/ng-doc.page.ts: -------------------------------------------------------------------------------- 1 | import { NgDocPage } from '@ng-doc/core'; 2 | import UsingSmartSignalsCategory from '../ng-doc.category'; 3 | 4 | const RegisterFeaturePage: NgDocPage = { 5 | title: `Entity Registration`, 6 | mdFile: './index.md', 7 | order: 6, 8 | category: UsingSmartSignalsCategory, 9 | }; 10 | 11 | export default RegisterFeaturePage; 12 | -------------------------------------------------------------------------------- /apps/documentation/src/app/using-smart-signals/global-registration/ng-doc.page.ts: -------------------------------------------------------------------------------- 1 | import { NgDocPage } from '@ng-doc/core'; 2 | import UsingSmartSignalsCategory from '../ng-doc.category'; 3 | 4 | const RegisterFeaturePage: NgDocPage = { 5 | title: `Global Registration`, 6 | mdFile: './index.md', 7 | order: 5, 8 | category: UsingSmartSignalsCategory, 9 | }; 10 | 11 | export default RegisterFeaturePage; 12 | -------------------------------------------------------------------------------- /apps/documentation/src/app/using-smart-signals/mark-and-delete/mark-and-delete-init/ng-doc.page.ts: -------------------------------------------------------------------------------- 1 | import { NgDocPage } from '@ng-doc/core'; 2 | import MarkAndDeleteCategory from '../ng-doc.category'; 3 | 4 | const ProxyChildPage: NgDocPage = { 5 | title: `MarkAndDeleteInit`, 6 | mdFile: './index.md', 7 | order: 2, 8 | category: MarkAndDeleteCategory, 9 | }; 10 | 11 | export default ProxyChildPage; 12 | -------------------------------------------------------------------------------- /apps/server/src/app/functions/id-to-string.function.ts: -------------------------------------------------------------------------------- 1 | export function idToString(idField: string = 'id'): ( 2 | p: Record, 3 | ) => { 4 | id: string; 5 | created: Date; 6 | } { 7 | return (p: Record): { id: string; created: Date } => { 8 | return { 9 | id: p[idField] as string, 10 | created: p.created as Date, 11 | }; 12 | }; 13 | } 14 | -------------------------------------------------------------------------------- /apps/documentation/src/app/ngrx-demo-walkthrough/setup-client-side/ng-doc.page.ts: -------------------------------------------------------------------------------- 1 | import { NgDocPage } from '@ng-doc/core'; 2 | import DemoWalkthroughNgRXCategory from '../ng-doc.category'; 3 | 4 | const SetupClientSidePage: NgDocPage = { 5 | title: `Setup Client Side`, 6 | mdFile: './index.md', 7 | order: 4, 8 | category: DemoWalkthroughNgRXCategory, 9 | }; 10 | 11 | export default SetupClientSidePage; 12 | -------------------------------------------------------------------------------- /apps/documentation/src/app/signals-demo-walkthrough/retrieving-data/ng-doc.page.ts: -------------------------------------------------------------------------------- 1 | import { NgDocPage } from '@ng-doc/core'; 2 | import DemoWalkthroughSignalsCategory from '../ng-doc.category'; 3 | 4 | const RetrievingDataPage: NgDocPage = { 5 | title: `Retrieving Data`, 6 | mdFile: './index.md', 7 | order: 3, 8 | category: DemoWalkthroughSignalsCategory, 9 | }; 10 | 11 | export default RetrievingDataPage; 12 | -------------------------------------------------------------------------------- /apps/documentation/src/app/signals-demo-walkthrough/smart-signals/ng-doc.page.ts: -------------------------------------------------------------------------------- 1 | import { NgDocPage } from '@ng-doc/core'; 2 | import DemoWalkthroughSignalsCategory from '../ng-doc.category'; 3 | 4 | const SelectorsPage: NgDocPage = { 5 | title: `Smart Signals (Selectors)`, 6 | mdFile: './index.md', 7 | order: 6, 8 | category: DemoWalkthroughSignalsCategory, 9 | }; 10 | 11 | export default SelectorsPage; 12 | -------------------------------------------------------------------------------- /apps/documentation/src/app/signals-demo-walkthrough/tree-component/ng-doc.page.ts: -------------------------------------------------------------------------------- 1 | import { NgDocPage } from '@ng-doc/core'; 2 | import DemoWalkthroughSignalsCategory from '../ng-doc.category'; 3 | 4 | const TreeComponentPage: NgDocPage = { 5 | title: `Tree Component`, 6 | mdFile: './index.md', 7 | order: 7, 8 | category: DemoWalkthroughSignalsCategory, 9 | }; 10 | 11 | export default TreeComponentPage; 12 | -------------------------------------------------------------------------------- /apps/documentation/src/app/signals-demo-walkthrough/updating-fields/ng-doc.page.ts: -------------------------------------------------------------------------------- 1 | import { NgDocPage } from '@ng-doc/core'; 2 | import DemoWalkthroughSignalsCategory from '../ng-doc.category'; 3 | 4 | const UpdatingFieldsPage: NgDocPage = { 5 | title: `Updating Fields`, 6 | mdFile: './index.md', 7 | order: 8, 8 | category: DemoWalkthroughSignalsCategory, 9 | }; 10 | 11 | export default UpdatingFieldsPage; 12 | -------------------------------------------------------------------------------- /apps/documentation/src/app/using-smart-ng-rx/mark-and-delete/entity-mark-and-delete/ng-doc.page.ts: -------------------------------------------------------------------------------- 1 | import { NgDocPage } from '@ng-doc/core'; 2 | import MarkAndDeleteCategory from '../ng-doc.category'; 3 | 4 | const ProxyChildPage: NgDocPage = { 5 | title: `Entity Mark and Delete`, 6 | mdFile: './index.md', 7 | order: 4, 8 | category: MarkAndDeleteCategory, 9 | }; 10 | 11 | export default ProxyChildPage; 12 | -------------------------------------------------------------------------------- /apps/documentation/src/app/using-smart-ng-rx/mark-and-delete/global-mark-and-delete/ng-doc.page.ts: -------------------------------------------------------------------------------- 1 | import { NgDocPage } from '@ng-doc/core'; 2 | import MarkAndDeleteCategory from '../ng-doc.category'; 3 | 4 | const ProxyChildPage: NgDocPage = { 5 | title: `Global Mark and Delete`, 6 | mdFile: './index.md', 7 | order: 3, 8 | category: MarkAndDeleteCategory, 9 | }; 10 | 11 | export default ProxyChildPage; 12 | -------------------------------------------------------------------------------- /apps/documentation/src/app/using-smart-signals/mark-and-delete/entity-mark-and-delete/ng-doc.page.ts: -------------------------------------------------------------------------------- 1 | import { NgDocPage } from '@ng-doc/core'; 2 | import MarkAndDeleteCategory from '../ng-doc.category'; 3 | 4 | const ProxyChildPage: NgDocPage = { 5 | title: `Entity Mark and Delete`, 6 | mdFile: './index.md', 7 | order: 4, 8 | category: MarkAndDeleteCategory, 9 | }; 10 | 11 | export default ProxyChildPage; 12 | -------------------------------------------------------------------------------- /apps/documentation/src/app/using-smart-signals/mark-and-delete/ng-doc.category.ts: -------------------------------------------------------------------------------- 1 | import { NgDocCategory } from '@ng-doc/core'; 2 | import UsingSmartSignalsCategory from '../ng-doc.category'; 3 | 4 | const MarkAndDeleteCategory: NgDocCategory = { 5 | title: 'Mark and Delete', 6 | expandable: true, 7 | order: 10, 8 | category: UsingSmartSignalsCategory, 9 | }; 10 | 11 | export default MarkAndDeleteCategory; 12 | -------------------------------------------------------------------------------- /apps/server/src/app/folders/folders.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | 3 | import { PrismaModule } from '../orm/prisma.module'; 4 | import { SocketModule } from '../socket/socket.module'; 5 | import { FoldersController } from './folders.controller'; 6 | 7 | @Module({ 8 | imports: [PrismaModule, SocketModule], 9 | controllers: [FoldersController], 10 | }) 11 | export class FoldersModule {} 12 | -------------------------------------------------------------------------------- /apps/server/src/app/socket/socket.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | import { Socket } from 'socket.io'; 3 | 4 | import { CUDMessage } from './cud-message.interface'; 5 | 6 | @Injectable() 7 | export class SocketService { 8 | handleConnection(socket: Socket): void { 9 | socket.on('emit', (data: CUDMessage) => { 10 | socket.emit('message', data); 11 | }); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /libs/e2e-common/src/lib/locators/locate-delete-button.ts: -------------------------------------------------------------------------------- 1 | import { Locator, Page } from '@playwright/test'; 2 | 3 | /** 4 | * Locates the delete button in the tree node. 5 | * 6 | * @param page The Playwright page object. 7 | * 8 | * @returns A locator for the delete button. 9 | */ 10 | export function locateDeleteButton(page: Page): Locator { 11 | return page.locator('button[aria-label="delete"]'); 12 | } 13 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/routes/tree-no-dirty/store/top/select-top-entities.selectors.ts: -------------------------------------------------------------------------------- 1 | // jscpd:ignore-start 2 | // intentionally duplicated. 3 | import { createSmartSelector } from '@smarttools/smart-ngrx'; 4 | 5 | import { Top } from '../../../../shared/top/top.interface'; 6 | 7 | export const selectTopEntities = createSmartSelector( 8 | 'tree-no-dirty', 9 | 'top', 10 | ); 11 | // jscpd:ignore-end 12 | -------------------------------------------------------------------------------- /apps/documentation/src/app/ngrx-demo-walkthrough/database-structure/ng-doc.page.ts: -------------------------------------------------------------------------------- 1 | import { NgDocPage } from '@ng-doc/core'; 2 | import DemoWalkthroughNgRXCategory from '../ng-doc.category'; 3 | 4 | const DatabaseStructurePage: NgDocPage = { 5 | title: `Database Structure`, 6 | mdFile: './index.md', 7 | order: 2, 8 | category: DemoWalkthroughNgRXCategory, 9 | }; 10 | 11 | export default DatabaseStructurePage; 12 | -------------------------------------------------------------------------------- /apps/documentation/src/app/signals-demo-walkthrough/effects-service/ng-doc.page.ts: -------------------------------------------------------------------------------- 1 | import { NgDocPage } from '@ng-doc/core'; 2 | import DemoWalkthroughSignalsCategory from '../ng-doc.category'; 3 | 4 | const EffectsServicesPage: NgDocPage = { 5 | title: `Effects Services`, 6 | mdFile: './index.md', 7 | order: 5, 8 | category: DemoWalkthroughSignalsCategory, 9 | }; 10 | 11 | export default EffectsServicesPage; 12 | -------------------------------------------------------------------------------- /libs/smart-core/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "module": "commonjs", 6 | "target": "es2016", 7 | "types": ["jest", "node"] 8 | }, 9 | "files": ["src/test-setup.ts"], 10 | "include": [ 11 | "jest.config.ts", 12 | "src/**/*.test.ts", 13 | "src/**/*.spec.ts", 14 | "src/**/*.d.ts" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/routes/tree-no-refresh/store/top/select-top-entities.selectors.ts: -------------------------------------------------------------------------------- 1 | // jscpd:ignore-start 2 | // intentionally duplicated. 3 | import { createSmartSelector } from '@smarttools/smart-ngrx'; 4 | 5 | import { Top } from '../../../../shared/top/top.interface'; 6 | 7 | export const selectTopEntities = createSmartSelector( 8 | 'tree-no-refresh', 9 | 'top', 10 | ); 11 | // jscpd:ignore-end 12 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/shared/department-children/department-child.interface.ts: -------------------------------------------------------------------------------- 1 | import { 2 | PartialArrayDefinition, 3 | SmartArray, 4 | SmartNgRXRowBase, 5 | } from '@smarttools/smart-ngrx'; 6 | 7 | export interface DepartmentChild extends SmartNgRXRowBase { 8 | id: string; 9 | name: string; 10 | children: 11 | | PartialArrayDefinition 12 | | SmartArray; 13 | } 14 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/routes/tree-no-dirty/store/department/select-departments.selector.ts: -------------------------------------------------------------------------------- 1 | import { createSmartSignal } from '@smarttools/smart-signals'; 2 | 3 | import { Department } from '../../../../shared/department/department.interface'; 4 | import { featureName } from '../../feature.const'; 5 | 6 | export const selectDepartments = createSmartSignal( 7 | featureName, 8 | 'departments', 9 | ); 10 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/routes/tree-no-refresh/store/department/select-departments.selector.ts: -------------------------------------------------------------------------------- 1 | import { createSmartSignal } from '@smarttools/smart-signals'; 2 | 3 | import { Department } from '../../../../shared/department/department.interface'; 4 | import { featureName } from '../../feature.const'; 5 | 6 | export const selectDepartments = createSmartSignal( 7 | featureName, 8 | 'departments', 9 | ); 10 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/routes/tree-no-remove/store/department/select-departments.selector.ts: -------------------------------------------------------------------------------- 1 | import { createSmartSignal } from '@smarttools/smart-signals'; 2 | 3 | import { Department } from '../../../../shared/department/department.interface'; 4 | import { featureName } from '../../feature.const'; 5 | 6 | export const selectDepartments = createSmartSignal( 7 | featureName, 8 | 'departments', 9 | ); 10 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/routes/tree-standard/store/department/select-departments.selector.ts: -------------------------------------------------------------------------------- 1 | import { createSmartSignal } from '@smarttools/smart-signals'; 2 | 3 | import { Department } from '../../../../shared/department/department.interface'; 4 | import { featureName } from '../../feature.const'; 5 | 6 | export const selectDepartments = createSmartSignal( 7 | featureName, 8 | 'departments', 9 | ); 10 | -------------------------------------------------------------------------------- /apps/documentation/src/app/signals-demo-walkthrough/setup-client-side/ng-doc.page.ts: -------------------------------------------------------------------------------- 1 | import { NgDocPage } from '@ng-doc/core'; 2 | import DemoWalkthroughSignalsCategory from '../ng-doc.category'; 3 | 4 | const SetupClientSidePage: NgDocPage = { 5 | title: `Setup Client Side`, 6 | mdFile: './index.md', 7 | order: 4, 8 | category: DemoWalkthroughSignalsCategory, 9 | }; 10 | 11 | export default SetupClientSidePage; 12 | -------------------------------------------------------------------------------- /apps/documentation/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "module": "commonjs", 6 | "target": "es2016", 7 | "types": ["jest", "node"] 8 | }, 9 | "files": ["src/test-setup.ts"], 10 | "include": [ 11 | "jest.config.ts", 12 | "src/**/*.test.ts", 13 | "src/**/*.spec.ts", 14 | "src/**/*.d.ts" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /libs/e2e-common/src/lib/locators/locate-options.ts: -------------------------------------------------------------------------------- 1 | import { Locator, Page } from '@playwright/test'; 2 | 3 | /** 4 | * Locates the options in a select element at the top of the page. 5 | * 6 | * @param page The Playwright page object. 7 | * 8 | * @returns A locator for the options in the select element. 9 | */ 10 | export function locateOptions(page: Page): Locator { 11 | return page.locator('select option'); 12 | } 13 | -------------------------------------------------------------------------------- /libs/smart-signals/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "module": "commonjs", 6 | "target": "es2016", 7 | "types": ["jest", "node"] 8 | }, 9 | "files": ["src/test-setup.ts"], 10 | "include": [ 11 | "jest.config.ts", 12 | "src/**/*.test.ts", 13 | "src/**/*.spec.ts", 14 | "src/**/*.d.ts" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/routes/tree-no-dirty/store/locations/selectors/select-location-entities.selectors.ts: -------------------------------------------------------------------------------- 1 | import { createSmartSignal } from '@smarttools/smart-signals'; 2 | 3 | import { Location } from '../../../../../shared/locations/location.interface'; 4 | import { featureName } from '../../../feature.const'; 5 | export const selectLocationEntities = createSmartSignal( 6 | featureName, 7 | 'locations', 8 | ); 9 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/routes/tree-no-refresh/store/locations/selectors/select-location-entities.selectors.ts: -------------------------------------------------------------------------------- 1 | import { createSmartSignal } from '@smarttools/smart-signals'; 2 | 3 | import { Location } from '../../../../../shared/locations/location.interface'; 4 | import { featureName } from '../../../feature.const'; 5 | export const selectLocationEntities = createSmartSignal( 6 | featureName, 7 | 'locations', 8 | ); 9 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/routes/tree-no-remove/store/locations/selectors/select-location-entities.selectors.ts: -------------------------------------------------------------------------------- 1 | import { createSmartSignal } from '@smarttools/smart-signals'; 2 | 3 | import { Location } from '../../../../../shared/locations/location.interface'; 4 | import { featureName } from '../../../feature.const'; 5 | export const selectLocationEntities = createSmartSignal( 6 | featureName, 7 | 'locations', 8 | ); 9 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/routes/tree-standard/store/locations/selectors/select-location-entities.selectors.ts: -------------------------------------------------------------------------------- 1 | import { createSmartSignal } from '@smarttools/smart-signals'; 2 | 3 | import { Location } from '../../../../../shared/locations/location.interface'; 4 | import { featureName } from '../../../feature.const'; 5 | export const selectLocationEntities = createSmartSignal( 6 | featureName, 7 | 'locations', 8 | ); 9 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/shared/department-children/department-child.interface.ts: -------------------------------------------------------------------------------- 1 | import { 2 | PartialArrayDefinition, 3 | SmartArray, 4 | SmartNgRXRowBase, 5 | } from '@smarttools/smart-signals'; 6 | 7 | export interface DepartmentChild extends SmartNgRXRowBase { 8 | id: string; 9 | name: string; 10 | children: 11 | | PartialArrayDefinition 12 | | SmartArray; 13 | } 14 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "module": "commonjs", 6 | "target": "es2016", 7 | "types": ["jest", "node"] 8 | }, 9 | "files": ["src/test-setup.ts"], 10 | "include": [ 11 | "jest.config.ts", 12 | "src/**/*.test.ts", 13 | "src/**/*.spec.ts", 14 | "src/**/*.d.ts" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /apps/documentation/src/app/signals-demo-walkthrough/database-structure/ng-doc.page.ts: -------------------------------------------------------------------------------- 1 | import { NgDocPage } from '@ng-doc/core'; 2 | import DemoWalkthroughSignalsCategory from '../ng-doc.category'; 3 | 4 | const DatabaseStructurePage: NgDocPage = { 5 | title: `Database Structure`, 6 | mdFile: './index.md', 7 | order: 2, 8 | category: DemoWalkthroughSignalsCategory, 9 | }; 10 | 11 | export default DatabaseStructurePage; 12 | -------------------------------------------------------------------------------- /apps/documentation/src/app/using-smart-ng-rx/using-with-existing-selectors/ng-doc.page.ts: -------------------------------------------------------------------------------- 1 | import { NgDocPage } from '@ng-doc/core'; 2 | import UsingSmartNgRXCategory from '../ng-doc.category'; 3 | 4 | const ExistingSelectorsPage: NgDocPage = { 5 | title: `Using With Existing Selectors`, 6 | mdFile: './index.md', 7 | order: 13, 8 | category: UsingSmartNgRXCategory, 9 | }; 10 | 11 | export default ExistingSelectorsPage; 12 | -------------------------------------------------------------------------------- /apps/documentation/ng-doc.config.ts: -------------------------------------------------------------------------------- 1 | import { NgDocConfiguration } from '@ng-doc/builder'; 2 | 3 | const NgDocConfig: NgDocConfiguration = { 4 | repoConfig: { 5 | url: 'https://github.com/DaveMBush/SmartNgRX', 6 | mainBranch: 'main', 7 | releaseBranch: 'release', 8 | }, 9 | guide: { 10 | headerTemplate: './apps/documentation/src/app/header-template.html', 11 | }, 12 | }; 13 | 14 | export default NgDocConfig; 15 | -------------------------------------------------------------------------------- /apps/documentation/src/app/using-smart-signals/mark-and-delete/global-mark-and-delete/ng-doc.page.ts: -------------------------------------------------------------------------------- 1 | import { NgDocPage } from '@ng-doc/core'; 2 | import MarkAndDeleteCategory from '../ng-doc.category'; 3 | 4 | const GlobalMarkAndDeletePage: NgDocPage = { 5 | title: `Global Mark and Delete`, 6 | mdFile: './index.md', 7 | order: 3, 8 | category: MarkAndDeleteCategory, 9 | }; 10 | 11 | export default GlobalMarkAndDeletePage; 12 | -------------------------------------------------------------------------------- /apps/documentation/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SmartNgRX Documentation 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /libs/e2e-common/src/lib/locators/locate-2nd-tree-node.ts: -------------------------------------------------------------------------------- 1 | import { Page } from '@playwright/test'; 2 | 3 | /** 4 | * Locates the second tree node in the tree. 5 | * Used to find a sub node in a tree. 6 | * 7 | * @param page The Playwright page object. 8 | * 9 | * @returns A locator for the second tree node. 10 | */ 11 | export function locate2ndTreeNode(page: Page) { 12 | return page.locator('mat-tree-node').nth(1); 13 | } 14 | -------------------------------------------------------------------------------- /apps/documentation/src/app/using-smart-signals/using-with-existing-signals/ng-doc.page.ts: -------------------------------------------------------------------------------- 1 | import { NgDocPage } from '@ng-doc/core'; 2 | import UsingSmartSignalsCategory from '../ng-doc.category'; 3 | 4 | const UsingExistingSignalsPage: NgDocPage = { 5 | title: `Using With Existing Signals`, 6 | mdFile: './index.md', 7 | order: 13, 8 | category: UsingSmartSignalsCategory, 9 | }; 10 | 11 | export default UsingExistingSignalsPage; 12 | -------------------------------------------------------------------------------- /apps/documentation/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "allowSyntheticDefaultImports": true, 5 | "outDir": "../../dist/out-tsc", 6 | "types": [], 7 | "moduleResolution": "bundler" 8 | }, 9 | "files": ["src/main.ts", "src/main.server.ts", "server.ts"], 10 | "include": ["src/**/*.d.ts"], 11 | "exclude": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts"] 12 | } 13 | -------------------------------------------------------------------------------- /apps/server/jest.config.ts: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line import/no-default-export -- needed for jest 2 | export default { 3 | displayName: 'server', 4 | preset: '../../jest.preset.js', 5 | testEnvironment: 'node', 6 | transform: { 7 | '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }], 8 | }, 9 | moduleFileExtensions: ['ts', 'js', 'html'], 10 | coverageDirectory: '../../coverage/apps/server', 11 | }; 12 | -------------------------------------------------------------------------------- /apps/server/src/app/departments/departments.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | 3 | import { PrismaModule } from '../orm/prisma.module'; 4 | import { SocketModule } from '../socket/socket.module'; 5 | import { DepartmentsController } from './department.controller'; 6 | 7 | @Module({ 8 | imports: [PrismaModule, SocketModule], 9 | controllers: [DepartmentsController], 10 | }) 11 | export class DepartmentsModule {} 12 | -------------------------------------------------------------------------------- /libs/smart-core/src/common/item-is-marked-for-deletion.function.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * checks to see if the item is marked as deleted so we know 3 | * to not merge it into the array. 4 | * Used by mergeVirtualArrays 5 | * 6 | * @param item the item to check 7 | * @returns true if the item is deleted, false otherwise 8 | */ 9 | export function itemIsMarkedForDeletion(item: string | undefined): boolean { 10 | return item === 'delete'; 11 | } 12 | -------------------------------------------------------------------------------- /libs/smart-core/src/error-handler/handle-error.function.ts: -------------------------------------------------------------------------------- 1 | import { errorHandlerRegistry } from './error-handler-registry.class'; 2 | 3 | /** 4 | * function that allows us to log errors to the client 5 | * 6 | * @param message the message to log 7 | * @param error the error to log 8 | */ 9 | export function handleError(message: string, error: unknown): void { 10 | errorHandlerRegistry.getHandler().handleError(message, error); 11 | } 12 | -------------------------------------------------------------------------------- /libs/smart-ngrx/src/tests/functions/create-store.function.ts: -------------------------------------------------------------------------------- 1 | import { createMockStore } from '@ngrx/store/testing'; 2 | 3 | import { store } from '../../smart-selector/store.function'; 4 | 5 | /** 6 | * Used during unit testing, this creates an empty 7 | * store and puts it in the store() function 8 | * so our code can get at it later. 9 | */ 10 | export function createStore(): void { 11 | store(createMockStore({ initialState: {} })); 12 | } 13 | -------------------------------------------------------------------------------- /apps/server/src/app/sprint-folders/sprint-folders.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | 3 | import { PrismaModule } from '../orm/prisma.module'; 4 | import { SocketModule } from '../socket/socket.module'; 5 | import { SprintFoldersController } from './sprint-folders.controller'; 6 | 7 | @Module({ 8 | imports: [PrismaModule, SocketModule], 9 | controllers: [SprintFoldersController], 10 | }) 11 | export class SprintFoldersModule {} 12 | -------------------------------------------------------------------------------- /.htmlhintrc: -------------------------------------------------------------------------------- 1 | { 2 | "tagname-lowercase": false, 3 | "attr-lowercase": false, 4 | "attr-value-double-quotes": true, 5 | "doctype-first": false, 6 | "tag-pair": true, 7 | "spec-char-escape": true, 8 | "id-unique": true, 9 | "src-not-empty": true, 10 | "attr-no-duplication": true, 11 | "title-require": false, 12 | "space-tab-mixed-disabled": "space2", 13 | "line-max-len": 80, 14 | "indent-width": 2, 15 | "indent-delta": [-2, 2] 16 | } 17 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/routes/tree-no-dirty/store/current-location/current-location.selector.ts: -------------------------------------------------------------------------------- 1 | import { createSelector } from '@ngrx/store'; 2 | 3 | import { selectTreeNoDirtyState2 } from '../selectors/select-tree-no-dirty-state2.selectors'; 4 | 5 | export const selectCurrentLocationId = createSelector( 6 | selectTreeNoDirtyState2, 7 | function selectCurrentLocationIdFunction2(state2) { 8 | return state2.currentLocation; 9 | }, 10 | ); 11 | -------------------------------------------------------------------------------- /apps/server/src/app/departments/department-dto.interface.ts: -------------------------------------------------------------------------------- 1 | export interface DepartmentDTO { 2 | id: string; 3 | parentId?: string; 4 | name: string; 5 | children: { 6 | /** starting index for the ids to be filled into the virtual array */ 7 | startIndex: number; 8 | /** the ids to put into the virtual array */ 9 | indexes: string[]; 10 | /** the total number of ids in the virtual array */ 11 | length: number; 12 | }; 13 | } 14 | -------------------------------------------------------------------------------- /libs/smart-core/src/common/is-null-or-undefined.function.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * determines if the value passed in is null or undefined 3 | * this is safer than just checking for truthy or falsy 4 | * because 0, '' are also falsy values. 5 | * 6 | * @param value the value to check 7 | * @returns true if the value is null or undefined 8 | */ 9 | export function isNullOrUndefined(value: unknown): boolean { 10 | return value === null || value === undefined; 11 | } 12 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/routes/home/home.component.ts: -------------------------------------------------------------------------------- 1 | import { ChangeDetectionStrategy, Component } from '@angular/core'; 2 | import { RouterLink } from '@angular/router'; 3 | 4 | @Component({ 5 | selector: 'dmb-demo-home', 6 | standalone: true, 7 | imports: [RouterLink], 8 | templateUrl: './home.component.html', 9 | styleUrl: './home.component.css', 10 | changeDetection: ChangeDetectionStrategy.OnPush, 11 | }) 12 | export class HomeComponent {} 13 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/routes/tree-no-refresh/store/current-location/current-location.selector.ts: -------------------------------------------------------------------------------- 1 | import { createSelector } from '@ngrx/store'; 2 | 3 | import { selectTreeNoRefreshState2 } from '../selectors/select-tree-no-refresh-state2.selectors'; 4 | 5 | export const selectCurrentLocationId = createSelector( 6 | selectTreeNoRefreshState2, 7 | function selectCurrentLocationIdFunction2(state2) { 8 | return state2.currentLocation; 9 | }, 10 | ); 11 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/routes/home/home.component.ts: -------------------------------------------------------------------------------- 1 | import { ChangeDetectionStrategy, Component } from '@angular/core'; 2 | import { RouterLink } from '@angular/router'; 3 | 4 | @Component({ 5 | selector: 'dmb-demo-home', 6 | standalone: true, 7 | imports: [RouterLink], 8 | templateUrl: './home.component.html', 9 | styleUrl: './home.component.css', 10 | changeDetection: ChangeDetectionStrategy.OnPush, 11 | }) 12 | export class HomeComponent {} 13 | -------------------------------------------------------------------------------- /apps/documentation/src/app/ngrx-demo-walkthrough/processing-websocket-messages/ng-doc.page.ts: -------------------------------------------------------------------------------- 1 | import { NgDocPage } from '@ng-doc/core'; 2 | import DemoWalkthroughNgRXCategory from '../ng-doc.category'; 3 | 4 | const ProcessingWebSocketMessagesPage: NgDocPage = { 5 | title: `Processing WebSocket Messages`, 6 | mdFile: './index.md', 7 | order: 11, 8 | category: DemoWalkthroughNgRXCategory, 9 | }; 10 | 11 | export default ProcessingWebSocketMessagesPage; 12 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/e2e/helpers/get-feature-entity.ts: -------------------------------------------------------------------------------- 1 | import { EntityState } from '@ngrx/entity'; 2 | import { Page } from '@playwright/test'; 3 | 4 | import { getStoreFeature } from './get-store-feature'; 5 | 6 | export async function getFeatureEntity( 7 | page: Page, 8 | feature: string, 9 | entity: string, 10 | ): Promise> { 11 | const state = await getStoreFeature(page, feature); 12 | return state[entity] as EntityState; 13 | } 14 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/routes/tree-no-remove/store/current-location/select-current-location.selectors.ts: -------------------------------------------------------------------------------- 1 | import { createSelector } from '@ngrx/store'; 2 | 3 | import { selectTreeNoRemoveState2 } from '../selectors/select-tree-no-remove-state2.selectors'; 4 | 5 | export const selectCurrentLocationId = createSelector( 6 | selectTreeNoRemoveState2, 7 | function selectCurrentLocationIdFunction2(state2) { 8 | return state2.currentLocation; 9 | }, 10 | ); 11 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/routes/tree-no-dirty/store/department-children/department-child.selector.ts: -------------------------------------------------------------------------------- 1 | import { createSmartSignal } from '@smarttools/smart-signals'; 2 | 3 | import { DepartmentChild } from '../../../../shared/department-children/department-child.interface'; 4 | import { featureName } from '../../feature.const'; 5 | 6 | export const selectDepartmentChildren = createSmartSignal( 7 | featureName, 8 | 'departmentChildren', 9 | ); 10 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/routes/tree-no-remove/store/department-children/department-child.selector.ts: -------------------------------------------------------------------------------- 1 | import { createSmartSignal } from '@smarttools/smart-signals'; 2 | 3 | import { DepartmentChild } from '../../../../shared/department-children/department-child.interface'; 4 | import { featureName } from '../../feature.const'; 5 | 6 | export const selectDepartmentChildren = createSmartSignal( 7 | featureName, 8 | 'departmentChildren', 9 | ); 10 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/routes/tree-standard/store/department-children/department-child.selector.ts: -------------------------------------------------------------------------------- 1 | import { createSmartSignal } from '@smarttools/smart-signals'; 2 | 3 | import { DepartmentChild } from '../../../../shared/department-children/department-child.interface'; 4 | import { featureName } from '../../feature.const'; 5 | 6 | export const selectDepartmentChildren = createSmartSignal( 7 | featureName, 8 | 'departmentChildren', 9 | ); 10 | -------------------------------------------------------------------------------- /apps/documentation/src/app/signals-demo-walkthrough/processing-websocket-messages/ng-doc.page.ts: -------------------------------------------------------------------------------- 1 | import { NgDocPage } from '@ng-doc/core'; 2 | import DemoWalkthroughSignalsCategory from '../ng-doc.category'; 3 | 4 | const ProcessingWebSocketMessagesPage: NgDocPage = { 5 | title: `Processing WebSocket Messages`, 6 | mdFile: './index.md', 7 | order: 11, 8 | category: DemoWalkthroughSignalsCategory, 9 | }; 10 | 11 | export default ProcessingWebSocketMessagesPage; 12 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/shared/department/department.interface.ts: -------------------------------------------------------------------------------- 1 | import { 2 | PartialArrayDefinition, 3 | SmartArray, 4 | SmartNgRXRowBase, 5 | } from '@smarttools/smart-ngrx'; 6 | 7 | import { DepartmentChild } from '../department-children/department-child.interface'; 8 | 9 | export interface Department extends SmartNgRXRowBase { 10 | id: string; 11 | name: string; 12 | children: PartialArrayDefinition | SmartArray; 13 | } 14 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/routes/tree-no-refresh/store/department-children/department-child.selector.ts: -------------------------------------------------------------------------------- 1 | import { createSmartSignal } from '@smarttools/smart-signals'; 2 | 3 | import { DepartmentChild } from '../../../../shared/department-children/department-child.interface'; 4 | import { featureName } from '../../feature.const'; 5 | 6 | export const selectDepartmentChildren = createSmartSignal( 7 | featureName, 8 | 'departmentChildren', 9 | ); 10 | -------------------------------------------------------------------------------- /apps/server/src/app/socket/socket.gateway.ts: -------------------------------------------------------------------------------- 1 | import { WebSocketGateway, WebSocketServer } from '@nestjs/websockets'; 2 | import { Server } from 'socket.io'; 3 | 4 | import { CUDMessage } from './cud-message.interface'; 5 | 6 | @WebSocketGateway({ cors: true, namespace: 'data' }) 7 | export class SocketGateway { 8 | @WebSocketServer() server!: Server; 9 | 10 | sendNotification(message: CUDMessage): void { 11 | this.server.emit('message', message); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /libs/smart-core/src/types/partial-array-definition.interface.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Defines the structure of a partial array 3 | * used to create a virtual array. 4 | */ 5 | export interface PartialArrayDefinition { 6 | /** starting index for the ids to be filled into the virtual array */ 7 | startIndex: number; 8 | /** the ids to put into the virtual array */ 9 | indexes: string[]; 10 | /** the total number of ids in the virtual array */ 11 | length: number; 12 | } 13 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/shared/department/department.interface.ts: -------------------------------------------------------------------------------- 1 | import { 2 | PartialArrayDefinition, 3 | SmartArray, 4 | SmartNgRXRowBase, 5 | } from '@smarttools/smart-signals'; 6 | 7 | import { DepartmentChild } from '../department-children/department-child.interface'; 8 | 9 | export interface Department extends SmartNgRXRowBase { 10 | id: string; 11 | name: string; 12 | children: PartialArrayDefinition | SmartArray; 13 | } 14 | -------------------------------------------------------------------------------- /libs/smart-signals/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "declaration": true, 6 | "declarationMap": true, 7 | "inlineSources": true, 8 | "types": [], 9 | "moduleResolution": "bundler" 10 | }, 11 | "exclude": [ 12 | "src/**/*.spec.ts", 13 | "src/test-setup.ts", 14 | "jest.config.ts", 15 | "src/**/*.test.ts" 16 | ], 17 | "include": ["src/**/*.ts"] 18 | } 19 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/routes/tree-no-dirty/store/locations/location.actions.ts: -------------------------------------------------------------------------------- 1 | import { actionFactory } from '@smarttools/smart-ngrx'; 2 | 3 | // this illustrates how you can access the actions from smart-ngrx 4 | // so you can use them in your own code which we do in current-location.effect.ts 5 | // so we can listen to when the locations are set so we can update the 6 | // current location id in the store 7 | export const locationActions = actionFactory('tree-no-dirty', 'locations'); 8 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "module": "commonjs", 6 | "target": "es2016", 7 | "types": ["jest", "node"] 8 | }, 9 | "files": ["src/test-setup.ts"], 10 | "include": [ 11 | "jest.config.ts", 12 | "src/**/*.test.ts", 13 | "src/**/*.spec.ts", 14 | "src/**/*.d.ts", 15 | "src/app/routes/tests/location.selectors.spec.ts" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /libs/e2e-common/src/lib/load-route.ts: -------------------------------------------------------------------------------- 1 | import { expect, Page } from '@playwright/test'; 2 | 3 | import { locateOptions } from './locators/locate-options'; 4 | 5 | /** 6 | * Loads a route in the Playwright page. 7 | * 8 | * @param page The Playwright page object. 9 | * @param route The route to load. 10 | */ 11 | export async function loadRoute(page: Page, route: string): Promise { 12 | await page.goto(route); 13 | await expect(locateOptions(page)).toHaveCount(3); 14 | } 15 | -------------------------------------------------------------------------------- /apps/documentation/src/app/api/smart-ngrx/ng-doc.api.ts: -------------------------------------------------------------------------------- 1 | import { NgDocApi } from '@ng-doc/core'; 2 | import APICategory from '../ng-doc.category'; 3 | 4 | const api: NgDocApi = { 5 | title: 'Smart NgRX API', 6 | category: APICategory, 7 | route: 'smart-ngrx-api', 8 | order: 1, 9 | scopes: [ 10 | { 11 | name: 'Smart NgRX API', 12 | route: 'smart-ngrx-api', 13 | include: ['libs/smart-ngrx/src/index.ts'], 14 | }, 15 | ], 16 | }; 17 | 18 | export default api; 19 | -------------------------------------------------------------------------------- /apps/server/src/app/socket/socket.module.ts: -------------------------------------------------------------------------------- 1 | import { Global, Module } from '@nestjs/common'; 2 | 3 | import { SocketGateway } from './socket.gateway'; 4 | import { socketGatewayToken } from './socket-gateway.token'; 5 | 6 | @Global() 7 | @Module({ 8 | providers: [ 9 | { 10 | provide: socketGatewayToken, 11 | useClass: SocketGateway, 12 | }, 13 | SocketGateway, 14 | ], 15 | exports: [socketGatewayToken, SocketGateway], 16 | }) 17 | export class SocketModule {} 18 | -------------------------------------------------------------------------------- /jest.preset.js: -------------------------------------------------------------------------------- 1 | const nxPreset = require('@nx/jest/preset').default; 2 | 3 | module.exports = { 4 | ...nxPreset, 5 | collectCoverageFrom: [ 6 | '/src/**/*.ts', 7 | '!/src/{main.ts,test-setup.ts}', 8 | '!/src/**/mocks/**/*.ts', 9 | '!/src/**/tests/**/*.ts', 10 | ], 11 | coverageThreshold: { 12 | global: { 13 | branches: 100, 14 | functions: 0, 15 | lines: 0, 16 | statements: 0, 17 | }, 18 | }, 19 | }; 20 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/shared/components/tree/tree-node.interface.ts: -------------------------------------------------------------------------------- 1 | import { CommonSourceNode } from './common-source-node.interface'; 2 | 3 | export interface TreeNode { 4 | parentId: string; 5 | // even though node has name, we need it here so 6 | // we can edit it without impacting node.name 7 | // until we are ready to save. 8 | name: string; 9 | type?: string; 10 | node: CommonSourceNode; 11 | level: number; 12 | hasChildren: boolean; 13 | isExpanded?: boolean; 14 | } 15 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/shared/components/tree/tree-node.interface.ts: -------------------------------------------------------------------------------- 1 | import { CommonSourceNode } from './common-source-node.interface'; 2 | 3 | export interface TreeNode { 4 | parentId: string; 5 | // even though node has name, we need it here so 6 | // we can edit it without impacting node.name 7 | // until we are ready to save. 8 | name: string; 9 | type?: string; 10 | node: CommonSourceNode; 11 | level: number; 12 | hasChildren: boolean; 13 | isExpanded?: boolean; 14 | } 15 | -------------------------------------------------------------------------------- /apps/documentation/src/app/api/smart-signals/ng-doc.api.ts: -------------------------------------------------------------------------------- 1 | import { NgDocApi } from '@ng-doc/core'; 2 | import APICategory from '../ng-doc.category'; 3 | 4 | const api: NgDocApi = { 5 | title: 'Smart Signals API', 6 | category: APICategory, 7 | route: 'smart-signals-api', 8 | order: 2, 9 | scopes: [ 10 | { 11 | name: 'Smart Signals API', 12 | route: 'smart-signals-api', 13 | include: ['libs/smart-signals/src/index.ts'], 14 | }, 15 | ], 16 | }; 17 | 18 | export default api; 19 | -------------------------------------------------------------------------------- /libs/e2e-common/src/lib/locators/locate-add-menu-container.ts: -------------------------------------------------------------------------------- 1 | import { Locator, Page } from '@playwright/test'; 2 | 3 | /** 4 | * Locates the add menu container in the tree node. 5 | * 6 | * @param page The Playwright page object. 7 | * 8 | * @returns A locator for the add menu container. 9 | */ 10 | export async function locateAddMenuContainer(page: Page): Promise { 11 | const menu = page.locator('.mat-mdc-menu-panel'); 12 | await menu.waitFor({ state: 'visible' }); 13 | return menu; 14 | } 15 | -------------------------------------------------------------------------------- /prisma.config.ts: -------------------------------------------------------------------------------- 1 | import 'dotenv/config'; 2 | import { PrismaBetterSqlite3 } from '@prisma/adapter-better-sqlite3'; 3 | import { defineConfig, env } from 'prisma/config'; 4 | 5 | export const adapter = new PrismaBetterSqlite3({ 6 | url: process.env.DATABASE_URL ?? 'file:../database.db', 7 | }); 8 | 9 | export default defineConfig({ 10 | schema: 'prisma/schema.prisma', 11 | migrations: { 12 | path: 'prisma/migrations', 13 | }, 14 | datasource: { 15 | url: env('DATABASE_URL'), 16 | }, 17 | }); 18 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/routes/tree-standard/store/current-location/current-location.selector.ts: -------------------------------------------------------------------------------- 1 | import { createSelector } from '@ngrx/store'; 2 | 3 | import { selectTreeStandardState2 } from '../selectors/select-tree-standard-state2.selectors'; 4 | 5 | export const selectCurrentLocationId = createSelector( 6 | selectTreeStandardState2, 7 | function selectCurrentLocationIdFunction2(state2) { 8 | // this gets set by the location.effect.service 9 | return state2.currentLocation; 10 | }, 11 | ); 12 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/routes/tree-no-dirty/store/locations/selectors/select-location-entities.selectors.ts: -------------------------------------------------------------------------------- 1 | // jscpd:ignore-start 2 | // intentionally duplicated because it is for different state for demo purposes 3 | import { createSmartSelector } from '@smarttools/smart-ngrx'; 4 | 5 | import { Location } from '../../../../../shared/locations/location.interface'; 6 | 7 | export const selectLocationEntities = createSmartSelector( 8 | 'tree-no-dirty', 9 | 'locations', 10 | ); 11 | // jscpd:ignore-end 12 | -------------------------------------------------------------------------------- /libs/smart-core/src/types/parent-info.interface.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Used to define the array of parents for a child 3 | * this is used to help remove an id from the parent's 4 | * child field. 5 | */ 6 | export interface ParentInfo { 7 | /** 8 | * The name of the feature this parent is in. 9 | */ 10 | feature: string; 11 | /** 12 | * The name of the entity for this parent. 13 | */ 14 | entity: string; 15 | /** 16 | * List of parent row IDs that have the child. 17 | */ 18 | ids: string[]; 19 | } 20 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/routes/tree-no-dirty/store/current-location/current-location-no-dirty.reducer.ts: -------------------------------------------------------------------------------- 1 | import { createReducer, on } from '@ngrx/store'; 2 | 3 | import { currentLocationActions } from './current-location.actions'; 4 | 5 | const initialState = ''; 6 | 7 | export const currentLocationNoDirtyReducer = createReducer( 8 | initialState, 9 | on( 10 | currentLocationActions.set, 11 | function currentLocationNoDirtyReducerSetFunction(_, { id }) { 12 | return id; 13 | }, 14 | ), 15 | ); 16 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/routes/tree-no-refresh/store/locations/selectors/select-location-entities.selectors.ts: -------------------------------------------------------------------------------- 1 | // jscpd:ignore-start 2 | // intentionally duplicated because it is for different state for demo purposes 3 | import { createSmartSelector } from '@smarttools/smart-ngrx'; 4 | 5 | import { Location } from '../../../../../shared/locations/location.interface'; 6 | 7 | export const selectLocationEntities = createSmartSelector( 8 | 'tree-no-refresh', 9 | 'locations', 10 | ); 11 | // jscpd:ignore-end 12 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/routes/tree-standard/store/current-location/current-location-standard.reducer.ts: -------------------------------------------------------------------------------- 1 | import { createReducer, on } from '@ngrx/store'; 2 | 3 | import { currentLocationActions } from './current-location.actions'; 4 | 5 | const initialState = ''; 6 | 7 | export const currentLocationStandardReducer = createReducer( 8 | initialState, 9 | on( 10 | currentLocationActions.set, 11 | function currentLocationStandardReducerSetFunction(_, { id }) { 12 | return id; 13 | }, 14 | ), 15 | ); 16 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/routes/tree-no-remove/store/current-location/current-location-no-remove.reducer.ts: -------------------------------------------------------------------------------- 1 | import { createReducer, on } from '@ngrx/store'; 2 | 3 | import { currentLocationActions } from './current-location.actions'; 4 | 5 | const initialState = ''; 6 | 7 | export const currentLocationNoRemoveReducer = createReducer( 8 | initialState, 9 | on( 10 | currentLocationActions.set, 11 | function currentLocationNoRemoveReducerSetFunction(_, { id }) { 12 | return id; 13 | }, 14 | ), 15 | ); 16 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/routes/tree-no-remove/store/locations/selectors/select-location-entities.selectors.ts: -------------------------------------------------------------------------------- 1 | // jscpd:ignore-start 2 | // intentionally duplicated because it is for different state for demo purposes 3 | import { createSmartSelector } from '@smarttools/smart-ngrx'; 4 | 5 | import { Location } from '../../../../../shared/locations/location.interface'; 6 | 7 | export const selectLocationEntities = createSmartSelector( 8 | 'tree-no-remove', 9 | 'locations', 10 | ); 11 | 12 | // jscpd:ignore-end 13 | -------------------------------------------------------------------------------- /apps/server/src/app/orm/prisma.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { PrismaClient } from '@prisma/client'; 3 | 4 | import { adapter } from './prisma-adapter'; 5 | import { prismaServiceToken } from './prisma-service.token'; 6 | 7 | @Module({ 8 | imports: [], 9 | controllers: [], 10 | providers: [ 11 | { 12 | provide: prismaServiceToken, 13 | useValue: new PrismaClient({ adapter }), 14 | }, 15 | ], 16 | exports: [prismaServiceToken], 17 | }) 18 | export class PrismaModule {} 19 | -------------------------------------------------------------------------------- /libs/smart-core/src/error-handler/error-handler-registry.class.ts: -------------------------------------------------------------------------------- 1 | import { SmartErrorHandler } from '../types/smart-error-handler.interface'; 2 | import { errorHandler } from './error-handler.class'; 3 | 4 | class ErrorHandlerRegistry { 5 | private handler = errorHandler; 6 | 7 | register(handler: SmartErrorHandler): void { 8 | this.handler = handler; 9 | } 10 | 11 | getHandler(): SmartErrorHandler { 12 | return this.handler; 13 | } 14 | } 15 | 16 | export const errorHandlerRegistry = new ErrorHandlerRegistry(); 17 | -------------------------------------------------------------------------------- /libs/smart-core/src/types/smart-error-handler.interface.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Interface for an error handler that can be used to handle errors in SmartNgRX. 3 | * This should be implemented by any service that wants to handle errors 4 | * generated by SmartNgRX. 5 | */ 6 | export interface SmartErrorHandler { 7 | /** 8 | * Handles an error. 9 | * 10 | * @param message The message to display to the user 11 | * @param error The error that occurred 12 | */ 13 | handleError(message: string, error: unknown): void; 14 | } 15 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/routes/tree-no-refresh/store/current-location/current-location-no-refresh.reducer.ts: -------------------------------------------------------------------------------- 1 | import { createReducer, on } from '@ngrx/store'; 2 | 3 | import { currentLocationActions } from './current-location.actions'; 4 | 5 | const initialState = ''; 6 | 7 | export const currentLocationNoRefreshReducer = createReducer( 8 | initialState, 9 | on( 10 | currentLocationActions.set, 11 | function currentLocationNoRefreshReducerSetFunction(_, { id }) { 12 | return id; 13 | }, 14 | ), 15 | ); 16 | -------------------------------------------------------------------------------- /apps/documentation/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { NgDocThemeService } from '@ng-doc/app/services/theme'; 3 | 4 | @Component({ 5 | selector: 'smart-ngrx-root', 6 | templateUrl: './app.component.html', 7 | styleUrls: ['./app.component.css'], 8 | standalone: false, 9 | }) 10 | export class AppComponent { 11 | title = 'documentation'; 12 | constructor(protected readonly themeService: NgDocThemeService) {} 13 | setTheme(): void { 14 | this.themeService.set('auto'); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/shared/department-children/update-id-function.ts: -------------------------------------------------------------------------------- 1 | import { DepartmentChild } from './department-child.interface'; 2 | 3 | export function updateId( 4 | rows: DepartmentChild[], 5 | type: string, 6 | /* istanbul ignore next -- not a true condition */ 7 | idName = 'id', 8 | ): DepartmentChild[] { 9 | return rows.map(function updateIdMapRow(row) { 10 | const id = row[idName as keyof DepartmentChild] as string; 11 | return { 12 | ...row, 13 | id: `${type}:${id}`, 14 | }; 15 | }); 16 | } 17 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/shared/department-children/update-id-function.ts: -------------------------------------------------------------------------------- 1 | import { DepartmentChild } from './department-child.interface'; 2 | 3 | export function updateId( 4 | rows: DepartmentChild[], 5 | type: string, 6 | /* istanbul ignore next -- not a true condition */ 7 | idName = 'id', 8 | ): DepartmentChild[] { 9 | return rows.map(function updateIdMapRow(row) { 10 | const id = row[idName as keyof DepartmentChild] as string; 11 | return { 12 | ...row, 13 | id: `${type}:${id}`, 14 | }; 15 | }); 16 | } 17 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/shared/components/node-editor/node-editor.component.css: -------------------------------------------------------------------------------- 1 | :host { 2 | display: block; 3 | width: 100%; 4 | height: 100%; 5 | position: relative; 6 | background: transparent; 7 | } 8 | 9 | input { 10 | min-width: 64px; 11 | height: 100%; 12 | padding: 8px; 13 | line-height: 20px; 14 | margin: 0; 15 | border: none; 16 | background: transparent; 17 | color: inherit; 18 | font-size: 14px; 19 | font-weight: 500; 20 | font-stretch: 100%; 21 | letter-spacing: 1.25px; 22 | font-family: inherit; 23 | } 24 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/shared/components/node-editor/node-editor.component.css: -------------------------------------------------------------------------------- 1 | :host { 2 | display: block; 3 | width: 100%; 4 | height: 100%; 5 | position: relative; 6 | background: transparent; 7 | } 8 | 9 | input { 10 | min-width: 64px; 11 | height: 100%; 12 | padding: 8px; 13 | line-height: 20px; 14 | margin: 0; 15 | border: none; 16 | background: transparent; 17 | color: inherit; 18 | font-size: 14px; 19 | font-weight: 500; 20 | font-stretch: 100%; 21 | letter-spacing: 1.25px; 22 | font-family: inherit; 23 | } 24 | -------------------------------------------------------------------------------- /libs/e2e-common/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "module": "commonjs", 5 | "forceConsistentCasingInFileNames": true, 6 | "strict": true, 7 | "importHelpers": true, 8 | "noImplicitOverride": true, 9 | "noImplicitReturns": true, 10 | "noFallthroughCasesInSwitch": true, 11 | "noPropertyAccessFromIndexSignature": true 12 | }, 13 | "files": [], 14 | "include": [], 15 | "references": [ 16 | { 17 | "path": "./tsconfig.lib.json" 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: 'pnpm' # See documentation for possible values 9 | directory: '/' # Location of package manifests 10 | schedule: 11 | interval: 'weekly' 12 | -------------------------------------------------------------------------------- /libs/smart-core/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "declaration": true, 6 | "declarationMap": true, 7 | "inlineSources": true, 8 | "types": [], 9 | "moduleResolution": "bundler" 10 | }, 11 | "exclude": [ 12 | "src/**/*.spec.ts", 13 | "src/test-setup.ts", 14 | "jest.config.ts", 15 | "src/**/*.test.ts" 16 | ], 17 | "include": [ 18 | "src/**/*.ts", 19 | "src/types/smart-validated-entity-definition.type.ts" 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /apps/documentation/src/app/using-smart-ng-rx/mark-and-delete/global-mark-and-delete/index.md: -------------------------------------------------------------------------------- 1 | # Global Mark and Delete 2 | 3 | The `MarkAndDeleteInit` property can be set as part of global registration using the `provideSmartNgRX` call. 4 | 5 | If you don't pass anything in, the default values will be used. But each of the properties can be set to override the defaults. 6 | 7 | Keep in mind the rules. For example, if you don't specify a value for `markDirtyTime`, but you specify a value for `removeTime` that is less than 15 minutes, the `removeTime` will be set to 30 minutes. 8 | -------------------------------------------------------------------------------- /libs/smart-ngrx/src/types/smart-ngrx-row-base-selector.type.ts: -------------------------------------------------------------------------------- 1 | import { EntityState } from '@ngrx/entity'; 2 | import { DefaultProjectorFn, MemoizedSelector } from '@ngrx/store'; 3 | import { SmartNgRXRowBase } from '@smarttools/smart-core'; 4 | 5 | /** 6 | * This type allows us to deal with just the SmartNgRXRowBase part 7 | * of an entity. This is used internally. 8 | * 9 | * @see SmartNgRXRowBase 10 | */ 11 | export type SmartNgRXRowBaseSelector = 12 | MemoizedSelector, DefaultProjectorFn>>; 13 | -------------------------------------------------------------------------------- /apps/documentation/src/app/using-smart-signals/mark-and-delete/global-mark-and-delete/index.md: -------------------------------------------------------------------------------- 1 | # Global Mark and Delete 2 | 3 | The `MarkAndDeleteInit` property can be set as part of global registration using the `provideSmartNgRX` call. 4 | 5 | If you don't pass anything in, the default values will be used. But each of the properties can be set to override the defaults. 6 | 7 | Keep in mind the rules. For example, if you don't specify a value for `markDirtyTime`, but you specify a value for `removeTime` that is less than 15 minutes, the `removeTime` will be set to 30 minutes. 8 | -------------------------------------------------------------------------------- /libs/smart-ngrx/src/types/child-definition-classic.interface.ts: -------------------------------------------------------------------------------- 1 | import { BaseChildDefinition, SmartNgRXRowBase } from '@smarttools/smart-core'; 2 | 3 | import { SmartNgRXRowBaseSelector } from './smart-ngrx-row-base-selector.type'; 4 | 5 | /** 6 | * The definition of how to access the child data from a parent entity. 7 | */ 8 | export interface ChildDefinitionClassic< 9 | P extends SmartNgRXRowBase = SmartNgRXRowBase, 10 | T extends SmartNgRXRowBase = SmartNgRXRowBase, 11 | > extends BaseChildDefinition

{ 12 | childSelector: SmartNgRXRowBaseSelector; 13 | } 14 | -------------------------------------------------------------------------------- /.github/workflows/remove-issue-from-project-on-close.yml: -------------------------------------------------------------------------------- 1 | name: Remove issue from project 2 | on: 3 | issues: 4 | types: 5 | - closed 6 | 7 | jobs: 8 | remove-issue: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Delete Issue from Project 12 | uses: monry/actions-delete-issue-from-project@v2.0.1 13 | with: 14 | github-token: ${{ secrets.PROJECT_SECRET }} 15 | project-owner: 'DaveMBush' 16 | project-number: 1 17 | issue-repository: 'DaveMBush/SmartNgRX' 18 | issue-id: ${{ github.event.issue.node_id }} 19 | -------------------------------------------------------------------------------- /apps/documentation/src/assets/icons/github.svg: -------------------------------------------------------------------------------- 1 | 4 | 6 | 7 | -------------------------------------------------------------------------------- /.cursor/rules/no-done-in-specs.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | description: Prevent the use of the 'done()' callback in *.spec.ts files. Use async/await or Promises for asynchronous tests instead. This ensures more reliable, readable, and modern test code. 3 | globs: 4 | alwaysApply: false 5 | --- 6 | Rule: 7 | 8 | - Do not use the 'done' callback in any test or helper function in *.spec.ts files. 9 | - Prefer async/await or returning a Promise for handling asynchronous code in tests. 10 | - Refactor existing tests to remove 'done' and use async/await or Promises. 11 | - This applies to all test files matching *.spec.ts. 12 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/main.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable sonarjs/no-commented-code -- needed for future debugging */ 2 | //import { enableProdMode } from '@angular/core'; 3 | import { bootstrapApplication } from '@angular/platform-browser'; 4 | 5 | import { AppComponent } from './app/app.component'; 6 | import { appConfig } from './app/app.config'; 7 | 8 | // we only need this for localhost dev tools support 9 | 10 | //enableProdMode() 11 | 12 | function catchError(err: unknown): void { 13 | alert(JSON.stringify(err)); 14 | } 15 | 16 | bootstrapApplication(AppComponent, appConfig).catch(catchError); 17 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/main.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable sonarjs/no-commented-code -- needed for future debugging */ 2 | //import { enableProdMode } from '@angular/core'; 3 | import { bootstrapApplication } from '@angular/platform-browser'; 4 | 5 | import { AppComponent } from './app/app.component'; 6 | import { appConfig } from './app/app.config'; 7 | 8 | // we only need this for localhost dev tools support 9 | 10 | //enableProdMode() 11 | 12 | function catchError(err: unknown): void { 13 | alert(JSON.stringify(err)); 14 | } 15 | 16 | bootstrapApplication(AppComponent, appConfig).catch(catchError); 17 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/routes/tree-standard/store/locations/selectors/select-locations.selectors.ts: -------------------------------------------------------------------------------- 1 | // jscpd:ignore-start 2 | // intentionally duplicated. 3 | import { getTopChildRows } from '@smarttools/smart-ngrx'; 4 | 5 | import { Location } from '../../../../../shared/locations/location.interface'; 6 | import { Top } from '../../../../../shared/top/top.interface'; 7 | import { selectTopLocations } from '../../top/select-top-locations.selectors'; 8 | 9 | export const selectLocations = getTopChildRows( 10 | selectTopLocations, 11 | 'locations', 12 | ); 13 | // jscpd:ignore-end 14 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/routes/tree-no-dirty/store/locations/selectors/select-locations.selectors.ts: -------------------------------------------------------------------------------- 1 | // jscpd:ignore-start 2 | // intentionally duplicated. 3 | import { getTopChildRows } from '@smarttools/smart-signals'; 4 | 5 | import { Location } from '../../../../../shared/locations/location.interface'; 6 | import { Top } from '../../../../../shared/top/top.interface'; 7 | import { selectTopLocations } from '../../top/select-top-locations.selectors'; 8 | 9 | export const selectLocations = getTopChildRows( 10 | selectTopLocations, 11 | 'locations', 12 | ); 13 | // jscpd:ignore-end 14 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/routes/tree-no-refresh/store/locations/selectors/select-locations.selectors.ts: -------------------------------------------------------------------------------- 1 | // jscpd:ignore-start 2 | // intentionally duplicated. 3 | import { getTopChildRows } from '@smarttools/smart-signals'; 4 | 5 | import { Location } from '../../../../../shared/locations/location.interface'; 6 | import { Top } from '../../../../../shared/top/top.interface'; 7 | import { selectTopLocations } from '../../top/select-top-locations.selectors'; 8 | 9 | export const selectLocations = getTopChildRows( 10 | selectTopLocations, 11 | 'locations', 12 | ); 13 | // jscpd:ignore-end 14 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/routes/tree-no-remove/store/locations/selectors/select-locations.selectors.ts: -------------------------------------------------------------------------------- 1 | // jscpd:ignore-start 2 | // intentionally duplicated. 3 | import { getTopChildRows } from '@smarttools/smart-signals'; 4 | 5 | import { Location } from '../../../../../shared/locations/location.interface'; 6 | import { Top } from '../../../../../shared/top/top.interface'; 7 | import { selectTopLocations } from '../../top/select-top-locations.selectors'; 8 | 9 | export const selectLocations = getTopChildRows( 10 | selectTopLocations, 11 | 'locations', 12 | ); 13 | // jscpd:ignore-end 14 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/routes/tree-standard/store/locations/selectors/select-locations.selectors.ts: -------------------------------------------------------------------------------- 1 | // jscpd:ignore-start 2 | // intentionally duplicated. 3 | import { getTopChildRows } from '@smarttools/smart-signals'; 4 | 5 | import { Location } from '../../../../../shared/locations/location.interface'; 6 | import { Top } from '../../../../../shared/top/top.interface'; 7 | import { selectTopLocations } from '../../top/select-top-locations.selectors'; 8 | 9 | export const selectLocations = getTopChildRows( 10 | selectTopLocations, 11 | 'locations', 12 | ); 13 | // jscpd:ignore-end 14 | -------------------------------------------------------------------------------- /apps/server/src/main.ts: -------------------------------------------------------------------------------- 1 | import { Logger } from '@nestjs/common'; 2 | import { NestFactory } from '@nestjs/core'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | 6 | async function bootstrap(): Promise { 7 | const app = await NestFactory.create(AppModule); 8 | const globalPrefix = 'api'; 9 | app.setGlobalPrefix(globalPrefix); 10 | const port = process.env.PORT ?? 3000; 11 | await app.listen(port); 12 | Logger.log( 13 | `🚀 Application is running on: http://localhost:${port}/${globalPrefix}`, 14 | ); 15 | } 16 | 17 | bootstrap().catch((err) => { 18 | Logger.error(err); 19 | }); 20 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/routes/tree-no-dirty/store/tree-standard-state.interface.ts: -------------------------------------------------------------------------------- 1 | import { DepartmentChildEntity } from '../../../shared/entities/department-child-entity.type'; 2 | import { DepartmentEntity } from '../../../shared/entities/department-entity.type'; 3 | import { LocationEntity } from '../../../shared/entities/location-entity.type'; 4 | import { TopEntity } from '../../../shared/entities/top-entity.type'; 5 | 6 | export interface TreeStandardState { 7 | top: TopEntity; 8 | locations: LocationEntity; 9 | departments: DepartmentEntity; 10 | departmentChildren: DepartmentChildEntity; 11 | } 12 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/routes/tree-no-refresh/store/tree-standard-state.interface.ts: -------------------------------------------------------------------------------- 1 | import { DepartmentChildEntity } from '../../../shared/entities/department-child-entity.type'; 2 | import { DepartmentEntity } from '../../../shared/entities/department-entity.type'; 3 | import { LocationEntity } from '../../../shared/entities/location-entity.type'; 4 | import { TopEntity } from '../../../shared/entities/top-entity.type'; 5 | 6 | export interface TreeStandardState { 7 | top: TopEntity; 8 | locations: LocationEntity; 9 | departments: DepartmentEntity; 10 | departmentChildren: DepartmentChildEntity; 11 | } 12 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/routes/tree-no-remove/store/tree-standard-state.interface.ts: -------------------------------------------------------------------------------- 1 | import { DepartmentChildEntity } from '../../../shared/entities/department-child-entity.type'; 2 | import { DepartmentEntity } from '../../../shared/entities/department-entity.type'; 3 | import { LocationEntity } from '../../../shared/entities/location-entity.type'; 4 | import { TopEntity } from '../../../shared/entities/top-entity.type'; 5 | 6 | export interface TreeStandardState { 7 | top: TopEntity; 8 | locations: LocationEntity; 9 | departments: DepartmentEntity; 10 | departmentChildren: DepartmentChildEntity; 11 | } 12 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/routes/tree-standard/store/tree-standard-state.interface.ts: -------------------------------------------------------------------------------- 1 | import { DepartmentChildEntity } from '../../../shared/entities/department-child-entity.type'; 2 | import { DepartmentEntity } from '../../../shared/entities/department-entity.type'; 3 | import { LocationEntity } from '../../../shared/entities/location-entity.type'; 4 | import { TopEntity } from '../../../shared/entities/top-entity.type'; 5 | 6 | export interface TreeStandardState { 7 | top: TopEntity; 8 | locations: LocationEntity; 9 | departments: DepartmentEntity; 10 | departmentChildren: DepartmentChildEntity; 11 | } 12 | -------------------------------------------------------------------------------- /libs/smart-core/src/common/assert.function.spec.ts: -------------------------------------------------------------------------------- 1 | import { assert } from './assert.function'; 2 | 3 | describe('assert.function.ts', () => { 4 | describe('if the assert condition is true', () => { 5 | it('does not throw an exception', () => { 6 | expect(() => { 7 | assert(true, 'this should not throw'); 8 | }).not.toThrow(); 9 | }); 10 | }); 11 | describe('if the assert condition is false', () => { 12 | it('throws an exception', () => { 13 | expect(() => { 14 | assert(false, 'this should throw'); 15 | }).toThrow('Error: this should throw'); 16 | }); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/routes/tree-standard/store/top/standard-top-definition.const.ts: -------------------------------------------------------------------------------- 1 | import { SmartEntityDefinition } from '@smarttools/smart-ngrx'; 2 | 3 | import { Top } from '../../../../shared/top/top.interface'; 4 | import { topEffectsServiceToken } from '../../../../shared/top/top-effects.service-token'; 5 | 6 | export const standardTopDefinition: SmartEntityDefinition = { 7 | entityName: 'top', 8 | effectServiceToken: topEffectsServiceToken, 9 | isInitialRow: true, 10 | defaultRow: function defaultRowFunction(id) { 11 | return { 12 | id, 13 | locations: [], 14 | }; 15 | }, 16 | }; 17 | -------------------------------------------------------------------------------- /libs/e2e-common/src/lib/locators/locate-add-menu-item.ts: -------------------------------------------------------------------------------- 1 | import { Locator, Page } from '@playwright/test'; 2 | 3 | /** 4 | * Locates the add menu item in the tree node. 5 | * 6 | * @param page The Playwright page object. 7 | * @param label The label of the add menu item. 8 | * 9 | * @returns A locator for the add menu item. 10 | */ 11 | export async function locateAddMenuItem( 12 | page: Page, 13 | label: string, 14 | ): Promise { 15 | const locator = page.locator( 16 | `button.mat-mdc-menu-item[aria-label="${label}"]`, 17 | ); 18 | await locator.waitFor({ state: 'visible' }); 19 | return locator; 20 | } 21 | -------------------------------------------------------------------------------- /libs/smart-core/src/common/is-virtual-array-contents.function.ts: -------------------------------------------------------------------------------- 1 | import { VirtualArrayContents } from '../types/virtual-array-contents.interface'; 2 | 3 | /** 4 | * Type guard to check if an item is a VirtualArrayContents 5 | * 6 | * @param item The item to check 7 | * @returns True if the item is a VirtualArrayContents, false otherwise 8 | */ 9 | export function isVirtualArrayContents( 10 | item: unknown, 11 | ): item is VirtualArrayContents { 12 | return ( 13 | typeof item === 'object' && 14 | item !== null && 15 | 'indexes' in item && 16 | Array.isArray((item as VirtualArrayContents).indexes) 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/routes/tree-standard/store/top/standard-signals-top-definition.const.ts: -------------------------------------------------------------------------------- 1 | import { SmartEntityDefinition } from '@smarttools/smart-signals'; 2 | 3 | import { Top } from '../../../../shared/top/top.interface'; 4 | import { topEffectsServiceToken } from '../../../../shared/top/top-effects.service-token'; 5 | 6 | export const standardSignalsTopDefinition: SmartEntityDefinition = { 7 | entityName: 'top', 8 | effectServiceToken: topEffectsServiceToken, 9 | isInitialRow: true, 10 | defaultRow: function defaultRowFunction(id) { 11 | return { 12 | id, 13 | locations: [], 14 | }; 15 | }, 16 | }; 17 | -------------------------------------------------------------------------------- /libs/smart-core/src/common/for-next.function.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Allows us to iterate over an array using for/next instead of using 3 | * an iterator (forEach for example) which can be 10x slower. This provides 4 | * the same benefit as forEach but without the performance hit. 5 | * 6 | * @param array the array we want to iterate over 7 | * @param callback the callback function to call for each item in the array. 8 | */ 9 | export function forNext( 10 | array: T[], 11 | callback: (item: T, index: number, array: T[]) => void, 12 | ): void { 13 | for (let i = 0; i < array.length; i++) { 14 | callback(array[i], i, array); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/routes/tree-no-dirty/store/locations/selectors/select-locations.selectors.ts: -------------------------------------------------------------------------------- 1 | // jscpd:ignore-start 2 | // intentionally duplicated because it is for different state for demo purposes 3 | import { getTopChildRows } from '@smarttools/smart-ngrx'; 4 | 5 | import { Location } from '../../../../../shared/locations/location.interface'; 6 | import { Top } from '../../../../../shared/top/top.interface'; 7 | import { selectTopLocations } from '../../top/select-top-locations.selectors'; 8 | 9 | export const selectLocations = getTopChildRows( 10 | selectTopLocations, 11 | 'locations', 12 | ); 13 | // jscpd:ignore-end 14 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/routes/tree-no-refresh/store/locations/selectors/select-locations.selectors.ts: -------------------------------------------------------------------------------- 1 | // jscpd:ignore-start 2 | // intentionally duplicated because it is for different state for demo purposes 3 | import { getTopChildRows } from '@smarttools/smart-ngrx'; 4 | 5 | import { Location } from '../../../../../shared/locations/location.interface'; 6 | import { Top } from '../../../../../shared/top/top.interface'; 7 | import { selectTopLocations } from '../../top/select-top-locations.selectors'; 8 | 9 | export const selectLocations = getTopChildRows( 10 | selectTopLocations, 11 | 'locations', 12 | ); 13 | // jscpd:ignore-end 14 | -------------------------------------------------------------------------------- /libs/smart-signals/src/types/child-definition-signals.interface.ts: -------------------------------------------------------------------------------- 1 | import { Signal } from '@angular/core'; 2 | import { EntityState } from '@ngrx/entity'; 3 | import { BaseChildDefinition, SmartNgRXRowBase } from '@smarttools/smart-core'; 4 | 5 | /** 6 | * The definition of how to access the child data from a parent entity. 7 | */ 8 | export interface ChildDefinitionSignals< 9 | P extends SmartNgRXRowBase = SmartNgRXRowBase, 10 | T extends SmartNgRXRowBase = SmartNgRXRowBase, 11 | > extends BaseChildDefinition

{ 12 | /** 13 | * The selector to retrieve the child data from the store. 14 | */ 15 | childSelector: Signal>; 16 | } 17 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/routes/tree-no-remove/store/locations/selectors/select-locations.selectors.ts: -------------------------------------------------------------------------------- 1 | // jscpd:ignore-start 2 | // intentionally duplicated because it is for different state for demo purposes 3 | import { getTopChildRows } from '@smarttools/smart-ngrx'; 4 | 5 | import { Location } from '../../../../../shared/locations/location.interface'; 6 | import { Top } from '../../../../../shared/top/top.interface'; 7 | import { selectTopLocations } from '../../top/select-top-locations.selectors'; 8 | 9 | export const selectLocations = getTopChildRows( 10 | selectTopLocations, 11 | 'locations', 12 | ); 13 | 14 | // jscpd:ignore-end 15 | -------------------------------------------------------------------------------- /libs/smart-core/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@smarttools/smart-core", 3 | "version": "2.1.4", 4 | "publishConfig": { 5 | "access": "public" 6 | }, 7 | "license": "MIT", 8 | "homepage": "https://davembush.github.io/SmartNgRX", 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/DaveMBush/SmartNgRX" 12 | }, 13 | "peerDependencies": { 14 | "@ngrx/store": "^19.0.0", 15 | "@ngrx/entity": "^19.0.0" 16 | }, 17 | "dependencies": { 18 | "tslib": "^2.3.0" 19 | }, 20 | "keywords": [ 21 | "angular", 22 | "ngrx", 23 | "smart-ngrx", 24 | "smart-signals" 25 | ], 26 | "sideEffects": false 27 | } 28 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/shared/components/tree/tree.component.css: -------------------------------------------------------------------------------- 1 | :host { 2 | display: block; 3 | height: 100%; 4 | } 5 | 6 | .selected { 7 | background-color: #939191; 8 | width: 100%; 9 | } 10 | 11 | cdk-virtual-scroll-viewport { 12 | height: calc(100% - 78px); 13 | width: 100%; 14 | } 15 | 16 | .edit-button, 17 | .add-button, 18 | .delete-button { 19 | display: none; 20 | } 21 | 22 | .show { 23 | display: block; 24 | } 25 | 26 | mat-tree-node:hover .edit-button, 27 | mat-tree-node:hover .delete-button, 28 | mat-tree-node:hover .add-button { 29 | display: block; 30 | } 31 | 32 | .mat-mdc-form-field-subscript-wrapper { 33 | height: 0; 34 | } 35 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/shared/components/tree/tree.component.css: -------------------------------------------------------------------------------- 1 | :host { 2 | display: block; 3 | height: 100%; 4 | } 5 | 6 | .selected { 7 | background-color: #939191; 8 | width: 100%; 9 | } 10 | 11 | cdk-virtual-scroll-viewport { 12 | height: calc(100% - 78px); 13 | width: 100%; 14 | } 15 | 16 | .edit-button, 17 | .add-button, 18 | .delete-button { 19 | display: none; 20 | } 21 | 22 | .show { 23 | display: block; 24 | } 25 | 26 | mat-tree-node:hover .edit-button, 27 | mat-tree-node:hover .delete-button, 28 | mat-tree-node:hover .add-button { 29 | display: block; 30 | } 31 | 32 | .mat-mdc-form-field-subscript-wrapper { 33 | height: 0; 34 | } 35 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/routes/tree-no-dirty/store/top/select-top-locations.selectors.ts: -------------------------------------------------------------------------------- 1 | import { createSmartSelector } from '@smarttools/smart-ngrx'; 2 | 3 | import { selectLocationsDepartments } from '../locations/selectors/select-locations-departments.selectors'; 4 | import { selectTopEntities } from './select-top-entities.selectors'; 5 | 6 | export const selectTopLocations = createSmartSelector(selectTopEntities, [ 7 | { 8 | childFeature: 'tree-no-dirty', 9 | childEntity: 'locations', 10 | parentField: 'locations', 11 | parentFeature: 'tree-no-dirty', 12 | parentEntity: 'top', 13 | childSelector: selectLocationsDepartments, 14 | }, 15 | ]); 16 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/routes/tree-no-remove/store/top/select-top-locations.selectors.ts: -------------------------------------------------------------------------------- 1 | import { createSmartSelector } from '@smarttools/smart-ngrx'; 2 | 3 | import { selectLocationsDepartments } from '../locations/selectors/select-locations-departments.selectors'; 4 | import { selectTopEntities } from './select-top-entities.selector'; 5 | 6 | export const selectTopLocations = createSmartSelector(selectTopEntities, [ 7 | { 8 | childFeature: 'tree-no-remove', 9 | childEntity: 'locations', 10 | parentField: 'locations', 11 | parentFeature: 'tree-no-remove', 12 | parentEntity: 'top', 13 | childSelector: selectLocationsDepartments, 14 | }, 15 | ]); 16 | -------------------------------------------------------------------------------- /libs/smart-ngrx/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@smarttools/smart-ngrx", 3 | "version": "2.1.4", 4 | "publishConfig": { 5 | "access": "public" 6 | }, 7 | "license": "MIT", 8 | "homepage": "https://davembush.github.io/SmartNgRX", 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/DaveMBush/SmartNgRX" 12 | }, 13 | "peerDependencies": { 14 | "@ngrx/store": "^19.0.0", 15 | "@ngrx/entity": "^19.0.0" 16 | }, 17 | "dependencies": { 18 | "@smarttools/smart-core": "^2.1.4", 19 | "tslib": "^2.3.0" 20 | }, 21 | "keywords": [ 22 | "angular", 23 | "ngrx", 24 | "smart-ngrx" 25 | ], 26 | "sideEffects": false 27 | } 28 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/routes/tree-no-refresh/store/top/select-top-locations.selectors.ts: -------------------------------------------------------------------------------- 1 | import { createSmartSelector } from '@smarttools/smart-ngrx'; 2 | 3 | import { selectLocationsDepartments } from '../locations/selectors/select-locations-departments.selectors'; 4 | import { selectTopEntities } from './select-top-entities.selectors'; 5 | 6 | export const selectTopLocations = createSmartSelector(selectTopEntities, [ 7 | { 8 | childFeature: 'tree-no-refresh', 9 | childEntity: 'locations', 10 | parentField: 'locations', 11 | parentFeature: 'tree-no-refresh', 12 | parentEntity: 'top', 13 | childSelector: selectLocationsDepartments, 14 | }, 15 | ]); 16 | -------------------------------------------------------------------------------- /libs/smart-signals/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@smarttools/smart-signals", 3 | "version": "2.1.4", 4 | "publishConfig": { 5 | "access": "public" 6 | }, 7 | "license": "MIT", 8 | "homepage": "https://davembush.github.io/SmartNgRX", 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/DaveMBush/SmartNgRX" 12 | }, 13 | "peerDependencies": { 14 | "@ngrx/entity": "^19.0.0", 15 | "@ngrx/signals": "^19.0.0" 16 | }, 17 | "dependencies": { 18 | "@smarttools/smart-core": "^2.1.4", 19 | "tslib": "^2.3.0" 20 | }, 21 | "keywords": [ 22 | "angular", 23 | "ngrx", 24 | "smart-signals" 25 | ], 26 | "sideEffects": false 27 | } 28 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/routes/tree-no-dirty/store/top/no-dirty-top-definition.const.ts: -------------------------------------------------------------------------------- 1 | import { SmartEntityDefinition } from '@smarttools/smart-ngrx'; 2 | 3 | import { Top } from '../../../../shared/top/top.interface'; 4 | import { topEffectsServiceToken } from '../../../../shared/top/top-effects.service-token'; 5 | 6 | export const noDirtyTopDefinition: SmartEntityDefinition = { 7 | entityName: 'top', 8 | effectServiceToken: topEffectsServiceToken, 9 | markAndDelete: { 10 | markDirtyTime: -1, 11 | }, 12 | isInitialRow: true, 13 | defaultRow: function noDirtyTopDefaultRowFunction(id) { 14 | return { 15 | id, 16 | locations: [], 17 | }; 18 | }, 19 | }; 20 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/routes/tree-standard/store/locations/standard-locations-definition.ts: -------------------------------------------------------------------------------- 1 | import { SmartEntityDefinition } from '@smarttools/smart-ngrx'; 2 | 3 | import { Location } from '../../../../shared/locations/location.interface'; 4 | import { locationEffectsServiceToken } from '../../../../shared/locations/location-effects.service-token'; 5 | 6 | export const standardLocationsDefinition: SmartEntityDefinition = { 7 | entityName: 'locations', 8 | effectServiceToken: locationEffectsServiceToken, 9 | defaultRow: function standardLocationsDefaultRowFunction(id) { 10 | return { 11 | id, 12 | name: '', 13 | departments: [], 14 | }; 15 | }, 16 | }; 17 | -------------------------------------------------------------------------------- /libs/smart-core/src/smart-selector/new-row-registry.class.ts: -------------------------------------------------------------------------------- 1 | import { psi } from '../common/psi.const'; 2 | 3 | class NewRowRegistry { 4 | private newRows: Set = new Set(); 5 | registerNewRow(feature: string, entity: string, id: string): void { 6 | this.newRows.add(`${feature}${psi}${entity}${psi}${id}`); 7 | } 8 | 9 | isNewRow(feature: string, entity: string, id: string): boolean { 10 | return this.newRows.has(`${feature}${psi}${entity}${psi}${id}`); 11 | } 12 | 13 | remove(feature: string, entity: string, id: string): void { 14 | this.newRows.delete(`${feature}${psi}${entity}${psi}${id}`); 15 | } 16 | } 17 | 18 | export const newRowRegistry = new NewRowRegistry(); 19 | -------------------------------------------------------------------------------- /.jscpd.json: -------------------------------------------------------------------------------- 1 | { 2 | "silent": true, 3 | "threshold": "0", 4 | "mode": "weak", 5 | "pattern": "**/*.{js,ts,html,css,scss,json,md}", 6 | "//": "ignore spec files for now", 7 | "ignore": [ 8 | "**/playwright.config.ts", 9 | "**/common.test.ts", 10 | "**/eslint.config.js", 11 | "**/jest.config.ts", 12 | "**/*.spec.ts", 13 | "**/apps/server/src/assets/**", 14 | "**/documentation/**", 15 | "**/.eslintrc.json", 16 | "**/jest.config.js", 17 | "**/tsconfig.json", 18 | "**/tsconfig.lib.json", 19 | "**/node_modules/**", 20 | "**/{.*,coverage,dist,tmp}/**", 21 | "**/apps/demo-ngrx-classic/src/**", 22 | "**/apps/demo-ngrx-signals/src/**" 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /apps/documentation/src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

SmartNgRX Documentation

4 |
5 | 6 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /libs/smart-core/src/providers/resolve-remove-time.function.ts: -------------------------------------------------------------------------------- 1 | import { MarkAndDeleteInit } from '../types/mark-and-delete-init.interface'; 2 | 3 | /** 4 | * Used internally to resolve the remove time to ensure it is always 5 | * greater than the mark dirty time. 6 | * 7 | * @param init The mark and delete init object 8 | * @returns The correct remove time 9 | */ 10 | export function resolveRemoveTime(init: MarkAndDeleteInit): number { 11 | let removeTime = init.removeTime; 12 | if ( 13 | removeTime > 0 && 14 | removeTime < init.markDirtyTime && 15 | init.markDirtyTime > -1 16 | ) { 17 | removeTime = init.markDirtyTime * 2; // 30 minutes 18 | } 19 | return removeTime; 20 | } 21 | -------------------------------------------------------------------------------- /libs/smart-core/src/types/smart-validated-entity-definition.type.ts: -------------------------------------------------------------------------------- 1 | import { EntityAdapter } from '@ngrx/entity'; 2 | 3 | import { SmartEntityDefinition } from './smart-entity-definition.interface'; 4 | import { SmartNgRXRowBase } from './smart-ngrx-row-base.interface'; 5 | 6 | /** 7 | * We need a type that makes the entityAdapter field in SmartEntityDefinition 8 | * not optional once it has been provided by the code. This is that type. 9 | */ 10 | interface PublicEntityAdapter { 11 | entityAdapter: EntityAdapter; 12 | } 13 | 14 | export type SmartValidatedEntityDefinition = 15 | PublicEntityAdapter & SmartEntityDefinition; 16 | -------------------------------------------------------------------------------- /apps/documentation/src/app/using-smart-ng-rx/error-handling/index.md: -------------------------------------------------------------------------------- 1 | # Error Handling 2 | 3 | From time to time, SmartNgRX will catch errors and fail gracefully. But, you will probably still want to know they occurred. 4 | 5 | For this, we've created a service token that you can use to register your own class which we will use to notify your code of the error. 6 | 7 | The token you'll need to import is `smartErrorHandlerToken` and you'll want your class to implement `SmartErrorHandler` which has one method `handleError`. 8 | 9 | `handleError` takes two arguments: 10 | 11 | - `message`: A string that describes where the error originated 12 | - `error`: The error that occurred as caught by the exception handler 13 | -------------------------------------------------------------------------------- /apps/documentation/src/app/using-smart-signals/error-handling/index.md: -------------------------------------------------------------------------------- 1 | # Error Handling 2 | 3 | From time to time, SmartNgRX will catch errors and fail gracefully. But, you will probably still want to know they occurred. 4 | 5 | For this, we've created a service token that you can use to register your own class which we will use to notify your code of the error. 6 | 7 | The token you'll need to import is `smartErrorHandlerToken` and you'll want your class to implement `SmartErrorHandler` which has one method `handleError`. 8 | 9 | `handleError` takes two arguments: 10 | 11 | - `message`: A string that describes where the error originated 12 | - `error`: The error that occurred as caught by the exception handler 13 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/routes/tree-no-dirty/store/top/no-dirty-signals-top-definition.const.ts: -------------------------------------------------------------------------------- 1 | import { SmartEntityDefinition } from '@smarttools/smart-signals'; 2 | 3 | import { Top } from '../../../../shared/top/top.interface'; 4 | import { topEffectsServiceToken } from '../../../../shared/top/top-effects.service-token'; 5 | 6 | export const noDirtySignalsTopDefinition: SmartEntityDefinition = { 7 | entityName: 'top', 8 | effectServiceToken: topEffectsServiceToken, 9 | markAndDelete: { 10 | markDirtyTime: -1, 11 | }, 12 | isInitialRow: true, 13 | defaultRow: function noDirtyTopDefaultRowFunction(id) { 14 | return { 15 | id, 16 | locations: [], 17 | }; 18 | }, 19 | }; 20 | -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | # Environment variables declared in this file are automatically made available to Prisma. 2 | # See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema 3 | 4 | # Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB. 5 | # See the documentation for all the connection string options: https://pris.ly/d/connection-strings 6 | 7 | DATABASE_URL="file:../database.db" 8 | 9 | 10 | # Nx 18 enables using plugins to infer targets by default 11 | # This is disabled for existing workspaces to maintain compatibility 12 | # For more info, see: https://nx.dev/concepts/inferred-tasks 13 | NX_ADD_PLUGINS=false 14 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/e2e/helpers/get-store-state.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/naming-convention -- special use for e2e testing */ 2 | /* eslint-disable no-underscore-dangle -- special use for e2e testing */ 3 | 4 | import { Page } from '@playwright/test'; 5 | 6 | declare global { 7 | interface Window { 8 | __APP_STORE__: { 9 | getState(): Record; 10 | }; 11 | } 12 | } 13 | 14 | export async function getStoreState( 15 | page: Page, 16 | ): Promise> { 17 | return page.evaluate(async function getStoreStateEvaluate(): Promise< 18 | Record 19 | > { 20 | return Promise.resolve(window.__APP_STORE__.getState()); 21 | }); 22 | } 23 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/routes/tree-no-remove/store/top/no-remove-top-definition.const.ts: -------------------------------------------------------------------------------- 1 | import { SmartEntityDefinition } from '@smarttools/smart-ngrx'; 2 | 3 | import { Top } from '../../../../shared/top/top.interface'; 4 | import { topEffectsServiceToken } from '../../../../shared/top/top-effects.service-token'; 5 | 6 | export const noRemoveTopDefinition: SmartEntityDefinition = { 7 | entityName: 'top', 8 | effectServiceToken: topEffectsServiceToken, 9 | markAndDelete: { 10 | markDirtyTime: 2 * 60 * 1000, 11 | removeTime: 0, 12 | }, 13 | isInitialRow: true, 14 | defaultRow: function noRemoveTopDefaultRowFunction(id) { 15 | return { 16 | id, 17 | locations: [], 18 | }; 19 | }, 20 | }; 21 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/routes/tree-no-refresh/store/top/no-refresh-signals-top-definition.const.ts: -------------------------------------------------------------------------------- 1 | import { SmartEntityDefinition } from '@smarttools/smart-signals'; 2 | 3 | import { Top } from '../../../../shared/top/top.interface'; 4 | import { topEffectsServiceToken } from '../../../../shared/top/top-effects.service-token'; 5 | import { markAndDelete } from '../mark-and-delete-init'; 6 | 7 | export const noRefreshSignalsTopDefinition: SmartEntityDefinition = { 8 | entityName: 'top', 9 | effectServiceToken: topEffectsServiceToken, 10 | markAndDelete, 11 | isInitialRow: true, 12 | defaultRow: function noRefreshTopFunction(id) { 13 | return { 14 | id, 15 | locations: [], 16 | }; 17 | }, 18 | }; 19 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/routes/tree-standard/store/locations/standard-signals-locations-definition.ts: -------------------------------------------------------------------------------- 1 | import { SmartEntityDefinition } from '@smarttools/smart-signals'; 2 | 3 | import { Location } from '../../../../shared/locations/location.interface'; 4 | import { locationEffectsServiceToken } from '../../../../shared/locations/location-effects.service-token'; 5 | 6 | export const standardSignalsLocationsDefinition: SmartEntityDefinition = 7 | { 8 | entityName: 'locations', 9 | effectServiceToken: locationEffectsServiceToken, 10 | defaultRow: function standardLocationsDefaultRowFunction(id) { 11 | return { 12 | id, 13 | name: '', 14 | departments: [], 15 | }; 16 | }, 17 | }; 18 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/routes/tree-no-remove/store/department/select-departments-children.selectors.ts: -------------------------------------------------------------------------------- 1 | import { createSmartSelector } from '@smarttools/smart-ngrx'; 2 | 3 | import { selectDepartmentChildren } from '../department-children/select-department-children.selectors'; 4 | import { selectDepartments } from './select-department.selectors'; 5 | 6 | export const selectDepartmentsChildren = createSmartSelector( 7 | selectDepartments, 8 | [ 9 | { 10 | childFeature: 'tree-no-remove', 11 | childEntity: 'departmentChildren', 12 | parentFeature: 'tree-no-remove', 13 | parentEntity: 'departments', 14 | parentField: 'children', 15 | childSelector: selectDepartmentChildren, 16 | }, 17 | ], 18 | ); 19 | -------------------------------------------------------------------------------- /libs/e2e-common/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './lib/common.test'; 2 | export * from './lib/load-route'; 3 | export * from './lib/locators/locate-2nd-tree-node'; 4 | export * from './lib/locators/locate-add-button'; 5 | export * from './lib/locators/locate-add-menu-container'; 6 | export * from './lib/locators/locate-add-menu-item'; 7 | export * from './lib/locators/locate-delete-button'; 8 | export * from './lib/locators/locate-edit-button'; 9 | export * from './lib/locators/locate-edit-field'; 10 | export * from './lib/locators/locate-first-tree-node'; 11 | export * from './lib/locators/locate-home-link'; 12 | export * from './lib/locators/locate-options'; 13 | export * from './lib/locators/locate-tree-label'; 14 | export * from './lib/websocket.test'; 15 | -------------------------------------------------------------------------------- /.verdaccio/config.yml: -------------------------------------------------------------------------------- 1 | # path to a directory with all packages 2 | storage: ../tmp/local-registry/storage 3 | 4 | # a list of other known repositories we can talk to 5 | uplinks: 6 | npmjs: 7 | url: https://registry.npmjs.org/ 8 | maxage: 60m 9 | 10 | packages: 11 | '**': 12 | # give all users (including non-authenticated users) full access 13 | # because it is a local registry 14 | access: $all 15 | publish: $all 16 | unpublish: $all 17 | 18 | # if package is not available locally, proxy requests to npm registry 19 | proxy: npmjs 20 | 21 | # log settings 22 | log: 23 | type: stdout 24 | format: pretty 25 | level: warn 26 | 27 | publish: 28 | allow_offline: true # set offline to true to allow publish offline 29 | -------------------------------------------------------------------------------- /apps/demo-ngrx-signals/src/app/routes/tree-no-remove/store/top/no-remove-signals-top-definition.const.ts: -------------------------------------------------------------------------------- 1 | import { SmartEntityDefinition } from '@smarttools/smart-signals'; 2 | 3 | import { Top } from '../../../../shared/top/top.interface'; 4 | import { topEffectsServiceToken } from '../../../../shared/top/top-effects.service-token'; 5 | 6 | export const noRemoveSignalsTopDefinition: SmartEntityDefinition = { 7 | entityName: 'top', 8 | effectServiceToken: topEffectsServiceToken, 9 | isInitialRow: true, 10 | markAndDelete: { 11 | markDirtyTime: 2 * 60 * 1000, 12 | removeTime: 0, 13 | }, 14 | defaultRow: function noRemoveTopDefaultRowFunction(id) { 15 | return { 16 | id, 17 | locations: [], 18 | }; 19 | }, 20 | }; 21 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/routes/tree-no-dirty/store/locations/no-dirty-locations-definition.ts: -------------------------------------------------------------------------------- 1 | import { SmartEntityDefinition } from '@smarttools/smart-ngrx'; 2 | 3 | import { Location } from '../../../../shared/locations/location.interface'; 4 | import { locationEffectsServiceToken } from '../../../../shared/locations/location-effects.service-token'; 5 | 6 | export const noDirtyLocationsDefinition: SmartEntityDefinition = { 7 | entityName: 'locations', 8 | effectServiceToken: locationEffectsServiceToken, 9 | markAndDelete: { 10 | markDirtyTime: -1, 11 | }, 12 | defaultRow: function noDirtyLocationsDefaultRowFunction(id) { 13 | return { 14 | id, 15 | name: '', 16 | departments: [], 17 | }; 18 | }, 19 | }; 20 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/routes/tree-no-dirty/store/locations/selectors/select-locations-departments.selectors.ts: -------------------------------------------------------------------------------- 1 | import { createSmartSelector } from '@smarttools/smart-ngrx'; 2 | 3 | import { selectDepartmentsChildren } from '../../department/select-departments-children.selectors'; 4 | import { selectLocationEntities } from './select-location-entities.selectors'; 5 | 6 | export const selectLocationsDepartments = createSmartSelector( 7 | selectLocationEntities, 8 | [ 9 | { 10 | childFeature: 'tree-no-dirty', 11 | childEntity: 'departments', 12 | parentFeature: 'tree-no-dirty', 13 | parentEntity: 'locations', 14 | parentField: 'departments', 15 | childSelector: selectDepartmentsChildren, 16 | }, 17 | ], 18 | ); 19 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/routes/tree-no-dirty/store/department/select-departments-children.selectors.ts: -------------------------------------------------------------------------------- 1 | import { createSmartSelector } from '@smarttools/smart-ngrx'; 2 | 3 | import { selectDepartmentChildren } from '../department-children/department-child.selector'; 4 | import { selectDepartments } from './select-departments.selectors'; 5 | 6 | export const selectDepartmentsChildren = createSmartSelector( 7 | // parent table selector 8 | selectDepartments, 9 | [ 10 | { 11 | childFeature: 'tree-no-dirty', 12 | childEntity: 'departmentChildren', 13 | parentFeature: 'tree-no-dirty', 14 | parentEntity: 'departments', 15 | parentField: 'children', 16 | childSelector: selectDepartmentChildren, 17 | }, 18 | ], 19 | ); 20 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/routes/tree-no-refresh/store/locations/selectors/select-locations-departments.selectors.ts: -------------------------------------------------------------------------------- 1 | import { createSmartSelector } from '@smarttools/smart-ngrx'; 2 | 3 | import { selectDepartmentsChildren } from '../../department/select-departments-children.selectors'; 4 | import { selectLocationEntities } from './select-location-entities.selectors'; 5 | 6 | export const selectLocationsDepartments = createSmartSelector( 7 | selectLocationEntities, 8 | [ 9 | { 10 | childFeature: 'tree-no-refresh', 11 | childEntity: 'departments', 12 | parentFeature: 'tree-no-refresh', 13 | parentEntity: 'locations', 14 | parentField: 'departments', 15 | childSelector: selectDepartmentsChildren, 16 | }, 17 | ], 18 | ); 19 | -------------------------------------------------------------------------------- /apps/demo-ngrx-classic/src/app/routes/tree-no-remove/store/locations/selectors/select-locations-departments.selectors.ts: -------------------------------------------------------------------------------- 1 | import { createSmartSelector } from '@smarttools/smart-ngrx'; 2 | 3 | import { selectDepartmentsChildren } from '../../department/select-departments-children.selectors'; 4 | import { selectLocationEntities } from './select-location-entities.selectors'; 5 | 6 | export const selectLocationsDepartments = createSmartSelector( 7 | selectLocationEntities, 8 | [ 9 | { 10 | childFeature: 'tree-no-remove', 11 | childEntity: 'departments', 12 | parentFeature: 'tree-no-remove', 13 | parentEntity: 'locations', 14 | parentField: 'departments', 15 | childSelector: selectDepartmentsChildren, 16 | }, 17 | ], 18 | ); 19 | --------------------------------------------------------------------------------