@inglorious/ui 9.3.0 → 9.4.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.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inglorious/ui",
|
|
3
|
-
"version": "9.
|
|
3
|
+
"version": "9.4.0",
|
|
4
4
|
"description": "A design system for Inglorious Web with render-function components and customizable themes",
|
|
5
5
|
"author": "Matteo Antony Mistretta <antony.mistretta@gmail.com> (https://ingloriouscoderz.it)",
|
|
6
6
|
"license": "MIT",
|
|
@@ -315,7 +315,7 @@
|
|
|
315
315
|
"@iconify-icons/fa7-regular": "^2.0.0",
|
|
316
316
|
"@iconify-icons/fa7-solid": "^2.0.0",
|
|
317
317
|
"@iconify-icons/material-symbols": "^2.0.6",
|
|
318
|
-
"@inglorious/charts": "12.2.
|
|
318
|
+
"@inglorious/charts": "12.2.1",
|
|
319
319
|
"@inglorious/eslint-config": "1.2.0",
|
|
320
320
|
"@inglorious/store": "12.0.0",
|
|
321
321
|
"@inglorious/vite-plugin-jsx": "2.0.5",
|
|
@@ -35,9 +35,16 @@
|
|
|
35
35
|
gap: var(--iw-space-2);
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
/* The two ends of a range share the column; native date and number
|
|
39
|
+
fields would otherwise keep a width of their own and spill out. */
|
|
40
|
+
.iw-data-grid-filter-range > .iw-input {
|
|
40
41
|
flex: 1 1 0;
|
|
42
|
+
min-width: 0;
|
|
43
|
+
|
|
44
|
+
.iw-input-frame,
|
|
45
|
+
.iw-input-control {
|
|
46
|
+
min-width: 0;
|
|
47
|
+
}
|
|
41
48
|
}
|
|
42
49
|
|
|
43
50
|
.iw-data-grid-row {
|
|
@@ -19,7 +19,6 @@ export const RangeFilter = {
|
|
|
19
19
|
placeholder: column.filter.placeholder ?? "≥",
|
|
20
20
|
size: "sm",
|
|
21
21
|
isFullWidth: true,
|
|
22
|
-
className: "iw-data-grid-filter-range-input",
|
|
23
22
|
onChange: (value) => {
|
|
24
23
|
const formattedValue = value ? format(value, column.type) : null
|
|
25
24
|
entity.onFilterChange?.({
|
|
@@ -34,7 +33,6 @@ export const RangeFilter = {
|
|
|
34
33
|
placeholder: column.filter.placeholder ?? "≤",
|
|
35
34
|
size: "sm",
|
|
36
35
|
isFullWidth: true,
|
|
37
|
-
className: "iw-data-grid-filter-range-input",
|
|
38
36
|
onChange: (value) => {
|
|
39
37
|
const formattedValue = value ? format(value, column.type) : null
|
|
40
38
|
entity.onFilterChange?.({
|
|
@@ -302,7 +302,7 @@ export function cellChange(entity, { rowId, columnId, value }) {
|
|
|
302
302
|
|
|
303
303
|
entity.edits ??= {}
|
|
304
304
|
const edits = (entity.edits[rowId] ??= {})
|
|
305
|
-
if (
|
|
305
|
+
if (isSameValue(row[columnId], value)) {
|
|
306
306
|
delete edits[columnId]
|
|
307
307
|
} else {
|
|
308
308
|
edits[columnId] = value
|
|
@@ -500,7 +500,7 @@ function getDefaultColumnFilter(type) {
|
|
|
500
500
|
*/
|
|
501
501
|
function getDefaultColumnWidth(filterType) {
|
|
502
502
|
if (filterType === "number") return 70
|
|
503
|
-
if (filterType === "range") return
|
|
503
|
+
if (filterType === "range") return 160
|
|
504
504
|
if (filterType === "select") return 70
|
|
505
505
|
if (filterType === "date") return 120
|
|
506
506
|
if (filterType === "time") return 120
|
|
@@ -564,3 +564,14 @@ function assertRowKeys(entity) {
|
|
|
564
564
|
seen.add(rowKey)
|
|
565
565
|
})
|
|
566
566
|
}
|
|
567
|
+
|
|
568
|
+
/**
|
|
569
|
+
* Whether two cell values are the same, dates by their time.
|
|
570
|
+
* @param {any} a
|
|
571
|
+
* @param {any} b
|
|
572
|
+
* @returns {boolean}
|
|
573
|
+
*/
|
|
574
|
+
function isSameValue(a, b) {
|
|
575
|
+
if (a instanceof Date && b instanceof Date) return a.getTime() === b.getTime()
|
|
576
|
+
return Object.is(a, b)
|
|
577
|
+
}
|
|
@@ -33,6 +33,13 @@ const PRETTY_PAGE = 1
|
|
|
33
33
|
const FIRST_PAGE = 0
|
|
34
34
|
const LAST_PAGE = 1
|
|
35
35
|
const ONE_EDIT = 1
|
|
36
|
+
const WEB_URL = /^https?:\/\//i
|
|
37
|
+
/** Fields edit dates as `YYYY-MM-DD`. */
|
|
38
|
+
const DATE_LENGTH = 10
|
|
39
|
+
const DATE_PART_LENGTH = 2
|
|
40
|
+
/** Date's months count from zero, people's from one. */
|
|
41
|
+
const MONTH_OFFSET = 1
|
|
42
|
+
const INPUT_TYPES = { number: "number", date: "date", url: "url" }
|
|
36
43
|
|
|
37
44
|
export const DataGrid = {
|
|
38
45
|
/**
|
|
@@ -317,15 +324,14 @@ export const DataGrid = {
|
|
|
317
324
|
})
|
|
318
325
|
}
|
|
319
326
|
|
|
320
|
-
const
|
|
327
|
+
const saved = row[column.id]
|
|
321
328
|
return Input.render({
|
|
322
329
|
size: "sm",
|
|
323
330
|
ariaLabel: column.title,
|
|
324
|
-
inputType:
|
|
325
|
-
value:
|
|
331
|
+
inputType: INPUT_TYPES[column.type] ?? "text",
|
|
332
|
+
value: column.type === "date" ? toDateField(value) : (value ?? ""),
|
|
326
333
|
isFullWidth: true,
|
|
327
|
-
onChange: (text) =>
|
|
328
|
-
change(isNumber ? (text === "" ? null : Number(text)) : text),
|
|
334
|
+
onChange: (text) => change(fromField(text, column.type, saved)),
|
|
329
335
|
})
|
|
330
336
|
},
|
|
331
337
|
|
|
@@ -337,8 +343,16 @@ export const DataGrid = {
|
|
|
337
343
|
* @param {any} payload.value The value to render.
|
|
338
344
|
* @returns {any} The value to be rendered.
|
|
339
345
|
*/
|
|
340
|
-
renderValue(_props, { value }) {
|
|
341
|
-
|
|
346
|
+
renderValue(_props, { value, column }) {
|
|
347
|
+
// A link only for a web address: a `javascript:` one must not run.
|
|
348
|
+
return when(
|
|
349
|
+
column.type === "url" && WEB_URL.test(String(value ?? "")),
|
|
350
|
+
() =>
|
|
351
|
+
html`<a href=${value} target="_blank" rel="noopener noreferrer"
|
|
352
|
+
>${value}</a
|
|
353
|
+
>`,
|
|
354
|
+
() => value,
|
|
355
|
+
)
|
|
342
356
|
},
|
|
343
357
|
|
|
344
358
|
/**
|
|
@@ -581,3 +595,38 @@ function getVirtualizationState(props, totalItems) {
|
|
|
581
595
|
visibleRange: { start, end },
|
|
582
596
|
}
|
|
583
597
|
}
|
|
598
|
+
|
|
599
|
+
/**
|
|
600
|
+
* A date, in whatever form a row keeps it, as a date field shows it.
|
|
601
|
+
* @param {Date|number|string|null|undefined} value
|
|
602
|
+
* @returns {string}
|
|
603
|
+
*/
|
|
604
|
+
function toDateField(value) {
|
|
605
|
+
if (value == null || value === "") return ""
|
|
606
|
+
if (typeof value === "string") return value.slice(0, DATE_LENGTH)
|
|
607
|
+
const date = new Date(value)
|
|
608
|
+
if (Number.isNaN(date.getTime())) return ""
|
|
609
|
+
const pad = (part) => String(part).padStart(DATE_PART_LENGTH, "0")
|
|
610
|
+
return `${date.getFullYear()}-${pad(date.getMonth() + MONTH_OFFSET)}-${pad(date.getDate())}`
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
/**
|
|
614
|
+
* What a field holds, back in the form the row kept the value in: a number,
|
|
615
|
+
* or a date as a `Date`, a timestamp or a string, as it was. A date that had
|
|
616
|
+
* a time of day keeps it.
|
|
617
|
+
* @param {string} text
|
|
618
|
+
* @param {string} type The column's type.
|
|
619
|
+
* @param {any} saved The row's own value.
|
|
620
|
+
* @returns {any}
|
|
621
|
+
*/
|
|
622
|
+
function fromField(text, type, saved) {
|
|
623
|
+
if (type === "number") return text === "" ? null : Number(text)
|
|
624
|
+
if (type !== "date") return text
|
|
625
|
+
if (text === "") return null
|
|
626
|
+
if (!(saved instanceof Date) && typeof saved !== "number") return text
|
|
627
|
+
|
|
628
|
+
const [year, month, day] = text.split("-").map(Number)
|
|
629
|
+
const date = new Date(saved)
|
|
630
|
+
date.setFullYear(year, month - MONTH_OFFSET, day)
|
|
631
|
+
return saved instanceof Date ? date : date.getTime()
|
|
632
|
+
}
|
|
@@ -26,7 +26,11 @@ export interface DataGridVirtualization {
|
|
|
26
26
|
export interface Column {
|
|
27
27
|
id: string
|
|
28
28
|
title: string
|
|
29
|
-
|
|
29
|
+
/**
|
|
30
|
+
* `"url"` cells show web addresses as links. A date column's editor gives
|
|
31
|
+
* back dates in the form the row keeps them: `Date`, timestamp or string.
|
|
32
|
+
*/
|
|
33
|
+
type?: "string" | "number" | "boolean" | "date" | "url"
|
|
30
34
|
isSortable?: boolean
|
|
31
35
|
isFilterable?: boolean
|
|
32
36
|
filter?: {
|