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.
@@ -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 { DateFormatter, NumericFormatter, CheckboxFormatter } from "../../grid/slick.formatters";
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 36 px; rowHeight can retain a different density. Core presentation follows the shared theme and inherits the host's typography. Editors, filter dialogs, menus, and lifecycle behavior are reserved for later steps.")
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
  };
@@ -0,0 +1,10 @@
1
+ {
2
+ "private": true,
3
+ "source": "index.html",
4
+ "targets": {
5
+ "showcase": {
6
+ "context": "browser",
7
+ "distDir": "../../dist/showcase"
8
+ }
9
+ }
10
+ }
@@ -8,7 +8,42 @@
8
8
  -webkit-font-smoothing: antialiased;
9
9
  }
10
10
 
11
- * { box-sizing: border-box; }
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%; box-sizing: border-box; }
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 styles also apply to vanilla buttons using .jsl-button.", "inline-note")
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
- vertical-align: top;
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, 26px);
199
- line-height: var(--jsl-grid-header-height, 26px);
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; }
@@ -46,10 +46,10 @@ classes should alter those!
46
46
  float: left;
47
47
  }
48
48
 
49
- /* Column headers are 6px taller; grouped headers retain their existing size. */
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, 26px);
52
- line-height: var(--jsl-grid-header-height, 26px);
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 {
@@ -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: 36,
31
+ rowHeight: 40,
32
32
  defaultColumnWidth: 80,
33
33
  enableAddRow: false,
34
34
  leaveSpaceForNewRows: false,