jb-grid 0.4.1 → 0.5.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.
Files changed (117) hide show
  1. package/README.md +138 -11
  2. package/package.json +5 -8
  3. package/react/README.md +207 -202
  4. package/react/dist/Content.d.ts +1 -2
  5. package/react/dist/Footer.d.ts +1 -2
  6. package/react/dist/Header.d.ts +3 -16
  7. package/react/dist/JBGrid.cjs.js +3631 -1
  8. package/react/dist/JBGrid.cjs.js.map +1 -1
  9. package/react/dist/JBGrid.d.ts +288 -26
  10. package/react/dist/JBGrid.js +3624 -1
  11. package/react/dist/JBGrid.js.map +1 -1
  12. package/react/dist/JBGrid.umd.js +3641 -1
  13. package/react/dist/JBGrid.umd.js.map +1 -1
  14. package/react/dist/JBGridData.d.ts +1 -5
  15. package/react/dist/JBGridViewModel.d.ts +28 -25
  16. package/react/dist/types.d.ts +17 -49
  17. package/react/lib/Components/Cell.tsx +17 -15
  18. package/react/lib/Components/ColumnHeader.tsx +44 -0
  19. package/react/lib/Components/ExpandToggle.tsx +14 -10
  20. package/react/lib/Components/FullscreenIcon.tsx +28 -0
  21. package/react/lib/Components/JBLoading.tsx +5 -4
  22. package/react/lib/Components/Pagination.tsx +1 -1
  23. package/react/lib/Components/PaginationInfo.tsx +71 -0
  24. package/react/lib/Components/RefreshIcon.tsx +11 -0
  25. package/react/lib/Components/Row.tsx +17 -15
  26. package/react/lib/Components/TableHeader.tsx +21 -0
  27. package/react/lib/Components/blob-loading.css +27 -23
  28. package/react/lib/Components/cell.css +7 -7
  29. package/react/lib/Components/content-error/ContentError.tsx +19 -16
  30. package/react/lib/Components/content-error/content-error.css +5 -11
  31. package/react/lib/Components/module-declaration.ts +61 -13
  32. package/react/lib/Content.tsx +23 -47
  33. package/react/lib/Footer.tsx +52 -79
  34. package/react/lib/Header.tsx +11 -24
  35. package/react/lib/JBGrid.tsx +129 -70
  36. package/react/lib/JBGridViewModel.ts +189 -452
  37. package/react/lib/footer.css +25 -55
  38. package/react/lib/header.css +20 -20
  39. package/react/lib/jb-grid.css +69 -55
  40. package/react/lib/types.ts +32 -129
  41. package/react/lib/variables.css +6 -0
  42. package/react/package.json +14 -10
  43. package/react/tsconfig.json +5 -4
  44. package/web-component/dist/index.cjs.js +559 -1
  45. package/web-component/dist/index.cjs.js.br +0 -0
  46. package/web-component/dist/index.cjs.js.gz +0 -0
  47. package/web-component/dist/index.cjs.js.map +1 -1
  48. package/web-component/dist/index.d.ts +161 -4
  49. package/web-component/dist/index.d.ts.map +1 -1
  50. package/web-component/dist/index.js +561 -1
  51. package/web-component/dist/index.js.br +0 -0
  52. package/web-component/dist/index.js.gz +0 -0
  53. package/web-component/dist/index.js.map +1 -1
  54. package/web-component/dist/index.umd.js +567 -1
  55. package/web-component/dist/index.umd.js.br +0 -0
  56. package/web-component/dist/index.umd.js.gz +0 -0
  57. package/web-component/dist/index.umd.js.map +1 -1
  58. package/web-component/dist/row/row.d.ts.map +1 -1
  59. package/web-component/dist/row/types.d.ts +3 -5
  60. package/web-component/dist/row/types.d.ts.map +1 -1
  61. package/web-component/dist/row/utils.d.ts +1 -2
  62. package/web-component/dist/row/utils.d.ts.map +1 -1
  63. package/web-component/dist/table-header/render.d.ts +2 -0
  64. package/web-component/dist/table-header/render.d.ts.map +1 -0
  65. package/web-component/dist/table-header/table-header.d.ts +9 -0
  66. package/web-component/dist/table-header/table-header.d.ts.map +1 -0
  67. package/web-component/dist/table-header/types.d.ts +4 -0
  68. package/web-component/dist/table-header/types.d.ts.map +1 -0
  69. package/web-component/dist/types.d.ts +6 -0
  70. package/web-component/dist/types.d.ts.map +1 -0
  71. package/web-component/dist/utils.d.ts +8 -0
  72. package/web-component/dist/utils.d.ts.map +1 -0
  73. package/web-component/lib/cell/cell.ts +10 -5
  74. package/web-component/lib/cell/style.css +2 -1
  75. package/web-component/lib/column-header/column-header.ts +117 -0
  76. package/web-component/lib/column-header/render.ts +14 -0
  77. package/web-component/lib/column-header/style.css +60 -0
  78. package/web-component/lib/column-header/types.ts +10 -0
  79. package/web-component/lib/fullscreen-icon/fullscreen-icon.ts +72 -0
  80. package/web-component/lib/fullscreen-icon/render.ts +23 -0
  81. package/web-component/lib/fullscreen-icon/style.css +44 -0
  82. package/web-component/lib/fullscreen-icon/types.ts +1 -0
  83. package/web-component/lib/i18n.ts +38 -0
  84. package/web-component/lib/index.ts +9 -4
  85. package/web-component/lib/pagination/README.md +4 -0
  86. package/web-component/lib/pagination/pagination.ts +25 -15
  87. package/web-component/lib/pagination/render.ts +13 -10
  88. package/web-component/lib/pagination/style.css +43 -31
  89. package/web-component/lib/pagination/types.ts +1 -1
  90. package/web-component/lib/pagination/variables.css +10 -0
  91. package/web-component/lib/pagination-info/pagination-info.ts +191 -0
  92. package/web-component/lib/pagination-info/render.ts +16 -0
  93. package/web-component/lib/pagination-info/style.css +47 -0
  94. package/web-component/lib/pagination-info/types.ts +12 -0
  95. package/web-component/lib/refresh-icon/refresh-icon.ts +79 -0
  96. package/web-component/lib/refresh-icon/render.ts +10 -0
  97. package/web-component/lib/refresh-icon/style.css +21 -0
  98. package/web-component/lib/row/render.ts +3 -3
  99. package/web-component/lib/row/row.ts +29 -15
  100. package/web-component/lib/row/style.css +30 -30
  101. package/web-component/lib/row/types.ts +7 -8
  102. package/web-component/lib/row/utils.ts +1 -40
  103. package/web-component/lib/row/variables.css +13 -0
  104. package/web-component/lib/table-header/render.ts +9 -0
  105. package/web-component/lib/table-header/style.css +29 -0
  106. package/web-component/lib/table-header/table-header.ts +50 -0
  107. package/web-component/lib/table-header/types.ts +4 -0
  108. package/web-component/lib/toggle/expand-toggle.ts +17 -13
  109. package/web-component/lib/toggle/render.ts +18 -13
  110. package/web-component/lib/toggle/style.css +18 -5
  111. package/web-component/lib/types.ts +6 -0
  112. package/web-component/lib/utils.ts +46 -0
  113. package/react/lib/JBGridBridgeExample.js +0 -90
  114. package/react/lib/JBGridData.ts +0 -51
  115. package/react/lib/global.d.ts +0 -15
  116. package/web-component/lib/global.d.ts +0 -15
  117. package/web-component/tsconfig.json +0 -17
@@ -0,0 +1,72 @@
1
+ import { renderHTML } from './render.js';
2
+ import CSS from './style.css';
3
+ import { registerDefaultVariables } from 'jb-core/theme';
4
+ import type { JBFullscreenIconState } from './types.js';
5
+ import { i18n } from "jb-core/i18n";
6
+ import { dictionary } from "../i18n";
7
+
8
+ export * from "./types.js";
9
+
10
+ export class JBFullscreenIconWebComponent extends HTMLElement {
11
+ #state: JBFullscreenIconState = "enter";
12
+ #internals?: ElementInternals;
13
+
14
+ /**
15
+ * Describes the action represented by the icon.
16
+ *
17
+ * - `"enter"` means activating the control will enter fullscreen mode.
18
+ * - `"exit"` means activating the control will exit fullscreen mode.
19
+ */
20
+ get state() {
21
+ return this.#state;
22
+ }
23
+
24
+ set state(value: JBFullscreenIconState) {
25
+ this.#state = value;
26
+ if (this.#internals) this.#internals.ariaLabel = dictionary.get(i18n, value === "exit" ? "exitFullscreen" : "enterFullscreen");
27
+ this.setAttribute("state", value);
28
+ }
29
+
30
+ static get observedAttributes() {
31
+ return ["state"];
32
+ }
33
+
34
+ constructor() {
35
+ super();
36
+ if (typeof this.attachInternals === "function") {
37
+ this.#internals = this.attachInternals();
38
+ this.#internals.role = "img";
39
+ }
40
+ this.#init();
41
+ }
42
+
43
+ #init() {
44
+ const shadowRoot = this.attachShadow({ mode: 'open', delegatesFocus: true, clonable: true, serializable: true });
45
+ registerDefaultVariables();
46
+ this.#render();
47
+ this.state = this.#normalizeState(this.getAttribute("state"));
48
+ }
49
+
50
+ #render() {
51
+ const html = `<style>${CSS}</style>\n${renderHTML()}`;
52
+ const element = document.createElement("template");
53
+ element.innerHTML = html;
54
+ this.shadowRoot!.appendChild(element.content.cloneNode(true));
55
+ }
56
+
57
+ attributeChangedCallback(name: string, _oldValue: string | null, newValue: string | null) {
58
+ if (name === "state") {
59
+ this.#state = this.#normalizeState(newValue);
60
+ if (this.#internals) this.#internals.ariaLabel = dictionary.get(i18n, this.#state === "exit" ? "exitFullscreen" : "enterFullscreen");
61
+ }
62
+ }
63
+
64
+ #normalizeState(value: string | null): JBFullscreenIconState {
65
+ return value === "exit" ? "exit" : "enter";
66
+ }
67
+ }
68
+
69
+ const myElementNotExists = !customElements.get('jb-fullscreen-icon');
70
+ if (myElementNotExists) {
71
+ window.customElements.define('jb-fullscreen-icon', JBFullscreenIconWebComponent);
72
+ }
@@ -0,0 +1,23 @@
1
+ export function renderHTML(): string {
2
+ return /* html */ `
3
+ <svg class="fullscreen-icon" x="0px" y="0px" viewBox="0 0 128 128" part="icon">
4
+ <title>Full Screen Button</title>
5
+ <g class="arrow arrow-top-start">
6
+ <path d="M47.979,41.795L26.466,20.292c-3.995-6.034,6.546-6.069,6.546-6.069h6.385c4.054-0.105,6.282-2.363,6.24-5.229 c-0.041-2.866-1.591-5.216-5.167-5.151H23.679C-0.4,2.575,0.342,20.363,0.755,24.486c0.02,0.208-0.015,0.375,0,0.502v0.025 l0.011,15.21c-0.029,2.556,1.211,5.834,4.501,6.265c3.329,0.434,6.025-2.22,6.06-4.774v-9.788v0.084 c-0.062-7.879,5.311-3.858,5.311-3.858l23.346,22.95c1.842,1.843,6.8,3.308,9.362,0.711C53.583,47.516,49.821,43.637,47.979,41.795 z"></path>
7
+ <path d="M0.755,26.555c0,0,0.024-0.099,0-0.277v-0.004V26.555z"></path>
8
+ </g>
9
+ <g class="arrow arrow-top-end">
10
+ <path d="M89.483,50.518l21.504-21.512c6.034-3.995,6.07,6.546,6.07,6.546v6.385c0.104,4.055,2.361,6.281,5.228,6.24 c2.867-0.04,5.217-1.59,5.152-5.167V26.219c1.266-24.08-16.521-23.337-20.645-22.924c-0.206,0.02-0.374-0.015-0.503,0h-0.024 l-15.21,0.011c-2.557-0.029-5.834,1.211-6.265,4.501c-0.436,3.329,2.219,6.025,4.773,6.06h9.789H99.27 c7.879-0.062,3.858,5.311,3.858,5.311L80.177,42.524c-1.842,1.842-3.309,6.801-0.711,9.363 C83.764,56.123,87.642,52.362,89.483,50.518z"></path>
11
+ <path d="M104.723,3.294c0,0,0.101,0.024,0.279,0h0.003H104.723z"></path>
12
+ </g>
13
+ <g class="arrow arrow-bottom-end">
14
+ <path d="M80.759,88.376l21.514,21.505c3.995,6.035-6.546,6.068-6.546,6.068H89.34c-4.054,0.106-6.282,2.364-6.24,5.23 c0.041,2.863,1.591,5.214,5.167,5.149h16.792c24.08,1.269,23.337-16.521,22.924-20.642c-0.02-0.21,0.017-0.377,0-0.503v-0.025 l-0.012-15.21c0.031-2.557-1.209-5.834-4.501-6.265c-3.327-0.436-6.023,2.219-6.06,4.772v9.79v-0.084 c0.062,7.879-5.312,3.856-5.312,3.856L88.753,79.068c-1.843-1.841-6.8-3.308-9.362-0.709 C75.153,82.656,78.916,86.534,80.759,88.376z"></path>
15
+ <path d="M127.982,103.617c0,0-0.022,0.098,0,0.276v0.006V103.617z"></path>
16
+ </g>
17
+ <g class="arrow arrow-bottom-start">
18
+ <path d="M43.481,79.65l-21.503,21.514c-6.035,3.995-6.07-6.547-6.07-6.547v-6.384c-0.104-4.054-2.362-6.283-5.229-6.241 c-2.865,0.041-5.215,1.592-5.151,5.168v16.792c-1.267,24.078,16.521,23.338,20.643,22.924c0.208-0.02,0.375,0.015,0.503,0h0.025 l15.21-0.011c2.557,0.028,5.833-1.213,6.264-4.501c0.436-3.33-2.219-6.026-4.773-6.06h-9.789h0.084 c-7.879,0.062-3.857-5.312-3.857-5.312l22.949-23.347c1.843-1.844,3.31-6.8,0.711-9.363C49.201,74.045,45.323,77.809,43.481,79.65z"></path>
19
+ <path d="M28.242,126.876c0,0-0.1-0.024-0.278,0h-0.004H28.242z"></path>
20
+ </g>
21
+ </svg>
22
+ `;
23
+ }
@@ -0,0 +1,44 @@
1
+ :host {
2
+ display: inline-flex;
3
+ align-items: center;
4
+ justify-content: center;
5
+ width: 1.5rem;
6
+ height: 1.5rem;
7
+ color: #7b7b7b;
8
+ }
9
+
10
+ :host(:hover) {
11
+ color: #2e3139;
12
+ }
13
+
14
+ .fullscreen-icon {
15
+ width: 100%;
16
+ height: 100%;
17
+ display: block;
18
+ fill: currentColor;
19
+ }
20
+
21
+ .arrow {
22
+ transition: transform 0.2s ease-out;
23
+ transform-box: view-box;
24
+ }
25
+
26
+ :host([state="exit"]) .arrow-top-start {
27
+ transform: rotate(180deg);
28
+ transform-origin: 1.875rem 1.875rem;
29
+ }
30
+
31
+ :host([state="exit"]) .arrow-top-end {
32
+ transform: rotate(180deg);
33
+ transform-origin: 6.25rem 1.875rem;
34
+ }
35
+
36
+ :host([state="exit"]) .arrow-bottom-end {
37
+ transform: rotate(180deg);
38
+ transform-origin: 6.25rem 6.25rem;
39
+ }
40
+
41
+ :host([state="exit"]) .arrow-bottom-start {
42
+ transform: rotate(180deg);
43
+ transform-origin: 1.875rem 6.25rem;
44
+ }
@@ -0,0 +1 @@
1
+ export type JBFullscreenIconState = "enter" | "exit";
@@ -0,0 +1,38 @@
1
+ import { JBDictionary } from "jb-core/i18n";
2
+
3
+ export type JBGridDictionary = {
4
+ toggleRowDetails: string;
5
+ refreshData: string;
6
+ enterFullscreen: string;
7
+ exitFullscreen: string;
8
+ firstPage: string;
9
+ previousPage: string;
10
+ nextPage: string;
11
+ lastPage: string;
12
+ page: (index: number) => string;
13
+ };
14
+
15
+ export const dictionary = new JBDictionary<JBGridDictionary>({
16
+ fa: {
17
+ toggleRowDetails: "نمایش یا پنهان کردن جزئیات ردیف",
18
+ refreshData: "به‌روزرسانی داده‌ها",
19
+ enterFullscreen: "ورود به حالت تمام‌صفحه",
20
+ exitFullscreen: "خروج از حالت تمام‌صفحه",
21
+ firstPage: "صفحه اول",
22
+ previousPage: "صفحه قبل",
23
+ nextPage: "صفحه بعد",
24
+ lastPage: "صفحه آخر",
25
+ page: (index) => `صفحه ${index}`,
26
+ },
27
+ en: {
28
+ toggleRowDetails: "Toggle row details",
29
+ refreshData: "Refresh data",
30
+ enterFullscreen: "Enter fullscreen",
31
+ exitFullscreen: "Exit fullscreen",
32
+ firstPage: "First page",
33
+ previousPage: "Previous page",
34
+ nextPage: "Next page",
35
+ lastPage: "Last page",
36
+ page: (index) => `Page ${index}`,
37
+ },
38
+ });
@@ -1,4 +1,9 @@
1
- export * from "./pagination/pagination.js";
2
- export * from "./row/row.js";
3
- export * from "./cell/cell.js";
4
- export * from "./toggle/expand-toggle.js";
1
+ export * from "./pagination/pagination.js";
2
+ export * from "./pagination-info/pagination-info.js";
3
+ export * from "./refresh-icon/refresh-icon.js";
4
+ export * from "./fullscreen-icon/fullscreen-icon.js";
5
+ export * from "./row/row.js";
6
+ export * from "./table-header/table-header.js";
7
+ export * from "./column-header/column-header.js";
8
+ export * from "./cell/cell.js";
9
+ export * from "./toggle/expand-toggle.js";
@@ -19,4 +19,8 @@ pagination.max = 10;
19
19
  ## CSS Variables
20
20
  | CSS variable name | description |
21
21
  | --- | --- |
22
+ | --jb-pagination-arrow-button-fill-color | Fill color of pagination arrow buttons. |
23
+ | --jb-pagination-arrow-button-fill-color-disabled | Fill color of disabled pagination arrow buttons. |
22
24
  | --jb-pagination-index-width | Width of each page index button. |
25
+ | --jb-pagination-page-index-color | Color of page index buttons. |
26
+ | --jb-pagination-page-index-color-current | Color of the current page index button. |
@@ -1,9 +1,11 @@
1
- import { renderHTML } from './render';
2
- import CSS from './style.css';
1
+ import { renderHTML } from './render';
2
+ import CSS from './style.css';
3
+ import VariablesCSS from './variables.css';
3
4
  import { registerDefaultVariables } from 'jb-core/theme';
4
5
  import type { JBPaginationElements, PageIndexDom } from './types.js';
5
6
  import { i18n } from "jb-core/i18n";
6
- import {enToFaDigits} from 'jb-core';
7
+ import {enToFaDigits} from 'jb-core';
8
+ import { dictionary } from "../i18n";
7
9
  export class JBPaginationWebComponent extends HTMLElement {
8
10
  #elements!: JBPaginationElements;
9
11
  #pageIndex: number = 1;
@@ -72,7 +74,7 @@ export class JBPaginationWebComponent extends HTMLElement {
72
74
  this.min = 1;
73
75
  }
74
76
  #render() {
75
- const html = `<style>${CSS}</style>\n${renderHTML()}`;
77
+ const html = `<style>${VariablesCSS} ${CSS}</style>\n${renderHTML()}`;
76
78
  const element = document.createElement("template");
77
79
  element.innerHTML = html;
78
80
  this.shadowRoot!.appendChild(element.content.cloneNode(true));
@@ -154,17 +156,21 @@ export class JBPaginationWebComponent extends HTMLElement {
154
156
  #createPageIndexElement(newIndex: number): PageIndexDom {
155
157
  //when we are out of bound we create empty page index
156
158
  const isEmpty = this.#min > newIndex || this.#max < newIndex;
157
- const elem = document.createElement('div') as PageIndexDom;
159
+ const elem = document.createElement('button') as PageIndexDom;
160
+ elem.type = "button";
161
+ elem.setAttribute("aria-label", dictionary.get(i18n, "page")(newIndex));
158
162
  elem.classList.add('page-index', isEmpty?'empty':'not-empty');
159
163
  elem.dataset.index = `${newIndex}`;
160
164
  elem.pageIndex = newIndex;
161
165
  elem.addEventListener("click", (e) => {
162
166
  this.#onPageIndexClick(elem);
163
167
  });
164
- if (isEmpty) {
165
- elem.isEmpty = true;
166
- } else {
167
- elem.isEmpty = false;
168
+ if (isEmpty) {
169
+ elem.isEmpty = true;
170
+ elem.disabled = true;
171
+ } else {
172
+ elem.isEmpty = false;
173
+ elem.disabled = false;
168
174
  elem.innerHTML = `${this.showPersianNumber?enToFaDigits(newIndex):newIndex}`;
169
175
  }
170
176
  return elem;
@@ -183,11 +189,15 @@ export class JBPaginationWebComponent extends HTMLElement {
183
189
  this.#updateActiveIndex(newIndex);
184
190
  shouldDispatch && this.#dispatchChangeEvent();
185
191
  }
186
- #updateActiveIndex(newIndex: number) {
187
- this.#elements.index.wrapper.querySelector(".current")?.classList.remove('current');
188
- this.#elements.index.list.forEach(x => {
189
- if (x.pageIndex == newIndex) {
190
- x.classList.add('current')
192
+ #updateActiveIndex(newIndex: number) {
193
+ const previousCurrent = this.#elements.index.wrapper.querySelector(".current");
194
+ previousCurrent?.classList.remove('current');
195
+ previousCurrent?.removeAttribute("aria-current");
196
+ this.#elements.index.list.forEach(x => {
197
+ x.tabIndex = x.pageIndex === newIndex ? 0 : -1;
198
+ if (x.pageIndex == newIndex) {
199
+ x.classList.add('current')
200
+ x.setAttribute("aria-current", "page");
191
201
  }
192
202
  })
193
203
  }
@@ -208,4 +218,4 @@ export class JBPaginationWebComponent extends HTMLElement {
208
218
  const myElementNotExists = !customElements.get('jb-pagination');
209
219
  if (myElementNotExists) {
210
220
  window.customElements.define('jb-pagination', JBPaginationWebComponent);
211
- }
221
+ }
@@ -1,22 +1,25 @@
1
- export function renderHTML(): string {
1
+ import { i18n } from "jb-core/i18n";
2
+ import { dictionary } from "../i18n";
3
+
4
+ export function renderHTML(): string {
2
5
  return /* html */ `
3
6
  <div class="jb-pagination-web-component">
4
7
  <section class="page-navigator">
5
- <button class="first-page arrow-btn">
6
- <svg width="100%" height="100%" viewBox="0 0 24 24" preserveAspectRatio="xMidYMid meet"><title>first page</title><path d="M7 6 v12 h2 v-12 h-2z M17.41 7.41L16 6l-6 6 6 6 1.41-1.41L12.83 12z"></path></svg>
8
+ <button class="first-page arrow-btn" type="button" aria-label="${dictionary.get(i18n, "firstPage")}">
9
+ <svg width="100%" height="100%" viewBox="0 0 24 24" preserveAspectRatio="xMidYMid meet" aria-hidden="true"><path d="M7 6 v12 h2 v-12 h-2z M17.41 7.41L16 6l-6 6 6 6 1.41-1.41L12.83 12z"></path></svg>
7
10
  </button>
8
- <button class="prev-page arrow-btn">
9
- <svg width="100%" height="100%" viewBox="0 0 24 24" preserveAspectRatio="xMidYMid meet"><title>prev page</title><path d="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z"></path></svg>
11
+ <button class="prev-page arrow-btn" type="button" aria-label="${dictionary.get(i18n, "previousPage")}">
12
+ <svg width="100%" height="100%" viewBox="0 0 24 24" preserveAspectRatio="xMidYMid meet" aria-hidden="true"><path d="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z"></path></svg>
10
13
  </button>
11
14
  <div class="page-index-wrapper">
12
15
  </div>
13
- <button class="next-page arrow-btn">
14
- <svg width="100%" height="100%" viewBox="0 0 24 24" preserveAspectRatio="xMidYMid meet"><title>previous page</title><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"></path></svg>
16
+ <button class="next-page arrow-btn" type="button" aria-label="${dictionary.get(i18n, "nextPage")}">
17
+ <svg width="100%" height="100%" viewBox="0 0 24 24" preserveAspectRatio="xMidYMid meet" aria-hidden="true"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"></path></svg>
15
18
  </button>
16
- <button class="last-page arrow-btn">
17
- <svg width="100%" height="100%" viewBox="0 0 24 24" preserveAspectRatio="xMidYMid meet"><title>last page</title><path d="M15 6 v12 h2 v-12 h-2z M8 6L6.59 7.41 11.17 12l-4.58 4.59L8 18l6-6z"></path></svg>
19
+ <button class="last-page arrow-btn" type="button" aria-label="${dictionary.get(i18n, "lastPage")}">
20
+ <svg width="100%" height="100%" viewBox="0 0 24 24" preserveAspectRatio="xMidYMid meet" aria-hidden="true"><path d="M15 6 v12 h2 v-12 h-2z M8 6L6.59 7.41 11.17 12l-4.58 4.59L8 18l6-6z"></path></svg>
18
21
  </button>
19
22
  </section>
20
23
  </div>
21
24
  `;
22
- }
25
+ }
@@ -1,9 +1,4 @@
1
- :host {
2
- /*width of each page index number button*/
3
- --jb-pagination-index-width: 3rem;
4
- }
5
-
6
- .jb-pagination-web-component {
1
+ .jb-pagination-web-component {
7
2
  display: flex;
8
3
  gap: 1rem;
9
4
  direction:ltr;
@@ -14,28 +9,36 @@
14
9
  justify-items: center;
15
10
  align-items: center;
16
11
 
17
- .arrow-btn {
18
- all: unset;
19
- cursor: pointer;
20
- height: 3rem;
21
- width: 3rem;
22
- fill: var(--jb-neutral-2);
23
- &:disabled {
24
- fill: var(--jb-neutral-7);
25
- }
26
- }
12
+ .arrow-btn {
13
+ all: unset;
14
+ box-sizing: border-box;
15
+ cursor: pointer;
16
+ height: 3rem;
17
+ width: 3rem;
18
+ fill: var(--arrow-button-fill-color);
19
+ &:focus-visible {
20
+ outline: 2px solid var(--jb-grid-focus-ring-color, Highlight);
21
+ outline-offset: 2px;
22
+ }
23
+ &:disabled {
24
+ fill: var(--arrow-button-fill-color-disabled);
25
+ cursor: default;
26
+ }
27
+ }
27
28
 
28
29
  .page-index-wrapper {
29
30
  position: relative;
30
- width: calc(var(--jb-pagination-index-width) * 3);
31
+ width: calc(var(--index-width) * 3);
31
32
  overflow: hidden;
32
33
  height: 100%;
33
34
 
34
- .page-index {
35
- font-size: 1rem;
36
- color: var(--jb-neutral-6);
35
+ .page-index {
36
+ all: unset;
37
+ box-sizing: border-box;
38
+ font-size: 1rem;
39
+ color: var(--page-index-color);
37
40
  position: absolute;
38
- width: var(--jb-pagination-index-width);
41
+ width: var(--index-width);
39
42
  height: 3rem;
40
43
  display: flex;
41
44
  justify-content: center;
@@ -43,12 +46,21 @@
43
46
  text-box-trim: trim-both;
44
47
  text-box-edge: ex alphabetic;
45
48
  transition: all 0.3s 0s ease;
46
- cursor: pointer;
49
+ cursor: pointer;
50
+
51
+ &:disabled {
52
+ cursor: default;
53
+ }
54
+
55
+ &:focus-visible {
56
+ outline: 2px solid var(--jb-grid-focus-ring-color, Highlight);
57
+ outline-offset: -2px;
58
+ }
47
59
 
48
- &.current {
49
- color: var(--jb-primary);
50
- transform: scale(120%);
51
- }
60
+ &.current {
61
+ color: var(--page-index-color-current);
62
+ transform: scale(120%);
63
+ }
52
64
 
53
65
  &.empty {
54
66
  cursor: default;
@@ -57,7 +69,7 @@
57
69
 
58
70
  .page-index:nth-child(1) {
59
71
  top: 0;
60
- left: calc(var(--jb-pagination-index-width) * -1);
72
+ left: calc(var(--index-width) * -1);
61
73
  transform: scale(10%);
62
74
  }
63
75
 
@@ -68,19 +80,19 @@
68
80
 
69
81
  .page-index:nth-child(3) {
70
82
  top: 0;
71
- left: calc(var(--jb-pagination-index-width));
83
+ left: calc(var(--index-width));
72
84
  }
73
85
 
74
86
  .page-index:nth-child(4) {
75
87
  top: 0;
76
- left: calc(var(--jb-pagination-index-width) * 2);
88
+ left: calc(var(--index-width) * 2);
77
89
  }
78
90
 
79
91
  .page-index:nth-child(5) {
80
92
  top: 0;
81
- left: calc(var(--jb-pagination-index-width) * 3);
93
+ left: calc(var(--index-width) * 3);
82
94
  transform: scale(10%);
83
95
  }
84
96
  }
85
97
  }
86
- }
98
+ }
@@ -12,4 +12,4 @@ export type JBPaginationElements = {
12
12
  }
13
13
  }
14
14
 
15
- export type PageIndexDom = HTMLDivElement & {pageIndex:number,isEmpty:boolean};
15
+ export type PageIndexDom = HTMLButtonElement & {pageIndex:number,isEmpty:boolean};
@@ -0,0 +1,10 @@
1
+ :host {
2
+ /* Theme/Color */
3
+ --arrow-button-fill-color: var(--jb-pagination-arrow-button-fill-color, var(--jb-neutral-2));
4
+ --arrow-button-fill-color-disabled: var(--jb-pagination-arrow-button-fill-color-disabled, var(--jb-neutral-7));
5
+ --page-index-color: var(--jb-pagination-page-index-color, var(--jb-neutral-6));
6
+ --page-index-color-current: var(--jb-pagination-page-index-color-current, var(--jb-primary));
7
+
8
+ /* Size */
9
+ --index-width: var(--jb-pagination-index-width, 3rem);
10
+ }
@@ -0,0 +1,191 @@
1
+ import { renderHTML } from './render.js';
2
+ import CSS from './style.css';
3
+ import { registerDefaultVariables } from 'jb-core/theme';
4
+ import { enToFaDigits } from 'jb-core';
5
+ import type { JBPaginationInfoElements, JBPaginationInfoPageSizeChangeEventDetail } from './types.js';
6
+
7
+ export * from "./types.js";
8
+
9
+ export class JBPaginationInfoWebComponent extends HTMLElement {
10
+ #elements!: JBPaginationInfoElements;
11
+ #pageSize = 20;
12
+ #pageSizes = [20, 30, 50, 100];
13
+ #startItemIndex = 0;
14
+ #endItemIndex = 0;
15
+ #totalItemsCount = 0;
16
+ #pageItemCountTitle = "";
17
+ #fromLabel = "";
18
+ #currentAvailableItemTitle = "";
19
+ #showPersianNumber = false;
20
+
21
+ get pageSize() {
22
+ return this.#pageSize;
23
+ }
24
+
25
+ set pageSize(value: number) {
26
+ this.#pageSize = value;
27
+ this.#syncSelectValue();
28
+ }
29
+
30
+ get pageSizes() {
31
+ return this.#pageSizes;
32
+ }
33
+
34
+ set pageSizes(value: number[]) {
35
+ this.#pageSizes = value;
36
+ this.#renderPageSizeOptions();
37
+ }
38
+
39
+ get startItemIndex() {
40
+ return this.#startItemIndex;
41
+ }
42
+
43
+ set startItemIndex(value: number) {
44
+ this.#startItemIndex = value;
45
+ this.#renderMetaData();
46
+ }
47
+
48
+ get endItemIndex() {
49
+ return this.#endItemIndex;
50
+ }
51
+
52
+ set endItemIndex(value: number) {
53
+ this.#endItemIndex = value;
54
+ this.#renderMetaData();
55
+ }
56
+
57
+ get totalItemsCount() {
58
+ return this.#totalItemsCount;
59
+ }
60
+
61
+ set totalItemsCount(value: number) {
62
+ this.#totalItemsCount = value;
63
+ this.#renderMetaData();
64
+ }
65
+
66
+ get pageItemCountTitle() {
67
+ return this.#pageItemCountTitle;
68
+ }
69
+
70
+ set pageItemCountTitle(value: string) {
71
+ this.#pageItemCountTitle = value;
72
+ this.#renderLabels();
73
+ }
74
+
75
+ get fromLabel() {
76
+ return this.#fromLabel;
77
+ }
78
+
79
+ set fromLabel(value: string) {
80
+ this.#fromLabel = value;
81
+ this.#renderLabels();
82
+ }
83
+
84
+ get currentAvailableItemTitle() {
85
+ return this.#currentAvailableItemTitle;
86
+ }
87
+
88
+ set currentAvailableItemTitle(value: string) {
89
+ this.#currentAvailableItemTitle = value;
90
+ this.#renderLabels();
91
+ }
92
+
93
+ get showPersianNumber() {
94
+ return this.#showPersianNumber;
95
+ }
96
+
97
+ set showPersianNumber(value: boolean) {
98
+ this.#showPersianNumber = value;
99
+ this.#renderPageSizeOptions();
100
+ this.#renderMetaData();
101
+ }
102
+
103
+ constructor() {
104
+ super();
105
+ this.#init();
106
+ }
107
+
108
+ #init() {
109
+ const shadowRoot = this.attachShadow({ mode: 'open', delegatesFocus: true, clonable: true, serializable: true });
110
+ registerDefaultVariables();
111
+ this.#render();
112
+ this.#elements = {
113
+ pageSizeSelect: shadowRoot.querySelector(".page-size-select")!,
114
+ pageSizeSection: shadowRoot.querySelector(".page-size-section")!,
115
+ startItemIndex: shadowRoot.querySelector(".start-item-index")!,
116
+ endItemIndex: shadowRoot.querySelector(".end-item-index")!,
117
+ fromLabel: shadowRoot.querySelector(".from-label")!,
118
+ totalItemsCount: shadowRoot.querySelector(".total-items-count")!,
119
+ };
120
+ this.#registerEventListener();
121
+ this.#renderPageSizeOptions();
122
+ this.#renderLabels();
123
+ this.#renderMetaData();
124
+ }
125
+
126
+ #render() {
127
+ const html = `<style>${CSS}</style>\n${renderHTML()}`;
128
+ const element = document.createElement("template");
129
+ element.innerHTML = html;
130
+ this.shadowRoot!.appendChild(element.content.cloneNode(true));
131
+ }
132
+
133
+ #registerEventListener() {
134
+ this.#elements.pageSizeSelect.addEventListener("change", () => {
135
+ const pageSize = Number(this.#elements.pageSizeSelect.value);
136
+ this.pageSize = pageSize;
137
+ this.dispatchEvent(new CustomEvent<JBPaginationInfoPageSizeChangeEventDetail>("page-size-change", {
138
+ bubbles: true,
139
+ composed: true,
140
+ detail: { pageSize }
141
+ }));
142
+ });
143
+ }
144
+
145
+ #renderPageSizeOptions() {
146
+ if (!this.#elements) {
147
+ return;
148
+ }
149
+ this.#elements.pageSizeSelect.replaceChildren(...this.#pageSizes.map((pageSize) => {
150
+ const option = document.createElement("option");
151
+ option.value = pageSize.toString();
152
+ option.textContent = this.#formatNumber(pageSize).toString();
153
+ return option;
154
+ }));
155
+ this.#syncSelectValue();
156
+ }
157
+
158
+ #syncSelectValue() {
159
+ if (!this.#elements) {
160
+ return;
161
+ }
162
+ this.#elements.pageSizeSelect.value = this.#pageSize.toString();
163
+ }
164
+
165
+ #renderLabels() {
166
+ if (!this.#elements) {
167
+ return;
168
+ }
169
+ this.#elements.pageSizeSection.title = this.#pageItemCountTitle;
170
+ this.#elements.fromLabel.textContent = ` ${this.#fromLabel}`;
171
+ this.#elements.totalItemsCount.title = this.#currentAvailableItemTitle;
172
+ }
173
+
174
+ #renderMetaData() {
175
+ if (!this.#elements) {
176
+ return;
177
+ }
178
+ this.#elements.startItemIndex.textContent = this.#formatNumber(this.#startItemIndex).toString();
179
+ this.#elements.endItemIndex.textContent = ` ${this.#formatNumber(this.#endItemIndex)} `;
180
+ this.#elements.totalItemsCount.textContent = ` ${this.#formatNumber(this.#totalItemsCount)} `;
181
+ }
182
+
183
+ #formatNumber(value: number) {
184
+ return this.#showPersianNumber ? enToFaDigits(value) : value;
185
+ }
186
+ }
187
+
188
+ const myElementNotExists = !customElements.get('jb-pagination-info');
189
+ if (myElementNotExists) {
190
+ window.customElements.define('jb-pagination-info', JBPaginationInfoWebComponent);
191
+ }
@@ -0,0 +1,16 @@
1
+ export function renderHTML(): string {
2
+ return /* html */ `
3
+ <section class="jb-pagination-info-web-component">
4
+ <section class="page-size-section">
5
+ <select class="page-size-select"></select>
6
+ </section>
7
+ <section class="items-information-section">
8
+ <span class="start-item-index"></span>
9
+ <span>-</span>
10
+ <span class="end-item-index"></span>
11
+ <span class="from-label"></span>
12
+ <span class="total-items-count"></span>
13
+ </section>
14
+ </section>
15
+ `;
16
+ }