@inglorious/web 4.4.0 → 4.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.
@@ -1,340 +0,0 @@
1
- /**
2
- * @typedef {import('../../types/api').API} API
3
- * @typedef {import('../../types/table').TableColumn} TableColumn
4
- * @typedef {import('../../types/table').TableEntity} TableEntity
5
- * @typedef {import('../../types/table').TableRow} TableRow
6
- * @typedef {import('lit-html').TemplateResult} TemplateResult
7
- */
8
-
9
- import { html } from "lit-html"
10
- import { classMap } from "lit-html/directives/class-map.js"
11
- import { ref } from "lit-html/directives/ref.js"
12
- import { repeat } from "lit-html/directives/repeat.js"
13
-
14
- import { filters } from "./filters.js"
15
- import { getPaginationInfo, getRows, getSortDirection } from "./logic.js"
16
-
17
- const DIVISOR = 2
18
- const FIRST_PAGE = 0
19
- const LAST_PAGE = 1
20
- const PRETTY_PAGE = 1
21
- const PERCENTAGE_TO_FLEX = 0.01
22
-
23
- /**
24
- * Rendering logic for the table component.
25
- */
26
- export const rendering = {
27
- /**
28
- * Measures and sets initial column widths.
29
- * This is called after the initial render to capture the "auto" widths of columns.
30
- * @param {TableEntity} entity The table entity.
31
- * @param {HTMLElement} containerEl The header row element containing the columns.
32
- */
33
- mount(entity, containerEl) {
34
- const columns = containerEl.querySelectorAll(":scope > *")
35
- ;[...columns].forEach((column, index) => {
36
- entity.columns[index].width = column.offsetWidth
37
- })
38
- },
39
- /**
40
- * Renders the main table component.
41
- * @param {TableEntity} entity The table entity.
42
- * @param {API} api The API object.
43
- * @returns {TemplateResult} The rendered table.
44
- */
45
- render(entity, api) {
46
- const type = api.getType(entity.type)
47
-
48
- return html`<div class="iw-table">
49
- ${type.renderHeader(entity, api)} ${type.renderBody(entity, api)}
50
- ${type.renderFooter(entity, api)}
51
- </div> `
52
- },
53
- /**
54
- * Renders the table header.
55
- * @param {TableEntity} entity The table entity.
56
- * @param {API} api The API object.
57
- * @returns {TemplateResult} The rendered header.
58
- */
59
- renderHeader(entity, api) {
60
- const type = api.getType(entity.type)
61
-
62
- return html`<div class="iw-table-header">
63
- <div
64
- class="iw-table-header-row"
65
- ${ref((el) => {
66
- if (
67
- el &&
68
- entity.columns.some(({ width }) => typeof width === "string")
69
- ) {
70
- queueMicrotask(() => {
71
- api.notify(`#${entity.id}:mount`, el)
72
- })
73
- }
74
- })}
75
- >
76
- ${repeat(
77
- entity.columns,
78
- (column) => column.id,
79
- (column, index) =>
80
- type.renderHeaderColumn(entity, { column, index }, api),
81
- )}
82
- </div>
83
-
84
- ${entity.search && type.renderSearchbar(entity, api)}
85
- </div>`
86
- },
87
- /**
88
- * Renders a single column in the header.
89
- * @param {TableEntity} entity The table entity.
90
- * @param {object} payload The payload.
91
- * @param {TableColumn} payload.column The column definition.
92
- * @param {API} api The API object.
93
- * @returns {TemplateResult} The rendered header column.
94
- */
95
- renderHeaderColumn(entity, { column }, api) {
96
- return html`<div
97
- class="iw-table-header-column"
98
- style=${getColumnStyle(column)}
99
- >
100
- <div
101
- @click=${() =>
102
- column.isSortable &&
103
- api.notify(`#${entity.id}:sortChange`, column.id)}
104
- class="iw-table-header-title"
105
- >
106
- ${column.title} ${getSortIcon(getSortDirection(entity, column.id))}
107
- </div>
108
-
109
- ${column.isFilterable && filters.render(entity, column, api)}
110
- </div>`
111
- },
112
- /**
113
- * Renders the search bar.
114
- * @param {TableEntity} entity The table entity.
115
- * @param {API} api The API object.
116
- * @returns {TemplateResult} The rendered search bar.
117
- */
118
- renderSearchbar(entity, api) {
119
- return html`<input
120
- name="search"
121
- type="text"
122
- placeholder=${entity.search.placeholder ?? "Fuzzy search..."}
123
- value=${entity.search.value}
124
- @input=${(event) =>
125
- api.notify(`#${entity.id}:searchChange`, event.target.value)}
126
- class="iw-table-searchbar"
127
- />`
128
- },
129
- /**
130
- * Renders the table body with rows.
131
- * @param {TableEntity} entity The table entity.
132
- * @param {API} api The API object.
133
- * @returns {TemplateResult} The rendered table body.
134
- */
135
- renderBody(entity, api) {
136
- const type = api.getType(entity.type)
137
-
138
- return html`<div class="iw-table-body">
139
- ${repeat(
140
- getRows(entity),
141
- (row) => row.id,
142
- (row, index) => type.renderRow(entity, { row, index }, api),
143
- )}
144
- </div>`
145
- },
146
- /**
147
- * Renders a single row in the table body.
148
- * @param {TableEntity} entity The table entity.
149
- * @param {object} payload The payload.
150
- * @param {TableRow} payload.row The row data.
151
- * @param {number} payload.index The row index.
152
- * @param {API} api The API object.
153
- * @returns {TemplateResult} The rendered row.
154
- */
155
- renderRow(entity, { row, index }, api) {
156
- const type = api.getType(entity.type)
157
- const rowId = row[entity.rowId ?? "id"]
158
-
159
- return html`<div
160
- @click=${() => api.notify(`#${entity.id}:rowToggle`, rowId)}
161
- class="iw-table-row ${classMap({
162
- "iw-table-row-even": index % DIVISOR,
163
- "iw-table-row-selected": entity.selection?.includes(rowId),
164
- })}"
165
- >
166
- ${repeat(
167
- entity.columns,
168
- (column) => column.id,
169
- (column, index) =>
170
- type.renderCell(entity, { cell: row[column.id], index }, api),
171
- )}
172
- </div>`
173
- },
174
- /**
175
- * Renders a single cell within a row.
176
- * @param {TableEntity} entity The table entity.
177
- * @param {object} payload The payload.
178
- * @param {any} payload.cell The cell data.
179
- * @param {number} payload.index The column index.
180
- * @param {API} api The API object.
181
- * @returns {TemplateResult} The rendered cell.
182
- */
183
- renderCell(entity, { cell, index }, api) {
184
- const type = api.getType(entity.type)
185
- const column = entity.columns[index]
186
-
187
- return html`<div
188
- class="iw-table-cell ${classMap({
189
- "iw-table-cell-number": column.type === "number",
190
- "iw-table-cell-date": column.type === "date",
191
- "iw-table-cell-boolean": column.type === "boolean",
192
- })}"
193
- style=${getColumnStyle(column)}
194
- >
195
- ${type.renderValue(entity, { value: cell, column, index }, api)}
196
- </div>`
197
- },
198
-
199
- /**
200
- * Renders the value within a cell. This can be overridden for custom formatting.
201
- * @param {TableEntity} _entity The table entity (ignored).
202
- * @param {object} payload The payload.
203
- * @param {any} payload.value The value to render.
204
- * @returns {any} The value to be rendered.
205
- */
206
- renderValue(_entity, { value }) {
207
- return value
208
- },
209
- /**
210
- * Renders the table footer.
211
- * @param {TableEntity} entity The table entity.
212
- * @param {API} api The API object.
213
- * @returns {TemplateResult} The rendered footer.
214
- */
215
- renderFooter(entity, api) {
216
- const type = api.getType(entity.type)
217
- const pagination = getPaginationInfo(entity)
218
-
219
- return html`<div class="iw-table-footer">
220
- <div class="iw-table-footer-row">
221
- <div>
222
- ${pagination.start + PRETTY_PAGE} to ${pagination.end} of ${pagination.totalRows}
223
- entries
224
- </div>
225
-
226
- ${type.renderPagination(entity, pagination, api)}
227
-
228
- <div class="iw-table-footer-row">
229
- <div>Page size:</div>
230
- <select
231
- name="pageSize"
232
- @change=${(event) =>
233
- api.notify(
234
- `#${entity.id}:pageSizeChange`,
235
- Number(event.target.value),
236
- )}
237
- >
238
- <option>10</option>
239
- <option>20</option>
240
- <option>30</option>
241
- </select>
242
- </div>
243
- </div>
244
- </div>
245
- </div>`
246
- },
247
- /**
248
- * Renders the pagination controls.
249
- * @param {TableEntity} entity The table entity.
250
- * @param {object} pagination The pagination info object from `getPaginationInfo`.
251
- * @param {API} api The API object.
252
- * @returns {TemplateResult} The rendered pagination controls.
253
- */
254
- renderPagination(entity, pagination, api) {
255
- return html`<div class="iw-table-row">
256
- <button
257
- ?disabled=${!pagination.hasPrevPage}
258
- @click=${() => api.notify(`#${entity.id}:pageChange`, FIRST_PAGE)}
259
- class="iw-table-pagination-button"
260
- >
261
- |&#10094;
262
- </button>
263
- <button
264
- ?disabled=${!pagination.hasPrevPage}
265
- @click=${() => api.notify(`#${entity.id}:pagePrev`)}
266
- class="iw-table-pagination-button"
267
- >
268
- &#10094;
269
- </button>
270
- <input
271
- name="page"
272
- type="number"
273
- min="1"
274
- max=${pagination.totalPages}
275
- value=${pagination.page + PRETTY_PAGE}
276
- class="iw-table-page-input"
277
- @input=${(event) =>
278
- api.notify(
279
- `#${entity.id}:pageChange`,
280
- Number(event.target.value) - PRETTY_PAGE,
281
- )}
282
- />
283
- /
284
- <span>${pagination.totalPages}</span>
285
- <button
286
- ?disabled="${!pagination.hasNextPage}"
287
- @click=${() => api.notify(`#${entity.id}:pageNext`)}
288
- class="iw-table-pagination-button"
289
- >
290
- &#10095;
291
- </button>
292
- <button
293
- ?disabled=${!pagination.hasNextPage}
294
- @click=${() =>
295
- api.notify(
296
- `#${entity.id}:pageChange`,
297
- pagination.totalPages - LAST_PAGE,
298
- )}
299
- class="iw-table-pagination-button"
300
- >
301
- &#10095;|
302
- </button>
303
- </div>`
304
- },
305
- }
306
-
307
- /**
308
- * Generates the style string for a column.
309
- * @param {TableColumn} column The column definition.
310
- * @returns {string} The style string.
311
- */
312
- function getColumnStyle(column) {
313
- if (typeof column.width === "string") {
314
- if (column.width?.endsWith("%")) {
315
- // eslint-disable-next-line no-magic-numbers
316
- const percentage = Number(column.width.slice(0, -1))
317
- return `flex: ${percentage * PERCENTAGE_TO_FLEX}`
318
- }
319
-
320
- return `width: ${column.width}`
321
- }
322
-
323
- return `width: ${column.width}px`
324
- }
325
-
326
- /**
327
- * Gets the appropriate sort icon for a given direction.
328
- * @param {"asc"|"desc"|null} direction The sort direction.
329
- * @returns {string} The sort icon.
330
- */
331
- function getSortIcon(direction) {
332
- switch (direction) {
333
- case "asc":
334
- return "▲"
335
- case "desc":
336
- return "▼"
337
- default:
338
- return "▲▼"
339
- }
340
- }
@@ -1,83 +0,0 @@
1
- .iw-table {
2
- .iw-table-header {
3
- border-bottom: 1px solid grey;
4
- padding: 1em 0;
5
- row-gap: 1em;
6
- }
7
-
8
- .iw-table-header-row {
9
- align-items: flex-start;
10
- column-gap: 1em;
11
- }
12
-
13
- .iw-table-header-column {
14
- row-gap: 0.5em;
15
- }
16
-
17
- .iw-table-header-title {
18
- font-weight: bold;
19
- white-space: nowrap;
20
- }
21
-
22
- .iw-table-body {
23
- max-height: 35em;
24
- overflow: auto;
25
- border-bottom: 1px solid grey;
26
- }
27
-
28
- .iw-table-row {
29
- align-items: center;
30
- column-gap: 1em;
31
- }
32
-
33
- .iw-table-row-even {
34
- background-color: aliceblue;
35
- }
36
-
37
- .iw-table-row-selected {
38
- border-left: 1px solid cornflowerblue;
39
- border-right: 1px solid cornflowerblue;
40
- }
41
-
42
- :not(.iw-table-row-selected) + .iw-table-row-selected {
43
- border-top: 1px solid cornflowerblue;
44
- margin-top: -1px;
45
- }
46
-
47
- .iw-table-row-selected + :not(.iw-table-row-selected) {
48
- border-top: 1px solid cornflowerblue;
49
- margin-top: -1px;
50
- }
51
-
52
- .iw-table-cell {
53
- padding: 1em;
54
- }
55
-
56
- .iw-table-cell-number,
57
- .iw-table-cell-date {
58
- text-align: right;
59
- }
60
-
61
- .iw-table-cell-boolean {
62
- text-align: center;
63
- }
64
-
65
- .iw-table-footer {
66
- padding: 1em 0;
67
- }
68
-
69
- .iw-table-footer-row {
70
- justify-content: space-between;
71
- align-items: center;
72
- column-gap: 1em;
73
- }
74
-
75
- .iw-table-page-input {
76
- min-width: 4em;
77
- text-align: right;
78
- }
79
-
80
- .iw-table-pagination-button {
81
- white-space: nowrap;
82
- }
83
- }
package/types/list.d.ts DELETED
@@ -1,91 +0,0 @@
1
- import type { TemplateResult } from "lit-html"
2
- import type { Api } from "./mount"
3
-
4
- /**
5
- * Represents a single item in the list's data array.
6
- * It's a generic record but should ideally have a unique `id`.
7
- */
8
- export type ListItem = Record<string, any> & { id: string | number }
9
-
10
- /**
11
- * Represents the state of a virtualized list entity.
12
- */
13
- export interface ListEntity<T extends ListItem = ListItem> {
14
- /** A unique identifier for the list entity. */
15
- id: string | number
16
-
17
- /** The entity type, used to look up rendering logic. */
18
- type: string
19
-
20
- /** The full array of items for the list. */
21
- items: T[]
22
-
23
- /** The current scroll position from the top in pixels. */
24
- scrollTop: number
25
-
26
- /** The start and end indices of the currently visible items (including buffer). */
27
- visibleRange: { start: number; end: number }
28
-
29
- /** The height of the visible viewport in pixels. */
30
- viewportHeight: number
31
-
32
- /** The number of items to render outside the viewport (above and below). */
33
- bufferSize: number
34
-
35
- /** The measured height of a single item in pixels. Null until measured on mount. */
36
- itemHeight: number | null
37
-
38
- /** An estimated height for items used for calculations before they are measured. */
39
- estimatedHeight: number
40
-
41
- /** Any other custom properties. */
42
- [key: string]: any
43
- }
44
-
45
- /**
46
- * The list type implementation, combining logic and rendering.
47
- */
48
- export declare const list: {
49
- /**
50
- * Initializes the list entity with default state.
51
- * @param entity The list entity to initialize.
52
- */
53
- create(entity: ListEntity): void
54
-
55
- /**
56
- * Handles the scroll event to update the visible range of items.
57
- * @param entity The list entity.
58
- * @param containerEl The scrolling container element.
59
- */
60
- scroll(entity: ListEntity, containerEl: HTMLElement): void
61
-
62
- /**
63
- * Mounts the list, measuring the first item to determine the `itemHeight`
64
- * for accurate virtualization calculations.
65
- * @param entity The list entity.
66
- * @param containerEl The scrolling container element.
67
- */
68
- mount(entity: ListEntity, containerEl: HTMLElement): void
69
-
70
- /**
71
- * Renders the virtualized list component.
72
- * @param entity The list entity.
73
- * @param api The store API.
74
- */
75
- render(entity: ListEntity, api: Api): TemplateResult
76
-
77
- /**
78
- * Renders a single item in the list. This is a default implementation
79
- * and is expected to be overridden by a specific list type.
80
- * @param entity The list entity.
81
- * @param payload The payload for rendering the item.
82
- * @param payload.item The item data.
83
- * @param payload.index The item's absolute index in the full list.
84
- * @param api The store API.
85
- */
86
- renderItem(
87
- entity: ListEntity,
88
- payload: { item: ListItem; index: number },
89
- api: Api,
90
- ): TemplateResult
91
- }
package/types/select.d.ts DELETED
@@ -1,136 +0,0 @@
1
- import type { TemplateResult } from "lit-html"
2
- import type { Api } from "./mount"
3
-
4
- /**
5
- * Represents an option in the select component.
6
- * Can be a simple string/number or an object with value and label.
7
- */
8
- export type SelectOption =
9
- | string
10
- | number
11
- | {
12
- value: string | number
13
- label?: string
14
- disabled?: boolean
15
- [key: string]: any
16
- }
17
-
18
- /**
19
- * Represents the state of a select entity.
20
- */
21
- export interface SelectEntity {
22
- /** A unique identifier for the select entity. */
23
- id: string | number
24
- /** The entity type (usually 'select'). */
25
- type: string
26
- /** The list of options available for selection. */
27
- options: SelectOption[]
28
- /** Whether the dropdown is currently open. */
29
- isOpen: boolean
30
- /** The current search term entered by the user. */
31
- searchTerm: string
32
- /** The index of the currently focused option (for keyboard navigation). */
33
- focusedIndex: number
34
- /** Whether multiple options can be selected. */
35
- isMulti: boolean
36
- /** The currently selected value(s). */
37
- selectedValue: string | number | (string | number)[] | null
38
- /** Whether the options are currently loading. */
39
- isLoading: boolean
40
- /** Whether the select component is disabled. */
41
- isDisabled: boolean
42
- /** Whether the user can search through options. */
43
- isSearchable: boolean
44
- /** Whether the selection can be cleared. */
45
- isClearable: boolean
46
- /** Whether new options can be created by the user. */
47
- isCreatable: boolean
48
- /** Placeholder text to display when no value is selected. */
49
- placeholder: string
50
- /** Message to display when no options match the search term. */
51
- noOptionsMessage: string
52
- /** Message to display while options are loading. */
53
- loadingMessage: string
54
- /** Property name to group options by. */
55
- groupBy: string | null
56
- }
57
-
58
- /**
59
- * The select type implementation.
60
- */
61
- export declare const select: {
62
- /**
63
- * Initializes the select entity with default state.
64
- * @param {SelectEntity} entity
65
- */
66
- create(entity: SelectEntity): void
67
-
68
- /**
69
- * Renders the select component.
70
- * @param entity The select entity.
71
- * @param api The store API.
72
- */
73
- render(entity: SelectEntity, api: Api): TemplateResult
74
-
75
- /**
76
- * Opens the select dropdown.
77
- * @param entity The select entity.
78
- */
79
- open(entity: SelectEntity): void
80
-
81
- /**
82
- * Closes the select dropdown.
83
- * @param entity The select entity.
84
- */
85
- close(entity: SelectEntity): void
86
-
87
- /**
88
- * Toggles the select dropdown open/closed state.
89
- * @param entity The select entity.
90
- */
91
- toggle(entity: SelectEntity): void
92
-
93
- /**
94
- * Selects an option.
95
- * @param entity The select entity.
96
- * @param option The option to select.
97
- */
98
- optionSelect(entity: SelectEntity, option: SelectOption): void
99
-
100
- /**
101
- * Clears the current selection.
102
- * @param entity The select entity.
103
- */
104
- clear(entity: SelectEntity): void
105
-
106
- /**
107
- * Updates the search term and filters options.
108
- * @param entity The select entity.
109
- * @param term The new search term.
110
- */
111
- searchChange(entity: SelectEntity, term: string): void
112
-
113
- /**
114
- * Moves focus to the next option.
115
- * @param entity The select entity.
116
- */
117
- focusNext(entity: SelectEntity): void
118
-
119
- /**
120
- * Moves focus to the previous option.
121
- * @param entity The select entity.
122
- */
123
- focusPrev(entity: SelectEntity): void
124
-
125
- /**
126
- * Moves focus to the first option.
127
- * @param entity The select entity.
128
- */
129
- focusFirst(entity: SelectEntity): void
130
-
131
- /**
132
- * Moves focus to the last option.
133
- * @param entity The select entity.
134
- */
135
- focusLast(entity: SelectEntity): void
136
- }