jsl-ui 10.0.0 → 10.0.2
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/docs/ButtonComponent.md +8 -0
- package/examples/showcase/grids.ts +71 -5
- package/examples/showcase/package.json +10 -0
- package/examples/showcase/showcase.css +37 -2
- package/examples/showcase/showcase.ts +7 -1
- package/lib/grid/slick-default-theme.css +32 -3
- package/lib/grid/slick.grid.css +3 -3
- package/lib/grid/slick.grid.js +1 -1
- package/lib/grid.css +748 -3306
- package/lib/index.css +42 -9
- package/lib/index.css.map +1 -1
- package/lib/index.js.map +1 -1
- package/lib/ui/button.css +18 -5
- package/package.json +3 -2
package/docs/ButtonComponent.md
CHANGED
|
@@ -12,3 +12,11 @@ const bookmark = new ButtonComponent({
|
|
|
12
12
|
- Set `type: "button"` for actions inside forms; omitting it retains native submit behavior.
|
|
13
13
|
- `pressed` is the initial state. Afterwards, use `bookmark.pressed`; assigning it does not raise callbacks.
|
|
14
14
|
- Icon strings are literal text, not HTML or icon-library class names. Supply a VNode/component for markup; name icon-only buttons with `ariaLabel` or hidden `content`.
|
|
15
|
+
- Manual markup uses the same styles; load `jsl-ui/lib/ui/button.css` when not importing components:
|
|
16
|
+
|
|
17
|
+
```html
|
|
18
|
+
<button type="button" class="jsl-button jsl-button-outline">View projects</button>
|
|
19
|
+
<a href="/projects" class="jsl-button jsl-button-outline">View projects</a>
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
- Use links for navigation and buttons for actions. Disable buttons with native `disabled`; for links, use `aria-disabled="true"` for styling and omit `href` or prevent activation yourself. Manual toggles need their own event handler to update `aria-pressed`.
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import { IJSLVNode, JSLRender } from "jsl-render";
|
|
2
2
|
import { GridComponent } from "../../components/grid/GridComponent";
|
|
3
3
|
import { ISlickColumn } from "../../grid/slick.core";
|
|
4
|
-
import { TextEditor, NumericEditor, BooleanEditor } from "../../grid/slick.editors.core";
|
|
5
|
-
import { DateEditor } from "../../grid/slick.editors.date";
|
|
6
|
-
import {
|
|
4
|
+
import { TextEditor, MultiLineTextEditor, NumericEditor, BooleanEditor } from "../../grid/slick.editors.core";
|
|
5
|
+
import { DateEditor, TimeEditor } from "../../grid/slick.editors.date";
|
|
6
|
+
import { AutoSuggestEditor, LookupEditor, MultiLookupEditor } from "../../grid/slick.editors.lookup";
|
|
7
|
+
import {
|
|
8
|
+
CheckboxFormatter, DateFormatter, DateTimeFormatter, LookupFormatter,
|
|
9
|
+
MultiLineTextFormatter, MultiLookupFormatter, NumericFormatter, TextFormatter, TimeFormatter,
|
|
10
|
+
TooltipFormatter, YesNoFormatter
|
|
11
|
+
} from "../../grid/slick.formatters";
|
|
7
12
|
import { ButtonComponent } from "../../components/button/ButtonComponent";
|
|
8
13
|
import { numeric } from "../../core/numeric";
|
|
9
14
|
|
|
@@ -44,6 +49,66 @@ editable.initEvents(grid => grid.onAfterEdit.subscribe((_event, args) => {
|
|
|
44
49
|
// Grid events run independently of the showcase renderer.
|
|
45
50
|
JSLRender.lastCreatedRenderer?.refresh();
|
|
46
51
|
}));
|
|
52
|
+
|
|
53
|
+
interface EditorSample {
|
|
54
|
+
id: number;
|
|
55
|
+
title: string;
|
|
56
|
+
notes: string;
|
|
57
|
+
amount: number;
|
|
58
|
+
start: Date;
|
|
59
|
+
startTime: string;
|
|
60
|
+
meeting: Date;
|
|
61
|
+
approved: boolean;
|
|
62
|
+
completed: boolean;
|
|
63
|
+
status: string;
|
|
64
|
+
tags: string[];
|
|
65
|
+
owner: string;
|
|
66
|
+
detail: string;
|
|
67
|
+
}
|
|
68
|
+
const editorValueList = [
|
|
69
|
+
{ text: "Planning", value: "planning" },
|
|
70
|
+
{ text: "In progress", value: "in-progress" },
|
|
71
|
+
{ text: "In review", value: "in-review" },
|
|
72
|
+
{ text: "Complete", value: "complete" }
|
|
73
|
+
];
|
|
74
|
+
const editorSamples: EditorSample[] = Array.from({ length: 8 }, (_, index) => ({
|
|
75
|
+
id: index + 1,
|
|
76
|
+
title: ["Design system", "Website refresh", "Customer research", "Mobile workspace"][index % 4],
|
|
77
|
+
notes: ["Review the latest proposal", "Collect feedback from the team", "Prepare the next milestone"][index % 3],
|
|
78
|
+
amount: 1250 + index * 175.5,
|
|
79
|
+
start: new Date(2026, 9, 2 + index),
|
|
80
|
+
startTime: ["09:00", "10:30", "13:15", "15:45"][index % 4],
|
|
81
|
+
meeting: new Date(2026, 9, 2 + index, 9 + index % 8, 30),
|
|
82
|
+
approved: index % 2 === 0,
|
|
83
|
+
completed: index % 3 === 0,
|
|
84
|
+
status: ["planning", "in-progress", "in-review", "complete"][index % 4],
|
|
85
|
+
tags: index % 2 === 0 ? ["Design", "Internal"] : ["Customer", "Research"],
|
|
86
|
+
owner: ["Alex Morgan", "Sam Weber", "Jordan Lee", "Robin Taylor"][index % 4],
|
|
87
|
+
detail: "Updated by the project team. Hover this cell to see the full tooltip text."
|
|
88
|
+
}));
|
|
89
|
+
const editorSampleColumns: ISlickColumn[] = [
|
|
90
|
+
{ id: "title", name: "Text", width: 155, formatter: TextFormatter, editor: TextEditor },
|
|
91
|
+
{ id: "notes", name: "Multiline text", width: 185, formatter: MultiLineTextFormatter, editor: MultiLineTextEditor },
|
|
92
|
+
{ id: "amount", name: "Number", width: 115, headerCssClass: "right", formatter: NumericFormatter, format: "#,0.00", editor: NumericEditor },
|
|
93
|
+
{ id: "start", name: "Date", width: 125, formatter: DateFormatter, editor: DateEditor },
|
|
94
|
+
{ id: "startTime", name: "Time input", width: 115, formatter: TextFormatter, editor: TimeEditor },
|
|
95
|
+
{ id: "meeting", name: "Time display", width: 110, formatter: TimeFormatter },
|
|
96
|
+
{ id: "meetingDateTime", field: "meeting", name: "Date & time", width: 165, formatter: DateTimeFormatter },
|
|
97
|
+
{ id: "approved", name: "Checkbox", width: 100, headerCssClass: "center", formatter: CheckboxFormatter, editor: BooleanEditor },
|
|
98
|
+
{ id: "completed", name: "Yes / no", width: 95, formatter: YesNoFormatter, editor: BooleanEditor },
|
|
99
|
+
{ id: "status", name: "Lookup", width: 130, formatter: LookupFormatter, valueList: editorValueList, editor: LookupEditor },
|
|
100
|
+
{ id: "tags", name: "Multi-lookup", width: 175, formatter: MultiLookupFormatter, valueList: ["Design", "Internal", "Customer", "Research"], editor: MultiLookupEditor },
|
|
101
|
+
{ id: "owner", name: "Autosuggest", width: 145, formatter: LookupFormatter, valueList: ["Alex Morgan", "Sam Weber", "Jordan Lee", "Robin Taylor"], editor: AutoSuggestEditor },
|
|
102
|
+
{ id: "detail", name: "Tooltip", width: 230, formatter: TooltipFormatter, editor: TextEditor }
|
|
103
|
+
];
|
|
104
|
+
const editorCatalog = new GridComponent(editorSamples, editorSampleColumns, {
|
|
105
|
+
primaryKey: "id", editable: true, autoEdit: false, rowHeight: 38
|
|
106
|
+
}, "340px");
|
|
107
|
+
const editorCatalogStatus: IJSLVNode = { tag: "p", content: "Double-click a cell or press F2 to edit. Enter commits; Escape cancels.", attr: { role: "status", class: "sample-status" } };
|
|
108
|
+
editorCatalog.initEvents(grid => grid.onAfterEdit.subscribe((_event, args) => {
|
|
109
|
+
editorCatalogStatus.content = "Updated " + args.column.name + " for sample " + args.item.id + ".";
|
|
110
|
+
JSLRender.lastCreatedRenderer?.refresh();
|
|
111
|
+
}));
|
|
47
112
|
const grouped = new GridComponent(data(45), columns(), { primaryKey: "id" }, "320px");
|
|
48
113
|
grouped.initEvents(grid => grid.dataView.setGrouping({
|
|
49
114
|
getter: "team", formatter: group => group.value + " · " + group.count + " projects", aggregators: [], displayTotalsRow: false
|
|
@@ -123,11 +188,12 @@ export const gridShowcase: IJSLVNode = {
|
|
|
123
188
|
{ tag: "div", attr: { class: "hero-copy" }, children: [
|
|
124
189
|
{ tag: "p", content: "THE ESSENTIALS / 22", attr: { class: "eyebrow" } },
|
|
125
190
|
{ tag: "h2", content: "Grids", attr: { id: "grids-title", class: "section-title" } },
|
|
126
|
-
text("p", "Representative data, editing, grouping, and scrolling examples for the grid redesign.")
|
|
191
|
+
text("p", "Representative data, formatting, editing, grouping, and scrolling examples for the grid redesign.")
|
|
127
192
|
] },
|
|
128
193
|
{ tag: "div", attr: { class: "grid-samples" }, children: [
|
|
129
194
|
card("grid-basic", "Browse and sort", "30 projects. Click a heading to sort, then Shift-click other headings to add sort levels. Numbers show sorting priority. Select rows and resize or reorder columns.", basic),
|
|
130
195
|
card("grid-editable", "Edit in place", "Double-click an editable cell or press F2. Enter commits and Escape cancels. Editors retain their existing behavior and styling in this step.", editable, [edits]),
|
|
196
|
+
card("grid-editors-formatters", "Formatters and editors", "Try text, multiline, numeric, date, time, boolean, lookup, multi-lookup, and autosuggest editors. The adjacent columns demonstrate the matching display formatters; TimeFormatter displays a Date value, while TimeEditor edits a time string.", editorCatalog, [editorCatalogStatus]),
|
|
131
197
|
card("grid-grouped", "Grouped projects", "45 projects grouped by team. Expand and collapse groups independently.", grouped, [
|
|
132
198
|
{ tag: "div", attr: { class: "sample-row" }, children: [
|
|
133
199
|
action("Collapse groups", () => { grouped.getRef().then(grid => grid.dataView.collapseAllGroups()); }),
|
|
@@ -144,7 +210,7 @@ export const gridShowcase: IJSLVNode = {
|
|
|
144
210
|
{ tag: "details", attr: { class: "usage" }, children: [
|
|
145
211
|
text("summary", "Grid showcase scope"),
|
|
146
212
|
text("p", 'NumericFormatter supplies right-aligned cells; NumericFormatter and DateFormatter apply tabular numerals. Set headerCssClass: "right" or "center" explicitly to match a column’s content. Header alignment is never inferred from its formatter.'),
|
|
147
|
-
text("p", "These examples use the existing GridComponent constructor, column definitions, editors, and DataView APIs. The default row height is
|
|
213
|
+
text("p", "These examples use the existing GridComponent constructor, column definitions, editors, and DataView APIs. The default row height is 40 px; rowHeight can retain a different density. Column headers fit up to two lines. Core presentation follows the shared theme and inherits the host's typography. Filter dialogs, menus, and lifecycle behavior are reserved for later steps.")
|
|
148
214
|
] }
|
|
149
215
|
]
|
|
150
216
|
};
|
|
@@ -8,7 +8,42 @@
|
|
|
8
8
|
-webkit-font-smoothing: antialiased;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
/* Keep the showcase layout predictable without resetting sizing on library DOM.
|
|
12
|
+
Grid cells and component internals should exercise their own box model. */
|
|
13
|
+
:where(
|
|
14
|
+
.skip-link, .sidebar, .brand, .brand-caption, .brand-mark, .nav-label, .nav-item,
|
|
15
|
+
.nav-number, .nav-icon, .sidebar-note, .status-dot, main, .page-meta, .page-tools,
|
|
16
|
+
.theme-picker, .version, .hero, .eyebrow, .hero-preview, .preview-caption,
|
|
17
|
+
.preview-note, .hero-description, .overview-bar, .overview-label, .overview-stat,
|
|
18
|
+
.sample-grid, .sample-card, .card-heading, .sample-number, .card-description,
|
|
19
|
+
.sample-row, .sample-status, .sample-code, .inline-note, .showcase-icon,
|
|
20
|
+
.format-bold, .format-italic, .format-underline, .sample-divider, .size-row,
|
|
21
|
+
.size-sample, .size-caption, .context-panel, .context-body, .context-eyebrow,
|
|
22
|
+
.context-copy, .context-footer, .count-badge, .usage, .page-footer,
|
|
23
|
+
.component-section, .showcase-dialog-copy, .dialog-summary, .dialog-summary-label,
|
|
24
|
+
.dialog-form-field, .dialog-member, .dialog-member-avatar, .dialog-member-role,
|
|
25
|
+
.dialog-help, .dialog-check, .tabs-example-copy, .tabs-draft-input,
|
|
26
|
+
.tabs-example-check, .tabs-header-badge, .tabs-rich-header, .date-hero-preview,
|
|
27
|
+
.date-showcase-field, .slider-sample, .showcase-slider, .slider-readout,
|
|
28
|
+
.slider-with-tooltip, .slider-orientations, .showcase-slider-vertical,
|
|
29
|
+
.slider-disabled-example, .showcase-calendar, .select-showcase-field,
|
|
30
|
+
.select-hero-preview, .select-person, .select-person-avatar, .select-person-copy,
|
|
31
|
+
.checks-expander-preview, .checkbox-stack, .checkbox-child-list,
|
|
32
|
+
.expander-example-copy, .expander-preview-copy, .expander-rich-header,
|
|
33
|
+
.expander-stack, .expander-custom-icon, .fields-preview, .field-sample,
|
|
34
|
+
.unstyled-input-sample, .showcase-inline-input, .composed-fields,
|
|
35
|
+
.notification-samples, .notification-action-copy, .loading-label-samples,
|
|
36
|
+
.loading-demo, .loading-demo-panel, .navigation-preview,
|
|
37
|
+
.navigation-compact-sample, .navigation-rich-brand, .navigation-brand-mark,
|
|
38
|
+
.remaining-demo, .layout-demo-panel, .tree-demo-content, .grid-samples,
|
|
39
|
+
.grid-sample-card, .grid-sample-host, .format-preview
|
|
40
|
+
) { box-sizing: border-box; }
|
|
41
|
+
|
|
42
|
+
/* Showcase-owned native controls also keep the page's intended dimensions. */
|
|
43
|
+
.theme-picker select, .dialog-form-field input, .dialog-form-field textarea,
|
|
44
|
+
.dialog-check input, .tabs-draft-input, .tabs-example-check input {
|
|
45
|
+
box-sizing: border-box;
|
|
46
|
+
}
|
|
12
47
|
body { margin: 0; font-size: 14px; line-height: 1.6; }
|
|
13
48
|
a { color: inherit; }
|
|
14
49
|
.skip-link { position: fixed; top: -5rem; left: 1rem; z-index: 5; padding: 0.5rem 1rem; background: var(--jsl-color-surface); }
|
|
@@ -219,7 +254,7 @@ h2 { font-size: 14px; font-weight: 600; margin: 0; letter-spacing: -0.15px; }
|
|
|
219
254
|
.navigation-brand-mark { display: grid; place-items: center; width: 24px; height: 24px; border-radius: 6px; background: var(--jsl-color-accent); color: var(--jsl-color-on-accent); font-size: 12px; }
|
|
220
255
|
.remaining-demo { display: flex; flex-direction: column; gap: 20px; margin-top: 24px; }
|
|
221
256
|
.remaining-demo h3 { margin: 0; font-size: 14px; font-weight: 600; }
|
|
222
|
-
.layout-demo-panel { padding: 16px; background: var(--jsl-color-surface); color: var(--jsl-color-text); overflow: auto; height: 100%;
|
|
257
|
+
.layout-demo-panel { padding: 16px; background: var(--jsl-color-surface); color: var(--jsl-color-text); overflow: auto; height: 100%; }
|
|
223
258
|
.layout-demo-panel p { color: var(--jsl-color-text-secondary); font-size: 13px; line-height: 1.6; }
|
|
224
259
|
.tree-demo-content { display: flex; align-items: center; gap: 8px; }
|
|
225
260
|
.grid-samples { display: flex; flex-direction: column; gap: 24px; margin: 24px 0; }
|
|
@@ -150,7 +150,13 @@ const cards = [
|
|
|
150
150
|
button({ content: "Save changes", icon: icon("check"), click: () => { saveStatus.content = "All changes saved."; } })
|
|
151
151
|
], "context-footer")
|
|
152
152
|
], "context-panel"),
|
|
153
|
-
node("p", "The same
|
|
153
|
+
node("p", "The same classes style component buttons, manual buttons, and links.", "inline-note"),
|
|
154
|
+
...["", " jsl-button-outline", " jsl-button-ghost"].map(variant => group([
|
|
155
|
+
button({ content: "View projects", variant: variant.includes("outline") ? "outline" : variant.includes("ghost") ? "ghost" : "solid" }),
|
|
156
|
+
{ tag: "button", attr: { type: "button", class: "jsl-button" + variant }, content: "View projects" },
|
|
157
|
+
{ tag: "a", attr: { href: "#grids", class: "jsl-button" + variant }, content: "View projects" }
|
|
158
|
+
])),
|
|
159
|
+
node("p", "Each row shows a ButtonComponent, a manual <button>, and an <a> link.", "inline-note")
|
|
154
160
|
]),
|
|
155
161
|
card("customization", "07", "Make it yours", "Custom content and application colors still fit the same button API.", [
|
|
156
162
|
group([
|
|
@@ -62,6 +62,32 @@ classes should alter those!
|
|
|
62
62
|
padding-right: 4px;
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
/* The themed Select control has a comfortable form-field minimum height.
|
|
66
|
+
In a grid editor it must fit the row instead of imposing that height. */
|
|
67
|
+
.slick-cell.editable .select-comp {
|
|
68
|
+
display: flex;
|
|
69
|
+
height: 100%;
|
|
70
|
+
min-height: 0;
|
|
71
|
+
max-width: none;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.slick-cell.editable .select-comp > .sel-expander {
|
|
75
|
+
height: 100%;
|
|
76
|
+
min-height: 0;
|
|
77
|
+
padding: 0 0.5rem;
|
|
78
|
+
border-radius: 0.25rem;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.slick-cell.editable .select-comp.multiple > .sel-expander {
|
|
82
|
+
padding-top: 0;
|
|
83
|
+
padding-bottom: 0;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.slick-cell.editable .select-comp .sel-chip {
|
|
87
|
+
padding: 0.0625rem 0.375rem;
|
|
88
|
+
font-size: 0.6875rem;
|
|
89
|
+
}
|
|
90
|
+
|
|
65
91
|
.slick-group {
|
|
66
92
|
border-bottom: 2px solid var(--jsl-color-border-subtle);
|
|
67
93
|
}
|
|
@@ -183,9 +209,12 @@ classes should alter those!
|
|
|
183
209
|
margin-bottom: 0;
|
|
184
210
|
display: inline-block;
|
|
185
211
|
max-width: 100%;
|
|
212
|
+
max-height: 2.4em;
|
|
186
213
|
overflow: hidden;
|
|
187
214
|
text-overflow: ellipsis;
|
|
188
|
-
|
|
215
|
+
white-space: normal;
|
|
216
|
+
line-height: 1.2;
|
|
217
|
+
vertical-align: middle;
|
|
189
218
|
}
|
|
190
219
|
.slick-header-sortable .slick-column-name { max-width: calc(100% - 1.25rem); }
|
|
191
220
|
.slick-header-column .slick-sort-indicator,
|
|
@@ -195,8 +224,8 @@ classes should alter those!
|
|
|
195
224
|
position: relative;
|
|
196
225
|
vertical-align: top;
|
|
197
226
|
margin: 0 0 0 .375rem;
|
|
198
|
-
height: var(--jsl-grid-header-height,
|
|
199
|
-
line-height: var(--jsl-grid-header-height,
|
|
227
|
+
height: var(--jsl-grid-header-height, 40px);
|
|
228
|
+
line-height: var(--jsl-grid-header-height, 40px);
|
|
200
229
|
color: var(--jsl-color-accent);
|
|
201
230
|
}
|
|
202
231
|
.slick-header-column .slick-sort-indicator { width: .65rem; }
|
package/lib/grid/slick.grid.css
CHANGED
|
@@ -46,10 +46,10 @@ classes should alter those!
|
|
|
46
46
|
float: left;
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
/* Column headers
|
|
49
|
+
/* Column headers fit up to two lines; grouped headers retain their existing size. */
|
|
50
50
|
.slick-header-column.ui-state-default {
|
|
51
|
-
height: var(--jsl-grid-header-height,
|
|
52
|
-
line-height: var(--jsl-grid-header-height,
|
|
51
|
+
height: var(--jsl-grid-header-height, 40px);
|
|
52
|
+
line-height: var(--jsl-grid-header-height, 40px);
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
.slick-footerrow-column.ui-state-default {
|
package/lib/grid/slick.grid.js
CHANGED
|
@@ -28,7 +28,7 @@ export function SlickGrid(container, data, columns, options) {
|
|
|
28
28
|
alwaysShowHorizontalScroll: false,
|
|
29
29
|
alwaysAllowHorizontalScroll: false,
|
|
30
30
|
explicitInitialization: false,
|
|
31
|
-
rowHeight:
|
|
31
|
+
rowHeight: 40,
|
|
32
32
|
defaultColumnWidth: 80,
|
|
33
33
|
enableAddRow: false,
|
|
34
34
|
leaveSpaceForNewRows: false,
|