@castlabs/ui 7.22.0 → 7.23.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.
@@ -17,9 +17,9 @@ declare module '@castlabs/ui/dist/castlabs-ui.module.js' {
17
17
 
18
18
  export function clFormReset (id: string, fields?: any[]): void
19
19
  export function clFormFieldReset (ref: any, oldValue: any): void
20
- export function clFormIsValid (id: string): boolean
21
- export function clFormIsValidAsync (id: string): Promise<boolean>
22
- export function clFormFieldFocusFirstInvalid (id: string): void
20
+ export function clFormIsValid (id: string, moreSelectors?: string[], triggerSelectors?: string[]): boolean
21
+ export function clFormIsValidAsync (id: string, moreSelectors?: string[], triggerSelectors?: string[]): Promise<boolean>
22
+ export function clFormFieldFocusFirstInvalid (id: string, moreSelectors?: string[]): void
23
23
  export function clFormAutofocus (id: string): void
24
24
 
25
25
  export function clFormatFilesize (bytes: number): string
@@ -43,20 +43,21 @@ declare module '@castlabs/ui/dist/castlabs-ui.module.js' {
43
43
  order?: string,
44
44
  caseSensitive?: boolean
45
45
  ): object[]
46
- export function clTableSorter (
46
+ export type ClTableSorter<Type> = {
47
+ col: number
48
+ order: 'ASC' | 'DESC' | 'NONE'
49
+ set: (col: number, order: string) => void
50
+ get: (col: number) => string
51
+ sorted: Type[]
52
+ sorter: () => any
53
+ sort: (col?: number, order?: string) => 'ASC' | 'DESC' | 'NONE'
54
+ }
55
+ export function clTableSorter<Type> (
47
56
  sort: (col: number, order: 'ASC' | 'DESC') => { sorted: object[], sortedOrder: 'ASC' | 'DESC' },
48
57
  initialCol?: number,
49
58
  initialOrder?: string
50
- ): any
51
- export function clTableSorterObjects<Type> (dataCallback: () => Type[], keys: string[], initialCol?: number, pageNoCallback?: () => number, pageSizeCallback?: () => number): {
52
- col: number,
53
- order: 'ASC' | 'DESC' | 'NONE',
54
- set: (col: number, order: string) => void,
55
- get: (col: number) => string,
56
- sorted: Type[],
57
- sorter: () => any,
58
- sort: (col?: number, order?: string) => 'ASC' | 'DESC' | 'NONE',
59
- }
59
+ ): ClTableSorter<Type>
60
+ export function clTableSorterObjects<Type> (dataCallback: () => Type[], keys: string[], initialCol?: number, pageNoCallback?: () => number, pageSizeCallback?: () => number): ClTableSorter<Type>
60
61
  export function clDebounce (callback: any, ms?: number, key?: string): void
61
62
  export function clSanitizeId (id: string): string
62
63
 
@@ -1,4 +1,4 @@
1
- /* @castlabs/ui v7.22.0 */
1
+ /* @castlabs/ui v7.23.1 */
2
2
 
3
3
  /*!
4
4
  * Bootstrap v5.3.8 (https://getbootstrap.com/)
@@ -216,9 +216,9 @@ function clMatomoSetup (siteId = 1, domain = 'castlabs.com') {
216
216
  })()
217
217
  }
218
218
 
219
- export function clTableSetupResize (id) {
219
+ export function clTableSetupResize (id, auto = false) {
220
220
  document.querySelectorAll(`#${id} .cl-resizer`).forEach(resizer => {
221
- clTableSetResizeListeners(resizer)
221
+ clTableSetResizeListeners(resizer, auto)
222
222
  })
223
223
  }
224
224
 
@@ -249,12 +249,15 @@ export function clTableSortStatic (id, col) {
249
249
  let lastX = 0
250
250
  let nextCol = null
251
251
 
252
- function clTableSetResizeListeners (resizer) {
252
+ function clTableSetResizeListeners (resizer, auto = false) {
253
253
  lastX = 0
254
254
  nextCol = null
255
255
  resizer.addEventListener('mousedown', mousedown)
256
256
  document.addEventListener('mousemove', mousemove)
257
257
  document.addEventListener('mouseup', mouseup)
258
+ if (auto) {
259
+ resizer.addEventListener('click', mouseclick)
260
+ }
258
261
  }
259
262
 
260
263
  const mousedown = mousedown => {
@@ -269,28 +272,25 @@ const mousemove = mousemove => {
269
272
  const delta = mousemove.pageX - lastX
270
273
  lastX = mousemove.pageX
271
274
 
272
- const colIndex = getColIndex(nextCol)
275
+ const colIndex = getColIndex(nextCol) - 1
273
276
  const id = mousemove.target?.closest('table')?.id ?? 'unknown'
274
277
  const nowrap = !!document.querySelector(`#${id}.cl-table-nowrap`) // are we in nowrap mode?
275
278
  if (nowrap) {
276
- // in nowrap mode we only adjust the first col
277
- document
278
- .querySelectorAll(
279
- `#${id} tr th:nth-child(${colIndex - 1})` // , #${id} tr td:nth-child(${colIndex - 1})
280
- )
281
- .forEach(cel => {
282
- const width =
283
- (cel.style.maxWidth === '' ? cel.offsetWidth : parseInt(cel.style.maxWidth)) + delta
284
- cel.style.width = (width > 32 ? width : 32) + 'px'
285
- cel.style.maxWidth = (width > 32 ? width : 32) + 'px'
286
- })
279
+ // in nowrap mode we only adjust the thead col
280
+ document.querySelectorAll(`#${id} tr th:nth-child(${colIndex})`).forEach(cel => {
281
+ const width =
282
+ (cel.style.maxWidth === '' ? cel.offsetWidth : parseInt(cel.style.maxWidth)) + delta
283
+ cel.style.width = (width > 32 ? width : 32) + 'px'
284
+ cel.style.maxWidth = (width > 32 ? width : 32) + 'px'
285
+ cel.clWidth = 0
286
+ })
287
287
  } else {
288
288
  // in regular mode we shrink one and enlarge the other col
289
- document.querySelectorAll(`#${id} tr td:nth-child(${colIndex - 1})`).forEach(cel => {
289
+ document.querySelectorAll(`#${id} tr td:nth-child(${colIndex})`).forEach(cel => {
290
290
  const width = cel.offsetWidth + delta
291
291
  cel.style.width = (width > 32 ? width : 32) + 'px'
292
292
  })
293
- document.querySelectorAll(`#${id} tr td:nth-child(${colIndex})`).forEach(cel => {
293
+ document.querySelectorAll(`#${id} tr td:nth-child(${colIndex + 1})`).forEach(cel => {
294
294
  const width = cel.offsetWidth - delta
295
295
  cel.style.width = (width > 32 ? width : 32) + 'px'
296
296
  })
@@ -306,8 +306,53 @@ const mouseup = mouseup => {
306
306
  }
307
307
  }
308
308
 
309
+ const mouseclick = click => {
310
+ if (click.detail === 2) {
311
+ const table = click.target?.closest('table')
312
+ if (!table) return
313
+ click.preventDefault()
314
+
315
+ const id = table?.id ?? 'unknown'
316
+ const th = click.target?.closest('th')
317
+ const colIndex = Array.from(th.parentElement.children).indexOf(th)
318
+
319
+ // set all to something small so scrollWidth is correct for all-smaller items (head only)
320
+ document.querySelectorAll(`#${id} th:nth-child(${colIndex})`).forEach(cel => {
321
+ cel.style.width = '1px'
322
+ cel.style.maxWidth = '1px'
323
+ })
324
+
325
+ // iterate over all rows and col index (thead + tbody), find max cel width
326
+ let maxCellWidth = 0
327
+ document
328
+ .querySelectorAll(`#${id} th:nth-child(${colIndex}), #${id} td:nth-child(${colIndex})`)
329
+ .forEach(cel => {
330
+ if ((cel.colSpan ?? 1) === 1) {
331
+ const cellWidth = cel.scrollWidth
332
+ maxCellWidth = cellWidth > maxCellWidth ? cellWidth : maxCellWidth
333
+ }
334
+ })
335
+
336
+ // set all outer width to inner (head only)
337
+ document.querySelectorAll(`#${id} th:nth-child(${colIndex})`).forEach(cel => {
338
+ if (cel.clWidth === maxCellWidth) {
339
+ // revert to un-maximized
340
+ cel.clWidth = 0
341
+ cel.style.removeProperty('width')
342
+ cel.style.removeProperty('max-width')
343
+ } else {
344
+ // maximize
345
+ const padding = 8 * 2
346
+ cel.clWidth = maxCellWidth
347
+ cel.style.width = `${cel.clWidth + padding}px`
348
+ cel.style.maxWidth = `${cel.clWidth + padding}px`
349
+ }
350
+ })
351
+ }
352
+ }
353
+
309
354
  function getColIndex (col) {
310
- // calculate index of a col in a row
355
+ // calculate index of a col in a row, 1-based
311
356
  let colNo = 0
312
357
  while (col) {
313
358
  colNo++
@@ -1120,9 +1165,11 @@ export function clFormFieldReset (ref, oldValue) {
1120
1165
  * Will autofocus the first wrong input (if any).
1121
1166
  *
1122
1167
  * @param {string} id ID of form.
1168
+ * @param {string[]} moreSelectors Optional additional classes that mark invalid fields.
1169
+ * @param {string[]} moreTriggers Optional additional classes mark elements that should be focused & blurred to trigger validation.
1123
1170
  * @return {boolean} True if form is OK.
1124
1171
  */
1125
- export function clFormIsValid (id) {
1172
+ export function clFormIsValid (id, moreSelectors = [], moreTriggers = []) {
1126
1173
  const form = document.querySelector(`#${id}`)
1127
1174
  if (!form) {
1128
1175
  // not a form at all
@@ -1133,16 +1180,18 @@ export function clFormIsValid (id) {
1133
1180
  // force trigger validation
1134
1181
  // as some inputs like the ace editor do custom validation based on events,
1135
1182
  // we fire various event/element/class combinations to make sure custom handlers run.
1136
- document.querySelectorAll(`#${id} input, #${id} .form-control`).forEach(input => {
1137
- input.focus()
1138
- input.blur()
1139
- })
1183
+ document
1184
+ .querySelectorAll(clSubselectorQuery(`#${id}`, ['input', '.form-control', ...moreTriggers]))
1185
+ .forEach(input => {
1186
+ input.focus()
1187
+ input.blur()
1188
+ })
1140
1189
 
1141
1190
  if (
1142
1191
  form.checkValidity() === false ||
1143
- document.querySelector(`#${id} :invalid, #${id} .invalid`)
1192
+ document.querySelector(clSubselectorQuery(`#${id}`, [':invalid', '.invalid', ...moreSelectors]))
1144
1193
  ) {
1145
- clFormFieldFocusFirstInvalid(id)
1194
+ clFormFieldFocusFirstInvalid(id, moreSelectors)
1146
1195
  return false
1147
1196
  }
1148
1197
 
@@ -1155,9 +1204,11 @@ export function clFormIsValid (id) {
1155
1204
  * Useful for complicated inputs like ACE editor.
1156
1205
  *
1157
1206
  * @param {string} id ID of form.
1207
+ * @param {string[]} moreSelectors Optional additional classes that mark invalid fields.
1208
+ * @param {string[]} moreTriggers Optional additional classes mark elements that should be focused & blurred to trigger validation.
1158
1209
  * @return {boolean} Promise of true if form is OK.
1159
1210
  */
1160
- export async function clFormIsValidAsync (id) {
1211
+ export async function clFormIsValidAsync (id, moreSelectors = [], moreTriggers = []) {
1161
1212
  const form = document.getElementById(id)
1162
1213
  if (!form) {
1163
1214
  // not a form at all
@@ -1168,10 +1219,12 @@ export async function clFormIsValidAsync (id) {
1168
1219
  // force trigger validation
1169
1220
  // as some inputs like the ace editor do custom validation based on events,
1170
1221
  // we fire various event/element/class combinations to make sure custom handlers run.
1171
- document.querySelectorAll(`#${id} input, #${id} .form-control`).forEach(input => {
1172
- input.focus()
1173
- input.blur()
1174
- })
1222
+ document
1223
+ .querySelectorAll(clSubselectorQuery(`#${id}`, ['input', '.form-control', ...moreTriggers]))
1224
+ .forEach(input => {
1225
+ input.focus()
1226
+ input.blur()
1227
+ })
1175
1228
  document.querySelectorAll(`#${id} .ace_editor`).forEach(input => {
1176
1229
  input.click()
1177
1230
  })
@@ -1181,9 +1234,11 @@ export async function clFormIsValidAsync (id) {
1181
1234
  setTimeout(() => {
1182
1235
  if (
1183
1236
  form.checkValidity() === false ||
1184
- document.querySelector(`#${id} :invalid, #${id} .invalid`)
1237
+ document.querySelector(
1238
+ clSubselectorQuery(`#${id}`, [':invalid', '.invalid', ...moreSelectors])
1239
+ )
1185
1240
  ) {
1186
- clFormFieldFocusFirstInvalid(id)
1241
+ clFormFieldFocusFirstInvalid(id, moreSelectors)
1187
1242
  resolve(false)
1188
1243
  return
1189
1244
  }
@@ -1208,12 +1263,23 @@ export function clFormAutofocus (formId) {
1208
1263
  * Set the focus in the first invalid field of the given form (if any).
1209
1264
  *
1210
1265
  * @param {string} formId HTML ID of form.
1266
+ * @param {string[]} moreSelectors Optional additional classes that mark invalid fields.
1211
1267
  */
1212
- export function clFormFieldFocusFirstInvalid (formId) {
1213
- const invalid = document.querySelector(`#${formId} :invalid, #${formId} .invalid`)
1268
+ export function clFormFieldFocusFirstInvalid (formId, moreSelectors = []) {
1269
+ const invalid = document.querySelector(
1270
+ clSubselectorQuery(`#${formId}`, [':invalid', '.invalid', ...moreSelectors])
1271
+ )
1214
1272
  invalid?.focus()
1215
1273
  }
1216
1274
 
1275
+ export function clSubselectorQuery (main, subselectors) {
1276
+ const selectors = []
1277
+ for (const subselector of subselectors) {
1278
+ selectors.push(`${main} ${subselector}`)
1279
+ }
1280
+ return selectors.join(', ')
1281
+ }
1282
+
1217
1283
  // -----------------------------------------------------------------------------
1218
1284
  // --- formatters --------------------------------------------------------------
1219
1285
  // -----------------------------------------------------------------------------