@uni-design-system/uni-angular 0.1.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,24 +1,41 @@
1
- import type { StorybookConfig } from '@analogjs/storybook-angular';
2
- import { dirname } from "path"
3
- import { fileURLToPath } from "url"
1
+ import { dirname } from 'path';
2
+ import { fileURLToPath } from 'url';
3
+ import { mergeConfig } from 'vite';
4
+ import type { StorybookConfig as StorybookConfigBase } from '@storybook/angular';
5
+ import type { StorybookConfigVite } from '@storybook/builder-vite';
4
6
 
5
7
  function getAbsolutePath(value: string) {
6
- return dirname(fileURLToPath(import.meta.resolve(`${value}/package.json`)))
8
+ return dirname(fileURLToPath(import.meta.resolve(`${value}/package.json`)));
7
9
  }
8
10
 
11
+ type StorybookConfig = StorybookConfigBase & StorybookConfigVite;
12
+
9
13
  const config: StorybookConfig = {
10
- "stories": [
11
- "../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"
12
- ],
13
- "addons": [
14
- getAbsolutePath('@storybook/addon-a11y'),
15
- getAbsolutePath('@storybook/addon-docs')
16
- ],
17
- "framework": {
18
- // Switch this to the Analog framework to fix the 'undefined' error
19
- name: getAbsolutePath('@analogjs/storybook-angular'),
20
- options: {}
21
- }
14
+ stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
15
+ addons: [getAbsolutePath('@storybook/addon-a11y'), getAbsolutePath('@storybook/addon-docs')],
16
+ framework: {
17
+ name: getAbsolutePath('@storybook/angular') as '@storybook/angular',
18
+ options: {
19
+ builder: {
20
+ name: getAbsolutePath('@storybook/builder-vite') as '@storybook/builder-vite',
21
+ options: {},
22
+ },
23
+ },
24
+ },
25
+ staticDirs: ['../../../public'],
26
+ managerHead: (head) => `${head}<link rel="icon" href="/favicon.ico" />`,
27
+ async viteFinal(config) {
28
+ return mergeConfig(config, {
29
+ optimizeDeps: {
30
+ exclude: ['@angular/animations', '@angular/animations/browser'],
31
+ },
32
+ build: {
33
+ rollupOptions: {
34
+ external: ['@angular/animations', '@angular/animations/browser'],
35
+ },
36
+ },
37
+ });
38
+ },
22
39
  };
23
40
 
24
41
  export default config;
@@ -0,0 +1,24 @@
1
+ import { addons } from 'storybook/manager-api';
2
+ import { create } from 'storybook/theming';
3
+
4
+ const theme = create({
5
+ base: 'light',
6
+ brandTitle: 'UNI Design System',
7
+ brandUrl: 'https://uni-design-system.github.io/uni/docs/angular',
8
+ brandImage: 'uni-storybook-logo.png',
9
+
10
+ appBg: 'white',
11
+ });
12
+
13
+ addons.setConfig({
14
+ theme,
15
+ sidebar: {
16
+ // This filter excludes individual stories from the sidebar list,
17
+ // leaving only the MDX documentation pages visible.
18
+ filters: {
19
+ patterns: (item) => {
20
+ return item.type === 'docs';
21
+ },
22
+ },
23
+ },
24
+ });
@@ -1,14 +1,21 @@
1
- import type { Preview } from '@storybook/angular'
1
+ import { applicationConfig, type Preview } from '@storybook/angular';
2
+ import { UNI_THEMES } from '../src/lib/theming/theme.token';
3
+ import { UniThemes } from '@uni-design-system/uni-core';
2
4
 
3
5
  const preview: Preview = {
4
6
  parameters: {
5
7
  controls: {
6
8
  matchers: {
7
- color: /(background|color)$/i,
8
- date: /Date$/i,
9
+ color: /(background|color)$/i,
10
+ date: /Date$/i,
9
11
  },
10
12
  },
11
13
  },
14
+ decorators: [
15
+ applicationConfig({
16
+ providers: [{ provide: UNI_THEMES, useValue: UniThemes }],
17
+ }),
18
+ ],
12
19
  };
13
20
 
14
21
  export default preview;
@@ -4,7 +4,8 @@
4
4
  "moduleResolution": "bundler",
5
5
  "module": "ESNext",
6
6
  "esModuleInterop": true,
7
- "types": ["node"],
7
+ "types": ["node", "vite/client"],
8
+ "jsx": "react-jsx",
8
9
  "allowSyntheticDefaultImports": true,
9
10
  "resolveJsonModule": true,
10
11
  "experimentalDecorators": true,
@@ -12,10 +13,12 @@
12
13
  },
13
14
  "exclude": ["../src/test.ts", "../src/**/*.spec.ts"],
14
15
  "include": [
15
- "../src/**/*.ts", // Added: includes your actual components
16
- "../src/**/*.stories.ts", // Updated: specifically target TS stories
16
+ "../src/**/*.ts",
17
+ "../src/**/*.tsx",
18
+ "../src/**/*.stories.ts",
17
19
  "./preview.ts",
18
- "./main.ts" // Added: includes the config itself
20
+ "./main.ts",
21
+ "./vite.config.ts"
19
22
  ],
20
23
  "files": ["./typings.d.ts"]
21
24
  }
@@ -1,23 +1,25 @@
1
-
2
- $ pnpm ng-packagr -p ng-package.json
3
- Building Angular Package
4
- 
5
- ------------------------------------------------------------------------------
6
- Building entry point '@uni-design-system/uni-angular'
7
- ------------------------------------------------------------------------------
8
- [?2026h⠋ Compiling with Angular sources in partial compilation mode.[?2026l[?2026h⠙ Compiling with Angular sources in partial compilation mode.[?2026l[?2026h⠹ Compiling with Angular sources in partial compilation mode.[?2026l✔ Compiling with Angular sources in partial compilation mode.
9
- [?2026h⠋ Writing FESM and DTS bundles[?2026l✔ Writing FESM and DTS bundles
10
- [?2026h⠋ Copying assets[?2026l✔ Copying assets
11
- [?2026h⠋ Writing package manifest[?2026lℹ Removing scripts section in package.json as it's considered a potential security vulnerability.
12
- ℹ Removing devDependencies section in package.json.
13
- ✔ Writing package manifest
14
- ✔ Built @uni-design-system/uni-angular
15
- 
16
- ------------------------------------------------------------------------------
17
- Built Angular Package
18
- - from: /Users/george/git/gaenglish/uni/packages/angular
19
- - to: /Users/george/git/gaenglish/uni/packages/angular/dist
20
- ------------------------------------------------------------------------------
21
- 
22
- Build at: 2026-05-11T20:45:52.333Z - Time: 723ms
23
- 
1
+ $ pnpm ng-packagr -p ng-package.json
2
+ Building Angular Package
3
+
4
+ ------------------------------------------------------------------------------
5
+ Building entry point '@uni-design-system/uni-angular'
6
+ ------------------------------------------------------------------------------
7
+ - Compiling with Angular sources in partial compilation mode.
8
+ ✔ Compiling with Angular sources in partial compilation mode.
9
+ ✔ Generating FESM and DTS bundles
10
+ - Copying assets
11
+ ✔ Copying assets
12
+ - Writing package manifest
13
+ ℹ Removing scripts section in package.json as it's considered a potential security vulnerability.
14
+ ℹ Removing devDependencies section in package.json.
15
+ ✔ Writing package manifest
16
+ ✔ Built @uni-design-system/uni-angular
17
+
18
+ ------------------------------------------------------------------------------
19
+ Built Angular Package
20
+ - from: /home/runner/work/uni/uni/packages/angular
21
+ - to: /home/runner/work/uni/uni/packages/angular/dist
22
+ ------------------------------------------------------------------------------
23
+
24
+ Build at: 2026-05-19T14:44:38.737Z - Time: 2418ms
25
+
package/CHANGELOG.md ADDED
@@ -0,0 +1,19 @@
1
+ # @uni-design-system/uni-angular
2
+
3
+ ## 2.0.0
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [[`4a049de`](https://github.com/uni-design-system/uni/commit/4a049def689d56d6b6cc1d2da73c9facd93ed515)]:
8
+ - @uni-design-system/uni-core@1.1.0
9
+
10
+ ## 1.0.0
11
+
12
+ ### Major Changes
13
+
14
+ - [`7d18157`](https://github.com/uni-design-system/uni/commit/7d18157cb131098688b70286513643423c37c756) Thanks [@gaenglish](https://github.com/gaenglish)! - init release
15
+
16
+ ### Patch Changes
17
+
18
+ - Updated dependencies [[`7d18157`](https://github.com/uni-design-system/uni/commit/7d18157cb131098688b70286513643423c37c756)]:
19
+ - @uni-design-system/uni-core@1.0.0
@@ -4,8 +4,8 @@ import { CommonModule } from '@angular/common';
4
4
 
5
5
  class UniButtonComponent {
6
6
  label = 'Button';
7
- bgColor = "#000000";
8
- textColor = "#fff";
7
+ bgColor = '#000000';
8
+ textColor = '#fff';
9
9
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: UniButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
10
10
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.12", type: UniButtonComponent, isStandalone: true, selector: "uni-button", inputs: { label: "label" }, ngImport: i0, template: `
11
11
  <button [style.background-color]="bgColor" [style.color]="textColor">
@@ -1 +1 @@
1
- {"version":3,"file":"uni-design-system-uni-angular.mjs","sources":["../../src/lib/button.component.ts","../../src/uni-design-system-uni-angular.ts"],"sourcesContent":["import { Component, Input } from '@angular/core';\nimport { CommonModule } from '@angular/common'; // Still useful for pipes/directives\n\n@Component({\n selector: 'uni-button',\n standalone: true, // Optional but explicit\n imports: [CommonModule],\n template: `\n <button [style.background-color]=\"bgColor\" [style.color]=\"textColor\">\n {{ label }}\n </button>\n `,\n styles: [`\n button { padding: 12px 24px; border-radius: 4px; border: none; cursor: pointer; }\n `]\n})\nexport class UniButtonComponent {\n @Input() label: string = 'Button';\n bgColor = \"#000000\";\n textColor = \"#fff\";\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":";;;;MAgBa,kBAAkB,CAAA;IACpB,KAAK,GAAW,QAAQ;IACjC,OAAO,GAAG,SAAS;IACnB,SAAS,GAAG,MAAM;wGAHP,kBAAkB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAlB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,IAAA,EAAA,kBAAkB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,YAAA,EAAA,MAAA,EAAA,EAAA,KAAA,EAAA,OAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EATnB;;;;AAIT,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,MAAA,EAAA,CAAA,0EAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EALS,YAAY,EAAA,CAAA,EAAA,CAAA;;4FAUX,kBAAkB,EAAA,UAAA,EAAA,CAAA;kBAb9B,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,YAAY,cACV,IAAI,EAAA,OAAA,EACP,CAAC,YAAY,CAAC,EAAA,QAAA,EACb;;;;AAIT,EAAA,CAAA,EAAA,MAAA,EAAA,CAAA,0EAAA,CAAA,EAAA;;sBAMA;;;ACjBH;;AAEG;;;;"}
1
+ {"version":3,"file":"uni-design-system-uni-angular.mjs","sources":["../../src/lib/button.component.ts","../../src/uni-design-system-uni-angular.ts"],"sourcesContent":["import { Component, Input } from '@angular/core';\nimport { CommonModule } from '@angular/common'; // Still useful for pipes/directives\n\n@Component({\n selector: 'uni-button',\n standalone: true, // Optional but explicit\n imports: [CommonModule],\n template: `\n <button [style.background-color]=\"bgColor\" [style.color]=\"textColor\">\n {{ label }}\n </button>\n `,\n styles: [\n `\n button {\n padding: 12px 24px;\n border-radius: 4px;\n border: none;\n cursor: pointer;\n }\n `,\n ],\n})\nexport class UniButtonComponent {\n @Input() label: string = 'Button';\n bgColor = '#000000';\n textColor = '#fff';\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":";;;;MAuBa,kBAAkB,CAAA;IACpB,KAAK,GAAW,QAAQ;IACjC,OAAO,GAAG,SAAS;IACnB,SAAS,GAAG,MAAM;wGAHP,kBAAkB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAlB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,IAAA,EAAA,kBAAkB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,YAAA,EAAA,MAAA,EAAA,EAAA,KAAA,EAAA,OAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EAhBnB;;;;AAIT,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,MAAA,EAAA,CAAA,0EAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EALS,YAAY,EAAA,CAAA,EAAA,CAAA;;4FAiBX,kBAAkB,EAAA,UAAA,EAAA,CAAA;kBApB9B,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,YAAY,cACV,IAAI,EAAA,OAAA,EACP,CAAC,YAAY,CAAC,EAAA,QAAA,EACb;;;;AAIT,EAAA,CAAA,EAAA,MAAA,EAAA,CAAA,0EAAA,CAAA,EAAA;;sBAaA;;;ACxBH;;AAEG;;;;"}
package/package.json CHANGED
@@ -1,23 +1,24 @@
1
1
  {
2
2
  "name": "@uni-design-system/uni-angular",
3
- "version": "0.1.0",
4
- "private": false,
5
- "publishConfig": {
6
- "access": "public"
3
+ "version": "2.0.0",
4
+ "repository": {
5
+ "type": "git",
6
+ "url": "https://github.com/uni-design-system/uni",
7
+ "directory": "packages/angular"
7
8
  },
8
- "scripts": {
9
- "build": "pnpm ng-packagr -p ng-package.json",
10
- "storybook": "ng run @uni-design-system/uni-angular:storybook",
11
- "build-storybook": "ng run @uni-design-system/uni-angular:build-storybook",
12
- "release": "pnpm build && pnpm changeset publish"
9
+ "publishConfig": {
10
+ "access": "public",
11
+ "provenance": true
13
12
  },
13
+ "private": false,
14
14
  "peerDependencies": {
15
15
  "@angular/common": "21.2.12",
16
16
  "@angular/core": "21.2.12",
17
- "@uni-design-system/uni-core": "workspace:*"
17
+ "@emotion/css": "^11.0.0",
18
+ "@uni-design-system/uni-core": "1.1.0"
18
19
  },
19
20
  "devDependencies": {
20
- "@analogjs/storybook-angular": "^2.5.0",
21
+ "@analogjs/storybook-angular": "^2.5.1",
21
22
  "@analogjs/vite-plugin-angular": "^2.5.0",
22
23
  "@angular-devkit/architect": "^0.2102.10",
23
24
  "@angular-devkit/build-angular": "^21.2.10",
@@ -25,16 +26,25 @@
25
26
  "@angular/compiler": "21.2.12",
26
27
  "@angular/compiler-cli": "21.2.12",
27
28
  "@angular/platform-browser-dynamic": "21.2.12",
28
- "@storybook/addon-a11y": "^10.3.6",
29
- "@storybook/addon-docs": "^10.3.6",
30
- "@storybook/addon-interactions": "^8.6.14",
31
- "@storybook/angular": "^10.3.6",
32
- "@uni-design-system/tsconfig": "workspace:*",
29
+ "@emotion/css": "^11.0.0",
30
+ "@storybook/addon-a11y": "^10.4.0",
31
+ "@storybook/addon-docs": "^10.4.0",
32
+ "@storybook/angular": "^10.4.0",
33
+ "@storybook/builder-vite": "^10.4.0",
34
+ "@types/react": "^18.3.28",
33
35
  "ng-packagr": "^21.0.0",
34
- "storybook": "^10.3.6",
36
+ "react": "^18.2.0",
37
+ "storybook": "^10.4.0",
35
38
  "tslib": "^2.8.1",
36
39
  "typescript": "5.9.3",
37
40
  "vite": "^8.0.10",
38
- "vite-tsconfig-paths": "^6.1.1"
41
+ "vite-tsconfig-paths": "^6.1.1",
42
+ "@uni-design-system/tsconfig": "0.0.0"
43
+ },
44
+ "scripts": {
45
+ "build": "pnpm ng-packagr -p ng-package.json",
46
+ "storybook": "pnpm ng run @uni-design-system/uni-angular:storybook",
47
+ "build-storybook": "pnpm dlx @angular/cli run @uni-design-system/uni-angular:build-storybook",
48
+ "release": "pnpm build && pnpm changeset publish"
39
49
  }
40
- }
50
+ }
@@ -10,12 +10,19 @@ import { CommonModule } from '@angular/common'; // Still useful for pipes/direct
10
10
  {{ label }}
11
11
  </button>
12
12
  `,
13
- styles: [`
14
- button { padding: 12px 24px; border-radius: 4px; border: none; cursor: pointer; }
15
- `]
13
+ styles: [
14
+ `
15
+ button {
16
+ padding: 12px 24px;
17
+ border-radius: 4px;
18
+ border: none;
19
+ cursor: pointer;
20
+ }
21
+ `,
22
+ ],
16
23
  })
17
24
  export class UniButtonComponent {
18
25
  @Input() label: string = 'Button';
19
- bgColor = "#000000";
20
- textColor = "#fff";
26
+ bgColor = '#000000';
27
+ textColor = '#fff';
21
28
  }
@@ -0,0 +1,113 @@
1
+ import { computed, signal, Signal, WritableSignal } from '@angular/core';
2
+
3
+ export type SortDirection = 'asc' | 'desc' | 'indet';
4
+
5
+ export interface Sort<T> {
6
+ column?: keyof T;
7
+ direction: SortDirection;
8
+ }
9
+
10
+ export abstract class UniBaseDatasource<T> {
11
+ abstract records: Signal<T[]>;
12
+ abstract recordCount: Signal<number>;
13
+
14
+ selections = signal<T[]>([]);
15
+
16
+ sort: WritableSignal<Sort<T>> = signal({
17
+ column: undefined,
18
+ direction: 'indet',
19
+ });
20
+
21
+ sortColumn = computed(() => this.sort().column);
22
+ sortDirection = computed(() => this.sort().direction);
23
+
24
+ abstract pageIndex: Signal<number>;
25
+ abstract pageSize: Signal<number>;
26
+ abstract pageCount: Signal<number>;
27
+
28
+ pages = computed(() => Array.from({ length: this.pageCount() }, (_, index) => index + 1));
29
+
30
+ startIndex = computed(() => {
31
+ const pageSize = this.pageSize();
32
+ const pageIndex = this.pageIndex();
33
+ return pageSize > 0 ? pageIndex * pageSize : 0;
34
+ });
35
+
36
+ endIndex = computed(() => {
37
+ const pageSize = this.pageSize();
38
+ const total = this.recordCount();
39
+
40
+ // If unpaginated (pageSize = 0), return total count
41
+ if (pageSize === 0) {
42
+ return total;
43
+ }
44
+
45
+ const start = this.startIndex();
46
+ return Math.min(start + pageSize, total);
47
+ });
48
+
49
+ disablePrevious = computed(() => {
50
+ const pageSize = this.pageSize();
51
+ return pageSize === 0 ? true : this.pageIndex() === 0;
52
+ });
53
+
54
+ disableNext = computed(() => {
55
+ const pageSize = this.pageSize();
56
+ return pageSize === 0 ? true : this.pageIndex() + 1 >= this.pageCount();
57
+ });
58
+
59
+ truncatedPages = computed(() => {
60
+ const currentPage = this.pageIndex() + 1;
61
+ const displayRange = 3;
62
+ const totalPages = this.pageCount();
63
+
64
+ const pages: (number | string)[] = [];
65
+ const startPage = Math.max(1, currentPage - displayRange);
66
+ const endPage = Math.min(totalPages, currentPage + displayRange);
67
+
68
+ if (startPage > 1) {
69
+ pages.push(1);
70
+ if (startPage > 2) {
71
+ pages.push('...');
72
+ }
73
+ }
74
+
75
+ for (let i = startPage; i <= endPage; i++) {
76
+ pages.push(i);
77
+ }
78
+
79
+ if (endPage < totalPages) {
80
+ if (endPage < totalPages - 1) {
81
+ pages.push('...');
82
+ }
83
+ pages.push(totalPages);
84
+ }
85
+ return pages;
86
+ });
87
+
88
+ isSelected(row: T): boolean {
89
+ return this.selections().some((selection) => selection === row);
90
+ }
91
+
92
+ toggleSelection(row: T) {
93
+ this.selections.update((selections) => {
94
+ return selections.includes(row) ? selections.filter((i) => i !== row) : [...selections, row];
95
+ });
96
+ }
97
+
98
+ clearSelections() {
99
+ this.selections.set([]);
100
+ }
101
+
102
+ selectAll() {
103
+ this.selections.set([...this.records()]);
104
+ }
105
+
106
+ abstract sortRecords(sort: Sort<T>): void;
107
+ abstract firstPage(): void;
108
+ abstract nextPage(): void;
109
+ abstract previousPage(): void;
110
+ abstract lastPage(): void;
111
+ abstract jumpToPage(page: number): void;
112
+ abstract setPageSize(size: number): void;
113
+ }
@@ -0,0 +1,10 @@
1
+ import { UniBaseDatasource } from './base-datasource';
2
+ import { UniRecordDatasource } from './record-datasource';
3
+ import { UniServerSideDatasource } from './server-side-datasource';
4
+
5
+ export type UniDatasource<T> = UniBaseDatasource<T>;
6
+
7
+ export type { Sort, SortDirection } from './base-datasource';
8
+ export type { DataLoader, PageRequest, PageResponse } from './server-side-datasource';
9
+
10
+ export { UniBaseDatasource, UniRecordDatasource, UniServerSideDatasource };
@@ -0,0 +1,115 @@
1
+ import { computed, signal } from '@angular/core';
2
+ import { UniBaseDatasource, Sort } from './base-datasource';
3
+
4
+ export class UniRecordDatasource<T> extends UniBaseDatasource<T> {
5
+ private _pageNumber = signal(1);
6
+ private _pageSize = signal<number>(0); // 0 = unpaginated (show all)
7
+
8
+ initialRecords = signal<T[]>([]);
9
+ override recordCount = computed((): number => this.initialRecords().filter(this.filter()).length);
10
+
11
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
12
+ filter = signal<(value: T) => boolean>((value) => true);
13
+
14
+ override pageIndex = signal<number>(0);
15
+ override pageSize = computed(() => this._pageSize());
16
+
17
+ override pageCount = computed(() => {
18
+ const pageSize = this.pageSize();
19
+ return pageSize > 0 ? Math.ceil(this.recordCount() / pageSize) : 1;
20
+ });
21
+
22
+ override records = computed(() => {
23
+ const { column, direction } = this.sort();
24
+ const filter = this.filter();
25
+ const pageSize = this.pageSize();
26
+
27
+ let filteredRecords = this.initialRecords().filter(filter);
28
+
29
+ // Apply sorting if specified
30
+ if (column && direction !== 'indet') {
31
+ filteredRecords = [...filteredRecords].sort((a, b) => {
32
+ const valueA = a[column];
33
+ const valueB = b[column];
34
+
35
+ const isString = typeof valueA === 'string';
36
+ const isAsc = direction === 'asc';
37
+
38
+ return isString
39
+ ? stringCompare(valueA as string, valueB as string, isAsc)
40
+ : numberCompare(valueA as number, valueB as number, isAsc);
41
+ });
42
+ }
43
+
44
+ // Apply pagination only if pageSize > 0
45
+ if (pageSize > 0) {
46
+ return filteredRecords.slice(this.startIndex(), this.endIndex());
47
+ }
48
+
49
+ // Return all records (unpaginated)
50
+ return filteredRecords;
51
+ });
52
+
53
+ constructor(data: T[]) {
54
+ super();
55
+ this.initialRecords.set(data);
56
+ }
57
+
58
+ override sortRecords(sort: Sort<T>) {
59
+ this.sort.set(sort);
60
+ }
61
+
62
+ override firstPage() {
63
+ this.pageIndex.set(0);
64
+ }
65
+
66
+ override nextPage() {
67
+ if (this.pageIndex() < this.pageCount() - 1) {
68
+ this.pageIndex.update((i) => i + 1);
69
+ }
70
+ }
71
+
72
+ override previousPage() {
73
+ this.pageIndex.update((i) => (i > 1 ? i - 1 : 0));
74
+ }
75
+
76
+ override lastPage() {
77
+ this.pageIndex.set(this.pageCount() - 1);
78
+ }
79
+
80
+ override jumpToPage(page: number) {
81
+ const i = page - 1;
82
+ if (i >= 0 && i < this.pageCount()) {
83
+ this.pageIndex.set(i);
84
+ }
85
+ }
86
+
87
+ override setPageSize(size: number) {
88
+ this._pageSize.set(size);
89
+ if (size > 0) {
90
+ this._pageNumber.set(1);
91
+ this.pageIndex.set(0);
92
+ }
93
+ }
94
+
95
+ setFilter(filter: (value: T) => boolean) {
96
+ this.filter.set(filter);
97
+ }
98
+
99
+ clearFilter() {
100
+ this.filter.set(() => true);
101
+ }
102
+ }
103
+
104
+ function stringCompare(a = 'zzz', b = 'zzz', isAsc: boolean): number {
105
+ a = a.toString();
106
+ b = b.toString();
107
+
108
+ return isAsc
109
+ ? a.localeCompare(b, undefined, { sensitivity: 'base' })
110
+ : b.localeCompare(a, undefined, { sensitivity: 'base' });
111
+ }
112
+
113
+ function numberCompare(a = 0, b = 0, isAsc: boolean): number {
114
+ return isAsc ? a - b : b - a;
115
+ }