@limetech/lime-elements 40.4.2 → 40.5.1
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.
- package/CHANGELOG.md +14 -0
- package/dist/cjs/component-UyqdiWGc.js +1576 -0
- package/dist/cjs/lime-elements.cjs.js +1 -1
- package/dist/cjs/limel-action-bar_3.cjs.entry.js +1 -1
- package/dist/cjs/limel-ai-avatar.cjs.entry.js +1 -1
- package/dist/cjs/{limel-breadcrumbs_8.cjs.entry.js → limel-breadcrumbs_6.cjs.entry.js} +188 -2164
- package/dist/cjs/limel-callout.cjs.entry.js +1 -1
- package/dist/cjs/limel-chart.cjs.entry.js +1 -1
- package/dist/cjs/limel-chip-set.cjs.entry.js +1 -1
- package/dist/cjs/limel-chip_2.cjs.entry.js +1 -1
- package/dist/cjs/limel-code-diff.cjs.entry.js +1 -1
- package/dist/cjs/limel-code-editor.cjs.entry.js +1 -1
- package/dist/cjs/limel-collapsible-section.cjs.entry.js +1 -1
- package/dist/cjs/limel-drag-handle.cjs.entry.js +1 -1
- package/dist/cjs/limel-email-viewer.cjs.entry.js +1 -1
- package/dist/cjs/limel-file-viewer.cjs.entry.js +1 -1
- package/dist/cjs/limel-file.cjs.entry.js +1 -1
- package/dist/cjs/limel-flatpickr-adapter.cjs.entry.js +1 -1
- package/dist/cjs/limel-helper-line_3.cjs.entry.js +454 -0
- package/dist/cjs/limel-hotkey_4.cjs.entry.js +26 -5
- package/dist/cjs/limel-list-item.cjs.entry.js +1 -1
- package/dist/cjs/limel-pagination-jump.cjs.entry.js +182 -0
- package/dist/cjs/limel-pagination.cjs.entry.js +165 -126
- package/dist/cjs/limel-picker.cjs.entry.js +1 -1
- package/dist/cjs/limel-profile-picture.cjs.entry.js +1 -1
- package/dist/cjs/limel-prosemirror-adapter.cjs.entry.js +1 -1
- package/dist/cjs/limel-slider.cjs.entry.js +1 -1
- package/dist/cjs/limel-snackbar.cjs.entry.js +1 -1
- package/dist/cjs/limel-table.cjs.entry.js +1 -1
- package/dist/cjs/limel-text-editor-link-menu.cjs.entry.js +1 -1
- package/dist/cjs/loader.cjs.js +1 -1
- package/dist/cjs/pagination.util-B5zzH8vg.js +135 -0
- package/dist/cjs/{translations-C5T2-_rH.js → translations-DFFpR6AZ.js} +32 -0
- package/dist/collection/collection-manifest.json +1 -0
- package/dist/collection/components/pagination/jump/pagination-jump.css +11 -0
- package/dist/collection/components/pagination/jump/pagination-jump.js +331 -0
- package/dist/collection/components/pagination/pagination.css +8 -4
- package/dist/collection/components/pagination/pagination.js +167 -10
- package/dist/collection/components/pagination/pagination.util.js +25 -8
- package/dist/collection/components/portal/portal.js +26 -5
- package/dist/collection/translations/da.js +4 -0
- package/dist/collection/translations/de.js +4 -0
- package/dist/collection/translations/en.js +4 -0
- package/dist/collection/translations/fi.js +4 -0
- package/dist/collection/translations/fr.js +4 -0
- package/dist/collection/translations/nl.js +4 -0
- package/dist/collection/translations/no.js +4 -0
- package/dist/collection/translations/sv.js +4 -0
- package/dist/esm/component-B2Vy44Z3.js +1570 -0
- package/dist/esm/lime-elements.js +1 -1
- package/dist/esm/limel-action-bar_3.entry.js +1 -1
- package/dist/esm/limel-ai-avatar.entry.js +1 -1
- package/dist/esm/{limel-breadcrumbs_8.entry.js → limel-breadcrumbs_6.entry.js} +3 -1977
- package/dist/esm/limel-callout.entry.js +1 -1
- package/dist/esm/limel-chart.entry.js +1 -1
- package/dist/esm/limel-chip-set.entry.js +1 -1
- package/dist/esm/limel-chip_2.entry.js +1 -1
- package/dist/esm/limel-code-diff.entry.js +1 -1
- package/dist/esm/limel-code-editor.entry.js +1 -1
- package/dist/esm/limel-collapsible-section.entry.js +1 -1
- package/dist/esm/limel-drag-handle.entry.js +1 -1
- package/dist/esm/limel-email-viewer.entry.js +1 -1
- package/dist/esm/limel-file-viewer.entry.js +1 -1
- package/dist/esm/limel-file.entry.js +1 -1
- package/dist/esm/limel-flatpickr-adapter.entry.js +1 -1
- package/dist/esm/limel-helper-line_3.entry.js +450 -0
- package/dist/esm/limel-hotkey_4.entry.js +26 -5
- package/dist/esm/limel-list-item.entry.js +1 -1
- package/dist/esm/limel-pagination-jump.entry.js +180 -0
- package/dist/esm/limel-pagination.entry.js +165 -126
- package/dist/esm/limel-picker.entry.js +1 -1
- package/dist/esm/limel-profile-picture.entry.js +1 -1
- package/dist/esm/limel-prosemirror-adapter.entry.js +1 -1
- package/dist/esm/limel-slider.entry.js +1 -1
- package/dist/esm/limel-snackbar.entry.js +1 -1
- package/dist/esm/limel-table.entry.js +1 -1
- package/dist/esm/limel-text-editor-link-menu.entry.js +1 -1
- package/dist/esm/loader.js +1 -1
- package/dist/esm/pagination.util-CSC2LJ7P.js +132 -0
- package/dist/esm/{translations-Bwm2y2E4.js → translations-DMtX4tTX.js} +32 -0
- package/dist/lime-elements/lime-elements.esm.js +1 -1
- package/dist/lime-elements/{p-2bbdac10.entry.js → p-11a96a8c.entry.js} +1 -1
- package/dist/lime-elements/p-1e93473a.entry.js +1 -0
- package/dist/lime-elements/{p-6b7ff8e0.entry.js → p-2daf0482.entry.js} +1 -1
- package/dist/lime-elements/{p-a41b5ff3.entry.js → p-2e568c56.entry.js} +1 -1
- package/dist/lime-elements/{p-de631fc3.entry.js → p-2f57c3fa.entry.js} +1 -1
- package/dist/lime-elements/p-3735111e.entry.js +1 -0
- package/dist/lime-elements/{p-c823054d.entry.js → p-452b9b01.entry.js} +1 -1
- package/dist/lime-elements/{p-f333f12d.entry.js → p-4855dc9b.entry.js} +1 -1
- package/dist/lime-elements/{p-a462aeb7.entry.js → p-4902fb3f.entry.js} +1 -1
- package/dist/lime-elements/p-507cc8a1.entry.js +159 -0
- package/dist/lime-elements/{p-dadd2e17.entry.js → p-531b6a64.entry.js} +1 -1
- package/dist/lime-elements/{p-b194a212.entry.js → p-5698e493.entry.js} +1 -1
- package/dist/lime-elements/{p-65a9adfc.entry.js → p-67f02b69.entry.js} +1 -1
- package/dist/lime-elements/{p-1162dd39.entry.js → p-867681aa.entry.js} +1 -1
- package/dist/lime-elements/{p-f72ee01b.entry.js → p-90df3e1b.entry.js} +1 -1
- package/dist/lime-elements/{p-22f85b99.entry.js → p-955292eb.entry.js} +1 -1
- package/dist/lime-elements/{p-c2ff132c.entry.js → p-9b631363.entry.js} +1 -1
- package/dist/lime-elements/{p-8af3e4a1.entry.js → p-9d3437ac.entry.js} +1 -1
- package/dist/lime-elements/p-B8hoCfhT.js +1 -0
- package/dist/lime-elements/p-BwJGg-WI.js +89 -0
- package/dist/lime-elements/p-CSC2LJ7P.js +1 -0
- package/dist/lime-elements/p-a1de9bbe.entry.js +1 -0
- package/dist/lime-elements/{p-b5484494.entry.js → p-acba5700.entry.js} +1 -1
- package/dist/lime-elements/{p-f6361b03.entry.js → p-b8c76487.entry.js} +1 -1
- package/dist/lime-elements/{p-ac9c5c88.entry.js → p-cafec335.entry.js} +1 -1
- package/dist/lime-elements/{p-48928b4f.entry.js → p-d146feda.entry.js} +1 -1
- package/dist/lime-elements/{p-532ea234.entry.js → p-deceda00.entry.js} +1 -1
- package/dist/lime-elements/{p-e508771f.entry.js → p-f04f7c8e.entry.js} +1 -1
- package/dist/lime-elements/{p-51dc5f0b.entry.js → p-f3c108c9.entry.js} +1 -1
- package/dist/lime-elements/{p-24041bbb.entry.js → p-fbcd99a3.entry.js} +1 -1
- package/dist/types/components/pagination/jump/pagination-jump.d.ts +102 -0
- package/dist/types/components/pagination/pagination.d.ts +81 -0
- package/dist/types/components/pagination/pagination.util.d.ts +12 -2
- package/dist/types/components/portal/portal.d.ts +3 -0
- package/dist/types/components.d.ts +145 -0
- package/dist/types/translations/da.d.ts +4 -0
- package/dist/types/translations/de.d.ts +4 -0
- package/dist/types/translations/en.d.ts +4 -0
- package/dist/types/translations/fi.d.ts +4 -0
- package/dist/types/translations/fr.d.ts +4 -0
- package/dist/types/translations/nl.d.ts +4 -0
- package/dist/types/translations/no.d.ts +4 -0
- package/dist/types/translations/sv.d.ts +4 -0
- package/package.json +1 -1
- package/dist/cjs/limel-helper-line.cjs.entry.js +0 -42
- package/dist/esm/limel-helper-line.entry.js +0 -40
- package/dist/lime-elements/p-497b7be1.entry.js +0 -248
- package/dist/lime-elements/p-DAFuhe5e.js +0 -1
- package/dist/lime-elements/p-b4988a9b.entry.js +0 -1
- package/dist/lime-elements/p-f81dc23a.entry.js +0 -1
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
import { h, Host, } from "@stencil/core";
|
|
2
|
+
import translate from "../../../global/translations";
|
|
3
|
+
import { ENTER } from "../../../util/keycodes";
|
|
4
|
+
import { pageFromInput } from "../pagination.util";
|
|
5
|
+
const FIRST_PAGE = 1;
|
|
6
|
+
const FALLBACK_LANGUAGE = 'en';
|
|
7
|
+
/**
|
|
8
|
+
* A field for going straight to a page, which `limel-pagination` puts inside
|
|
9
|
+
* the popover its `···` opens.
|
|
10
|
+
*
|
|
11
|
+
* It draws nothing until it is opened, so a pagination that is merely on
|
|
12
|
+
* screen pays for an empty element rather than a form. It lives in a
|
|
13
|
+
* component of its own because the popover carries its content into a shadow
|
|
14
|
+
* root elsewhere on the page, where a parent's stylesheet cannot reach it —
|
|
15
|
+
* a component brings its own.
|
|
16
|
+
*
|
|
17
|
+
* @private
|
|
18
|
+
* @exampleComponent limel-example-pagination-jump-basic
|
|
19
|
+
*/
|
|
20
|
+
export class PaginationJump {
|
|
21
|
+
constructor() {
|
|
22
|
+
/**
|
|
23
|
+
* Whether the field is being shown.
|
|
24
|
+
*/
|
|
25
|
+
this.open = false;
|
|
26
|
+
/**
|
|
27
|
+
* The page to start from, which is the one the user is on.
|
|
28
|
+
*/
|
|
29
|
+
this.page = FIRST_PAGE;
|
|
30
|
+
/**
|
|
31
|
+
* How many pages there are, which is as far as a jump can go.
|
|
32
|
+
*/
|
|
33
|
+
this.pageCount = FIRST_PAGE;
|
|
34
|
+
/**
|
|
35
|
+
* Set while a page is being fetched, so that another cannot be asked for.
|
|
36
|
+
*/
|
|
37
|
+
this.loading = false;
|
|
38
|
+
/**
|
|
39
|
+
* The language used for the labels and for the way numbers are written.
|
|
40
|
+
*/
|
|
41
|
+
this.language = 'en';
|
|
42
|
+
/** What the field shows: the page it opened with, then what is typed. */
|
|
43
|
+
this.value = '';
|
|
44
|
+
this.shouldFocusField = false;
|
|
45
|
+
/**
|
|
46
|
+
* Only to keep what is on screen honest. `limel-input-field` draws an
|
|
47
|
+
* unfocused number through its `value` prop, so a field left pinned to
|
|
48
|
+
* the page it opened with would show that again the moment the keyboard
|
|
49
|
+
* moved to the button, while the button submitted something else.
|
|
50
|
+
*
|
|
51
|
+
* @param event - the field reporting what it now holds
|
|
52
|
+
*/
|
|
53
|
+
this.handleChange = (event) => {
|
|
54
|
+
var _a;
|
|
55
|
+
event.stopPropagation();
|
|
56
|
+
this.value = String((_a = event.detail) !== null && _a !== void 0 ? _a : '');
|
|
57
|
+
};
|
|
58
|
+
this.handleKeyDown = (event) => {
|
|
59
|
+
if (event.key !== ENTER) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
event.preventDefault();
|
|
63
|
+
this.commit();
|
|
64
|
+
};
|
|
65
|
+
this.commit = () => {
|
|
66
|
+
const page = this.target;
|
|
67
|
+
if (page === null || this.loading) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
this.jump.emit(page);
|
|
71
|
+
};
|
|
72
|
+
this.setField = (element) => {
|
|
73
|
+
this.field = element;
|
|
74
|
+
};
|
|
75
|
+
this.translate = (key, params) => {
|
|
76
|
+
return translate.get(key, this.language, params);
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
componentWillLoad() {
|
|
80
|
+
this.handleOpenChange();
|
|
81
|
+
}
|
|
82
|
+
handleLanguageChange() {
|
|
83
|
+
this.numberFormat = undefined;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Opening starts the field again rather than leaving what was last typed,
|
|
87
|
+
* so it always offers the page the user is on now.
|
|
88
|
+
*/
|
|
89
|
+
handleOpenChange() {
|
|
90
|
+
if (!this.open) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
this.value = String(this.page);
|
|
94
|
+
this.shouldFocusField = true;
|
|
95
|
+
}
|
|
96
|
+
componentDidRender() {
|
|
97
|
+
if (!this.shouldFocusField) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
this.shouldFocusField = false;
|
|
101
|
+
this.focusField();
|
|
102
|
+
}
|
|
103
|
+
render() {
|
|
104
|
+
if (!this.open) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
return (h(Host, { onKeyDown: this.handleKeyDown }, h("limel-input-field", { type: "number", label: this.translate('pagination.page-number'), placeholder: this.pageRange, value: this.value, onChange: this.handleChange, min: FIRST_PAGE, max: this.pageCount, step: 1, ref: this.setField }), h("limel-button", { primary: true, label: this.translate('pagination.go'), disabled: this.loading, onClick: this.commit })));
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* The page that is in the field now, or `null` when it holds none.
|
|
111
|
+
*
|
|
112
|
+
* Read from the field itself rather than from its `change` event, which
|
|
113
|
+
* arrives on a debounce: a page typed and submitted inside that window
|
|
114
|
+
* would otherwise be submitted as whatever preceded it.
|
|
115
|
+
*/
|
|
116
|
+
get target() {
|
|
117
|
+
var _a, _b;
|
|
118
|
+
const typed = (_b = (_a = this.nativeField) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : this.value;
|
|
119
|
+
return pageFromInput(typed, this.pageCount);
|
|
120
|
+
}
|
|
121
|
+
/** `1–492`, so that being moved to 492 is a stated bound, not a surprise. */
|
|
122
|
+
get pageRange() {
|
|
123
|
+
return this.translate('pagination.page-range', {
|
|
124
|
+
first: this.formatNumber(FIRST_PAGE),
|
|
125
|
+
last: this.formatNumber(this.pageCount),
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
/** `limel-input-field` wraps a native field but does not expose it. */
|
|
129
|
+
get nativeField() {
|
|
130
|
+
var _a, _b;
|
|
131
|
+
return (_b = (_a = this.field) === null || _a === void 0 ? void 0 : _a.shadowRoot) === null || _b === void 0 ? void 0 : _b.querySelector('input');
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* The field delegates focus to the input it wraps, so focusing the host
|
|
135
|
+
* is enough. Selecting as well, so the page the user is on is both the
|
|
136
|
+
* starting value and out of the way of the first keystroke.
|
|
137
|
+
*/
|
|
138
|
+
focusField() {
|
|
139
|
+
const host = this.field;
|
|
140
|
+
host === null || host === void 0 ? void 0 : host.componentOnReady().then(() => {
|
|
141
|
+
// A frame later, so the popover has been put where it belongs
|
|
142
|
+
// before the field takes the keyboard.
|
|
143
|
+
requestAnimationFrame(() => {
|
|
144
|
+
var _a, _b;
|
|
145
|
+
host.focus();
|
|
146
|
+
(_b = (_a = host.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('input')) === null || _b === void 0 ? void 0 : _b.select();
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* The parent warns about a language it cannot use, so this one only has
|
|
152
|
+
* to keep drawing.
|
|
153
|
+
*
|
|
154
|
+
* @param value - the number to write out
|
|
155
|
+
* @returns the number in the current language
|
|
156
|
+
*/
|
|
157
|
+
formatNumber(value) {
|
|
158
|
+
var _a;
|
|
159
|
+
(_a = this.numberFormat) !== null && _a !== void 0 ? _a : (this.numberFormat = this.createNumberFormat());
|
|
160
|
+
return this.numberFormat.format(value);
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* The pagination hands down the language it settled on, so this only has
|
|
164
|
+
* to survive being handed one directly, as the example page does.
|
|
165
|
+
*
|
|
166
|
+
* @returns a formatter, in that language where it can be used
|
|
167
|
+
*/
|
|
168
|
+
createNumberFormat() {
|
|
169
|
+
try {
|
|
170
|
+
return new Intl.NumberFormat(this.language);
|
|
171
|
+
}
|
|
172
|
+
catch (_a) {
|
|
173
|
+
return new Intl.NumberFormat(FALLBACK_LANGUAGE);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
static get is() { return "limel-pagination-jump"; }
|
|
177
|
+
static get encapsulation() { return "shadow"; }
|
|
178
|
+
static get originalStyleUrls() {
|
|
179
|
+
return {
|
|
180
|
+
"$": ["pagination-jump.scss"]
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
static get styleUrls() {
|
|
184
|
+
return {
|
|
185
|
+
"$": ["pagination-jump.css"]
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
static get properties() {
|
|
189
|
+
return {
|
|
190
|
+
"open": {
|
|
191
|
+
"type": "boolean",
|
|
192
|
+
"mutable": false,
|
|
193
|
+
"complexType": {
|
|
194
|
+
"original": "boolean",
|
|
195
|
+
"resolved": "boolean",
|
|
196
|
+
"references": {}
|
|
197
|
+
},
|
|
198
|
+
"required": false,
|
|
199
|
+
"optional": false,
|
|
200
|
+
"docs": {
|
|
201
|
+
"tags": [],
|
|
202
|
+
"text": "Whether the field is being shown."
|
|
203
|
+
},
|
|
204
|
+
"getter": false,
|
|
205
|
+
"setter": false,
|
|
206
|
+
"reflect": true,
|
|
207
|
+
"attribute": "open",
|
|
208
|
+
"defaultValue": "false"
|
|
209
|
+
},
|
|
210
|
+
"page": {
|
|
211
|
+
"type": "number",
|
|
212
|
+
"mutable": false,
|
|
213
|
+
"complexType": {
|
|
214
|
+
"original": "number",
|
|
215
|
+
"resolved": "number",
|
|
216
|
+
"references": {}
|
|
217
|
+
},
|
|
218
|
+
"required": false,
|
|
219
|
+
"optional": false,
|
|
220
|
+
"docs": {
|
|
221
|
+
"tags": [],
|
|
222
|
+
"text": "The page to start from, which is the one the user is on."
|
|
223
|
+
},
|
|
224
|
+
"getter": false,
|
|
225
|
+
"setter": false,
|
|
226
|
+
"reflect": false,
|
|
227
|
+
"attribute": "page",
|
|
228
|
+
"defaultValue": "1"
|
|
229
|
+
},
|
|
230
|
+
"pageCount": {
|
|
231
|
+
"type": "number",
|
|
232
|
+
"mutable": false,
|
|
233
|
+
"complexType": {
|
|
234
|
+
"original": "number",
|
|
235
|
+
"resolved": "number",
|
|
236
|
+
"references": {}
|
|
237
|
+
},
|
|
238
|
+
"required": false,
|
|
239
|
+
"optional": false,
|
|
240
|
+
"docs": {
|
|
241
|
+
"tags": [],
|
|
242
|
+
"text": "How many pages there are, which is as far as a jump can go."
|
|
243
|
+
},
|
|
244
|
+
"getter": false,
|
|
245
|
+
"setter": false,
|
|
246
|
+
"reflect": false,
|
|
247
|
+
"attribute": "page-count",
|
|
248
|
+
"defaultValue": "1"
|
|
249
|
+
},
|
|
250
|
+
"loading": {
|
|
251
|
+
"type": "boolean",
|
|
252
|
+
"mutable": false,
|
|
253
|
+
"complexType": {
|
|
254
|
+
"original": "boolean",
|
|
255
|
+
"resolved": "boolean",
|
|
256
|
+
"references": {}
|
|
257
|
+
},
|
|
258
|
+
"required": false,
|
|
259
|
+
"optional": false,
|
|
260
|
+
"docs": {
|
|
261
|
+
"tags": [],
|
|
262
|
+
"text": "Set while a page is being fetched, so that another cannot be asked for."
|
|
263
|
+
},
|
|
264
|
+
"getter": false,
|
|
265
|
+
"setter": false,
|
|
266
|
+
"reflect": false,
|
|
267
|
+
"attribute": "loading",
|
|
268
|
+
"defaultValue": "false"
|
|
269
|
+
},
|
|
270
|
+
"language": {
|
|
271
|
+
"type": "string",
|
|
272
|
+
"mutable": false,
|
|
273
|
+
"complexType": {
|
|
274
|
+
"original": "Languages",
|
|
275
|
+
"resolved": "\"da\" | \"de\" | \"en\" | \"fi\" | \"fr\" | \"nb\" | \"nl\" | \"no\" | \"sv\"",
|
|
276
|
+
"references": {
|
|
277
|
+
"Languages": {
|
|
278
|
+
"location": "import",
|
|
279
|
+
"path": "../../date-picker/date.types",
|
|
280
|
+
"id": "src/components/date-picker/date.types.ts::Languages",
|
|
281
|
+
"referenceLocation": "Languages"
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
},
|
|
285
|
+
"required": false,
|
|
286
|
+
"optional": false,
|
|
287
|
+
"docs": {
|
|
288
|
+
"tags": [],
|
|
289
|
+
"text": "The language used for the labels and for the way numbers are written."
|
|
290
|
+
},
|
|
291
|
+
"getter": false,
|
|
292
|
+
"setter": false,
|
|
293
|
+
"reflect": false,
|
|
294
|
+
"attribute": "language",
|
|
295
|
+
"defaultValue": "'en'"
|
|
296
|
+
}
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
static get states() {
|
|
300
|
+
return {
|
|
301
|
+
"value": {}
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
static get events() {
|
|
305
|
+
return [{
|
|
306
|
+
"method": "jump",
|
|
307
|
+
"name": "jump",
|
|
308
|
+
"bubbles": true,
|
|
309
|
+
"cancelable": true,
|
|
310
|
+
"composed": true,
|
|
311
|
+
"docs": {
|
|
312
|
+
"tags": [],
|
|
313
|
+
"text": "Asks for the page that was typed, already brought within the set."
|
|
314
|
+
},
|
|
315
|
+
"complexType": {
|
|
316
|
+
"original": "number",
|
|
317
|
+
"resolved": "number",
|
|
318
|
+
"references": {}
|
|
319
|
+
}
|
|
320
|
+
}];
|
|
321
|
+
}
|
|
322
|
+
static get watchers() {
|
|
323
|
+
return [{
|
|
324
|
+
"propName": "language",
|
|
325
|
+
"methodName": "handleLanguageChange"
|
|
326
|
+
}, {
|
|
327
|
+
"propName": "open",
|
|
328
|
+
"methodName": "handleOpenChange"
|
|
329
|
+
}];
|
|
330
|
+
}
|
|
331
|
+
}
|
|
@@ -186,14 +186,18 @@ button limel-spinner {
|
|
|
186
186
|
pointer-events: none;
|
|
187
187
|
}
|
|
188
188
|
|
|
189
|
-
|
|
189
|
+
limel-popover {
|
|
190
190
|
display: inline-flex;
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
.gap {
|
|
194
194
|
width: 1.75rem;
|
|
195
|
+
padding: 0;
|
|
195
196
|
color: rgb(var(--contrast-1100));
|
|
196
197
|
}
|
|
198
|
+
.gap[aria-expanded=true] {
|
|
199
|
+
box-shadow: var(--button-shadow-inset-pressed) !important;
|
|
200
|
+
}
|
|
197
201
|
|
|
198
202
|
.arrow span {
|
|
199
203
|
display: block;
|
|
@@ -15,6 +15,10 @@ const FALLBACK_LANGUAGE = 'en';
|
|
|
15
15
|
* looking at, and how far into it they have got; and hovering a page shows
|
|
16
16
|
* exactly which items it holds.
|
|
17
17
|
*
|
|
18
|
+
* Where there are more pages than fit, the ones left out are stood for by a
|
|
19
|
+
* `···`. That is a button: it opens a field for going straight to any page in
|
|
20
|
+
* the set, so no page is more than one move away however long the set is.
|
|
21
|
+
*
|
|
18
22
|
* @exampleComponent limel-example-pagination-basic
|
|
19
23
|
* @exampleComponent limel-example-pagination-single-page
|
|
20
24
|
* @exampleComponent limel-example-pagination-page-size
|
|
@@ -63,12 +67,15 @@ export class Pagination {
|
|
|
63
67
|
this.idPrefix = createRandomString();
|
|
64
68
|
/** The value each prop was last complained about with. */
|
|
65
69
|
this.warnedAbout = new Map();
|
|
70
|
+
/** The language `numberFormat` settled on, which may not be the prop. */
|
|
71
|
+
this.settledLanguage = FALLBACK_LANGUAGE;
|
|
66
72
|
/** Settled once per render, and the only thing `render` reads. */
|
|
67
73
|
this.view = {
|
|
68
74
|
page: FIRST_PAGE,
|
|
69
75
|
pageCount: null,
|
|
70
76
|
pageSize: DEFAULT_PAGE_SIZE,
|
|
71
77
|
totalItems: null,
|
|
78
|
+
slots: [],
|
|
72
79
|
};
|
|
73
80
|
/**
|
|
74
81
|
* A gap marker, or a page button.
|
|
@@ -79,11 +86,38 @@ export class Pagination {
|
|
|
79
86
|
*/
|
|
80
87
|
this.renderSlot = (slot, index) => {
|
|
81
88
|
if (slot.kind === 'gap') {
|
|
82
|
-
return (
|
|
89
|
+
return this.renderGap(index);
|
|
83
90
|
}
|
|
84
91
|
const isCurrent = slot.page === this.currentPage;
|
|
85
92
|
return (h("button", { key: `page-${index}`, id: this.slotId(index), class: "page", "aria-current": isCurrent ? 'page' : null, "aria-disabled": this.loading ? 'true' : null, "aria-label": this.pageLabel(slot.page, isCurrent), "data-page": slot.page, onClick: this.selectPage }, this.formatNumber(slot.page), this.renderSpinner(isCurrent), this.renderTooltip(slot.page, index)));
|
|
86
93
|
};
|
|
94
|
+
this.openJump = (event) => {
|
|
95
|
+
if (this.loading) {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
const { index } = event.currentTarget.dataset;
|
|
99
|
+
this.openGap = Number(index);
|
|
100
|
+
};
|
|
101
|
+
this.closeJump = () => {
|
|
102
|
+
this.openGap = undefined;
|
|
103
|
+
};
|
|
104
|
+
/**
|
|
105
|
+
* The control is told what to show, so a jump only asks. Focus goes back
|
|
106
|
+
* to the marker the field came out of, which is still on screen and will
|
|
107
|
+
* still be somewhere sensible if the consumer declines.
|
|
108
|
+
*
|
|
109
|
+
* @param event - the page the field asks for
|
|
110
|
+
*/
|
|
111
|
+
this.handleJump = (event) => {
|
|
112
|
+
event.stopPropagation();
|
|
113
|
+
const page = event.detail;
|
|
114
|
+
this.focusGap(this.openGap);
|
|
115
|
+
this.openGap = undefined;
|
|
116
|
+
if (this.goTo(page)) {
|
|
117
|
+
this.awaitedJump = page;
|
|
118
|
+
this.jumpedFrom = this.view.page;
|
|
119
|
+
}
|
|
120
|
+
};
|
|
87
121
|
// Bound once: a handler built per render is a new identity each time, so
|
|
88
122
|
// every button would have its listener swapped on every page change.
|
|
89
123
|
this.step = (event) => {
|
|
@@ -95,9 +129,6 @@ export class Pagination {
|
|
|
95
129
|
this.goTo(this.currentPage + (direction === 'next' ? 1 : -1));
|
|
96
130
|
};
|
|
97
131
|
this.selectPage = (event) => {
|
|
98
|
-
if (this.loading) {
|
|
99
|
-
return;
|
|
100
|
-
}
|
|
101
132
|
const { page } = event.currentTarget.dataset;
|
|
102
133
|
this.goTo(Number(page));
|
|
103
134
|
};
|
|
@@ -127,7 +158,9 @@ export class Pagination {
|
|
|
127
158
|
pageCount: pageCount,
|
|
128
159
|
pageSize: pageSize,
|
|
129
160
|
totalItems: totalItems,
|
|
161
|
+
slots: this.slotsFor(page, pageCount),
|
|
130
162
|
};
|
|
163
|
+
this.closeJumpWithoutAGap();
|
|
131
164
|
this.readOut(page);
|
|
132
165
|
this.reconcile(page, usable);
|
|
133
166
|
this.shownPage = page;
|
|
@@ -144,6 +177,10 @@ export class Pagination {
|
|
|
144
177
|
return Math.max(FIRST_PAGE, Math.ceil(total / pageSize));
|
|
145
178
|
}
|
|
146
179
|
componentDidRender() {
|
|
180
|
+
this.reportCorrection();
|
|
181
|
+
this.followJumpedPage();
|
|
182
|
+
}
|
|
183
|
+
reportCorrection() {
|
|
147
184
|
const page = this.pendingCorrection;
|
|
148
185
|
if (page === undefined) {
|
|
149
186
|
return;
|
|
@@ -250,10 +287,23 @@ export class Pagination {
|
|
|
250
287
|
handleLanguageChange() {
|
|
251
288
|
this.numberFormat = undefined;
|
|
252
289
|
}
|
|
290
|
+
/**
|
|
291
|
+
* A gap is remembered by the position it sits in, and the positions are
|
|
292
|
+
* worked out afresh every render. A page set from outside can put a page
|
|
293
|
+
* number where the open gap was, taking the popover down without it ever
|
|
294
|
+
* closing — and leaving it to spring open by itself the next time a gap
|
|
295
|
+
* lands there.
|
|
296
|
+
*/
|
|
297
|
+
closeJumpWithoutAGap() {
|
|
298
|
+
var _a;
|
|
299
|
+
const open = this.openGap;
|
|
300
|
+
if (open !== undefined && ((_a = this.view.slots[open]) === null || _a === void 0 ? void 0 : _a.kind) !== 'gap') {
|
|
301
|
+
this.openGap = undefined;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
253
304
|
render() {
|
|
254
|
-
const {
|
|
255
|
-
|
|
256
|
-
return (h(Host, { key: '40d6123ac887f519fc31690fce7fcb46e73f2199' }, h("nav", { key: '183d264e761d168c74b8e2bfa8d04f3ee391c937', "aria-label": this.translate('pagination.label'), "aria-busy": this.loading ? 'true' : null }, this.renderArrow('previous'), slots.map((slot, index) => this.renderSlot(slot, index)), this.renderArrow('next')), h("span", { key: 'e59f62c2644a9a4a513ab3a3455854084434aabc', class: "live-region", "aria-live": "polite" }, this.liveMessage)));
|
|
305
|
+
const { slots } = this.view;
|
|
306
|
+
return (h(Host, { key: 'fac46260ef2a4e1e8d4aad962c90477831b6497f' }, h("nav", { key: '239f0ba745b9a385064c4a2c91a572e70a9e6525', "aria-label": this.translate('pagination.label'), "aria-busy": this.loading ? 'true' : null }, this.renderArrow('previous'), slots.map((slot, index) => this.renderSlot(slot, index)), this.renderArrow('next')), h("span", { key: '82fa3c9fb3fa8063be64d8373158d3d0e93138c1', class: "live-region", "aria-live": "polite" }, this.liveMessage)));
|
|
257
307
|
}
|
|
258
308
|
/**
|
|
259
309
|
* Without a count we know where the user is but not how far the set goes,
|
|
@@ -270,6 +320,92 @@ export class Pagination {
|
|
|
270
320
|
}
|
|
271
321
|
return getPageSlots(page, pageCount);
|
|
272
322
|
}
|
|
323
|
+
/**
|
|
324
|
+
* A gap is a button, not a marker: the pages it stands for are still
|
|
325
|
+
* reachable, and typing one is the only way to reach any of them in a
|
|
326
|
+
* single move.
|
|
327
|
+
*
|
|
328
|
+
* Both gaps open the same field, and it accepts any page in the set.
|
|
329
|
+
* Scoping each one to the pages it happens to hide would make two
|
|
330
|
+
* identical markers behave differently, with nothing on screen to say
|
|
331
|
+
* which is which.
|
|
332
|
+
*
|
|
333
|
+
* @param index - where the gap sits in the window
|
|
334
|
+
* @returns the rendered gap
|
|
335
|
+
*/
|
|
336
|
+
renderGap(index) {
|
|
337
|
+
const { pageCount } = this.view;
|
|
338
|
+
// A gap stands for pages that were left out, which cannot have
|
|
339
|
+
// happened before the count arrived: until then `slotsFor` renders
|
|
340
|
+
// the page the user is on and nothing else.
|
|
341
|
+
if (pageCount === null) {
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
return (h("limel-popover", { key: `gap-${index}`, open: this.openGap === index, openDirection: "bottom", onClose: this.closeJump }, h("button", { slot: "trigger", class: "gap", id: this.slotId(index), "aria-label": this.translate('pagination.jump-to-a-page'), "aria-disabled": this.loading ? 'true' : null, "data-index": index, onClick: this.openJump }, "\u00B7\u00B7\u00B7"), h("limel-pagination-jump", { open: this.openGap === index, page: this.view.page, pageCount: pageCount, loading: this.loading, language: this.languageForNumbers, onJump: this.handleJump })));
|
|
345
|
+
}
|
|
346
|
+
focusGap(index) {
|
|
347
|
+
var _a;
|
|
348
|
+
const gap = (_a = this.host.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector(`[id="${this.slotId(index)}"]`);
|
|
349
|
+
gap === null || gap === void 0 ? void 0 : gap.focus();
|
|
350
|
+
this.jumpFocus = gap;
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* Put the keyboard on the page a jump asked for, once the consumer has
|
|
354
|
+
* shown it.
|
|
355
|
+
*
|
|
356
|
+
* Committing tears down the popover the field was in, so between asking
|
|
357
|
+
* and arriving focus is on nothing. If the user has since put it somewhere
|
|
358
|
+
* of their own, arriving is no longer ours to report.
|
|
359
|
+
*/
|
|
360
|
+
followJumpedPage() {
|
|
361
|
+
if (this.awaitedJump === undefined) {
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
if (!this.ownsFocus()) {
|
|
365
|
+
this.forgetJump();
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
if (this.view.page !== this.awaitedJump) {
|
|
369
|
+
// Still showing the page the jump was asked from is what waiting
|
|
370
|
+
// looks like. Anywhere else is an answer too — the consumer chose
|
|
371
|
+
// to show something else — and a later arrival at the page we
|
|
372
|
+
// asked for is then no longer ours to follow.
|
|
373
|
+
if (this.view.page !== this.jumpedFrom) {
|
|
374
|
+
this.forgetJump();
|
|
375
|
+
}
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
this.forgetJump();
|
|
379
|
+
this.focusCurrentPage();
|
|
380
|
+
}
|
|
381
|
+
forgetJump() {
|
|
382
|
+
this.awaitedJump = undefined;
|
|
383
|
+
this.jumpedFrom = undefined;
|
|
384
|
+
}
|
|
385
|
+
/**
|
|
386
|
+
* Whether the keyboard is still where the jump put it. Somewhere else
|
|
387
|
+
* inside this component means the user moved it and it is theirs.
|
|
388
|
+
*
|
|
389
|
+
* The marker itself can also go: jumping from the left gap to page 3
|
|
390
|
+
* turns that slot into a page number, and the button holding focus is
|
|
391
|
+
* taken away with it. A browser answers that by focusing the body rather
|
|
392
|
+
* than nothing, so the marker having left the page is what we look for.
|
|
393
|
+
*/
|
|
394
|
+
ownsFocus() {
|
|
395
|
+
var _a, _b;
|
|
396
|
+
const inside = (_a = this.host.shadowRoot) === null || _a === void 0 ? void 0 : _a.activeElement;
|
|
397
|
+
if (inside === this.jumpFocus) {
|
|
398
|
+
return true;
|
|
399
|
+
}
|
|
400
|
+
return (!((_b = this.jumpFocus) === null || _b === void 0 ? void 0 : _b.isConnected) &&
|
|
401
|
+
document.activeElement === document.body);
|
|
402
|
+
}
|
|
403
|
+
focusCurrentPage() {
|
|
404
|
+
var _a;
|
|
405
|
+
const button = (_a = this.host.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('[aria-current="page"]');
|
|
406
|
+
button === null || button === void 0 ? void 0 : button.focus();
|
|
407
|
+
this.jumpFocus = undefined;
|
|
408
|
+
}
|
|
273
409
|
/**
|
|
274
410
|
* A spinner around the page the user is heading for, while it loads.
|
|
275
411
|
*
|
|
@@ -332,13 +468,19 @@ export class Pagination {
|
|
|
332
468
|
return pageCount === null || page >= pageCount;
|
|
333
469
|
}
|
|
334
470
|
/**
|
|
471
|
+
* The one path every move the user asks for goes through, and so the one
|
|
472
|
+
* place that refuses while a page is still being fetched.
|
|
473
|
+
*
|
|
335
474
|
* @param page - the page to ask for, 1-based
|
|
475
|
+
* @returns whether it was asked for
|
|
336
476
|
*/
|
|
337
477
|
goTo(page) {
|
|
338
|
-
|
|
339
|
-
|
|
478
|
+
this.awaitedJump = undefined;
|
|
479
|
+
if (this.loading || page === this.currentPage) {
|
|
480
|
+
return false;
|
|
340
481
|
}
|
|
341
482
|
this.emitGoToPage(page, 'user');
|
|
483
|
+
return true;
|
|
342
484
|
}
|
|
343
485
|
/** The page on screen: the one asked for, within the range that exists. */
|
|
344
486
|
get currentPage() {
|
|
@@ -474,6 +616,17 @@ export class Pagination {
|
|
|
474
616
|
(_a = this.numberFormat) !== null && _a !== void 0 ? _a : (this.numberFormat = this.createNumberFormat());
|
|
475
617
|
return this.numberFormat.format(value);
|
|
476
618
|
}
|
|
619
|
+
/**
|
|
620
|
+
* What the jump form is given, so that the one warning about a language
|
|
621
|
+
* this browser cannot write numbers in describes both of us. A tag it
|
|
622
|
+
* accepts but does not support, `zz`, resolves to the viewer's own locale
|
|
623
|
+
* rather than throwing, so asking the formatter is the only way to know.
|
|
624
|
+
*/
|
|
625
|
+
get languageForNumbers() {
|
|
626
|
+
var _a;
|
|
627
|
+
(_a = this.numberFormat) !== null && _a !== void 0 ? _a : (this.numberFormat = this.createNumberFormat());
|
|
628
|
+
return this.settledLanguage;
|
|
629
|
+
}
|
|
477
630
|
/**
|
|
478
631
|
* `Intl.NumberFormat` throws on a language it cannot parse, and this is
|
|
479
632
|
* called from `render`, so an unusable one would leave the control drawing
|
|
@@ -494,6 +647,7 @@ export class Pagination {
|
|
|
494
647
|
const resolved = format.resolvedOptions().locale;
|
|
495
648
|
if (getPrimarySubtag(resolved) === getPrimarySubtag(language)) {
|
|
496
649
|
this.warnedAbout.delete('language');
|
|
650
|
+
this.settledLanguage = language;
|
|
497
651
|
return format;
|
|
498
652
|
}
|
|
499
653
|
}
|
|
@@ -504,6 +658,7 @@ export class Pagination {
|
|
|
504
658
|
// Where the translations land for the same unusable value, rather than
|
|
505
659
|
// the viewer's own locale — English labels beside system digit
|
|
506
660
|
// grouping would read differently on every machine.
|
|
661
|
+
this.settledLanguage = FALLBACK_LANGUAGE;
|
|
507
662
|
return new Intl.NumberFormat(FALLBACK_LANGUAGE);
|
|
508
663
|
}
|
|
509
664
|
static get is() { return "limel-pagination"; }
|
|
@@ -631,7 +786,8 @@ export class Pagination {
|
|
|
631
786
|
}
|
|
632
787
|
static get states() {
|
|
633
788
|
return {
|
|
634
|
-
"liveMessage": {}
|
|
789
|
+
"liveMessage": {},
|
|
790
|
+
"openGap": {}
|
|
635
791
|
};
|
|
636
792
|
}
|
|
637
793
|
static get events() {
|
|
@@ -659,6 +815,7 @@ export class Pagination {
|
|
|
659
815
|
}
|
|
660
816
|
}];
|
|
661
817
|
}
|
|
818
|
+
static get elementRef() { return "host"; }
|
|
662
819
|
static get watchers() {
|
|
663
820
|
return [{
|
|
664
821
|
"propName": "language",
|
|
@@ -8,6 +8,30 @@ export const VISIBLE_SLOTS = 7;
|
|
|
8
8
|
/** How many pages to keep either side of the current one. */
|
|
9
9
|
const SIBLINGS = 1;
|
|
10
10
|
const FIRST_PAGE = 1;
|
|
11
|
+
/**
|
|
12
|
+
* The page a typed value asks for, or `null` when it asks for nothing.
|
|
13
|
+
*
|
|
14
|
+
* Out of range is not an error. The set has a first and a last page, both
|
|
15
|
+
* written under the field, and the nearer of them is what someone typing past
|
|
16
|
+
* the end meant.
|
|
17
|
+
*
|
|
18
|
+
* @param value - what was typed into the field
|
|
19
|
+
* @param pageCount - how many pages there are, or `null` when not yet known
|
|
20
|
+
* @returns a page within the set, or `null` when there is no number to use
|
|
21
|
+
*/
|
|
22
|
+
export function pageFromInput(value, pageCount) {
|
|
23
|
+
const typed = Number.parseInt(value, 10);
|
|
24
|
+
if (!Number.isSafeInteger(typed)) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
const atLeastFirst = Math.max(typed, FIRST_PAGE);
|
|
28
|
+
// Each bound on its own. Folding them into one `Math.min` against
|
|
29
|
+
// `pageCount ?? typed` handed the typed value back as its own upper
|
|
30
|
+
// bound, so without a count `0` stayed `0` rather than becoming page 1.
|
|
31
|
+
return pageCount === null
|
|
32
|
+
? atLeastFirst
|
|
33
|
+
: Math.min(atLeastFirst, pageCount);
|
|
34
|
+
}
|
|
11
35
|
/**
|
|
12
36
|
* Build the list of positions to render for a given page count.
|
|
13
37
|
*
|
|
@@ -101,13 +125,6 @@ function withGaps(pages) {
|
|
|
101
125
|
if (skipped === 1) {
|
|
102
126
|
return [{ kind: 'page', page: page - 1 }, slot];
|
|
103
127
|
}
|
|
104
|
-
return [
|
|
105
|
-
{
|
|
106
|
-
kind: 'gap',
|
|
107
|
-
from: pages[index - 1] + 1,
|
|
108
|
-
to: page - 1,
|
|
109
|
-
},
|
|
110
|
-
slot,
|
|
111
|
-
];
|
|
128
|
+
return [{ kind: 'gap' }, slot];
|
|
112
129
|
});
|
|
113
130
|
}
|