@ship-ui/core 0.22.14 → 0.22.16
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/README.md +46 -0
- package/assets/mcp/components.json +605 -4
- package/bin/src/scanner.zig +9 -18
- package/fesm2022/ship-ui-core-sh-form-field-experimental.mjs +17 -2
- package/fesm2022/ship-ui-core-sh-form-field-experimental.mjs.map +1 -1
- package/fesm2022/ship-ui-core-ship-a11y-keybindings.mjs +442 -0
- package/fesm2022/ship-ui-core-ship-a11y-keybindings.mjs.map +1 -0
- package/fesm2022/ship-ui-core-ship-accordion.mjs +1 -0
- package/fesm2022/ship-ui-core-ship-accordion.mjs.map +1 -1
- package/fesm2022/ship-ui-core-ship-alert.mjs +3 -2
- package/fesm2022/ship-ui-core-ship-alert.mjs.map +1 -1
- package/fesm2022/ship-ui-core-ship-blueprint.mjs +23 -10
- package/fesm2022/ship-ui-core-ship-blueprint.mjs.map +1 -1
- package/fesm2022/ship-ui-core-ship-checkbox.mjs +16 -14
- package/fesm2022/ship-ui-core-ship-checkbox.mjs.map +1 -1
- package/fesm2022/ship-ui-core-ship-color-picker.mjs +3 -1
- package/fesm2022/ship-ui-core-ship-color-picker.mjs.map +1 -1
- package/fesm2022/ship-ui-core-ship-datepicker.mjs +67 -33
- package/fesm2022/ship-ui-core-ship-datepicker.mjs.map +1 -1
- package/fesm2022/ship-ui-core-ship-dialog.mjs +14 -5
- package/fesm2022/ship-ui-core-ship-dialog.mjs.map +1 -1
- package/fesm2022/ship-ui-core-ship-divider.mjs +4 -2
- package/fesm2022/ship-ui-core-ship-divider.mjs.map +1 -1
- package/fesm2022/ship-ui-core-ship-editor.mjs +2673 -0
- package/fesm2022/ship-ui-core-ship-editor.mjs.map +1 -0
- package/fesm2022/ship-ui-core-ship-icon.mjs +2 -2
- package/fesm2022/ship-ui-core-ship-icon.mjs.map +1 -1
- package/fesm2022/ship-ui-core-ship-list.mjs +4 -2
- package/fesm2022/ship-ui-core-ship-list.mjs.map +1 -1
- package/fesm2022/ship-ui-core-ship-menu.mjs +8 -5
- package/fesm2022/ship-ui-core-ship-menu.mjs.map +1 -1
- package/fesm2022/ship-ui-core-ship-popover.mjs +14 -5
- package/fesm2022/ship-ui-core-ship-popover.mjs.map +1 -1
- package/fesm2022/ship-ui-core-ship-progress-bar.mjs +5 -1
- package/fesm2022/ship-ui-core-ship-progress-bar.mjs.map +1 -1
- package/fesm2022/ship-ui-core-ship-radio.mjs +16 -14
- package/fesm2022/ship-ui-core-ship-radio.mjs.map +1 -1
- package/fesm2022/ship-ui-core-ship-select.mjs +9 -9
- package/fesm2022/ship-ui-core-ship-select.mjs.map +1 -1
- package/fesm2022/ship-ui-core-ship-sidenav.mjs +2 -2
- package/fesm2022/ship-ui-core-ship-sidenav.mjs.map +1 -1
- package/fesm2022/ship-ui-core-ship-spinner.mjs +3 -1
- package/fesm2022/ship-ui-core-ship-spinner.mjs.map +1 -1
- package/fesm2022/ship-ui-core-ship-spotlight.mjs +77 -24
- package/fesm2022/ship-ui-core-ship-spotlight.mjs.map +1 -1
- package/fesm2022/ship-ui-core-ship-table.mjs +517 -138
- package/fesm2022/ship-ui-core-ship-table.mjs.map +1 -1
- package/fesm2022/ship-ui-core-ship-theme-toggle.mjs +2 -2
- package/fesm2022/ship-ui-core-ship-theme-toggle.mjs.map +1 -1
- package/fesm2022/ship-ui-core-ship-toggle-card.mjs +24 -3
- package/fesm2022/ship-ui-core-ship-toggle-card.mjs.map +1 -1
- package/fesm2022/ship-ui-core-ship-toggle.mjs +16 -14
- package/fesm2022/ship-ui-core-ship-toggle.mjs.map +1 -1
- package/fesm2022/ship-ui-core-ship-tree.mjs +2 -2
- package/fesm2022/ship-ui-core-ship-tree.mjs.map +1 -1
- package/fesm2022/ship-ui-core-ship-virtual-scroll.mjs +2 -2
- package/fesm2022/ship-ui-core-ship-virtual-scroll.mjs.map +1 -1
- package/fesm2022/ship-ui-core.mjs +36 -23
- package/fesm2022/ship-ui-core.mjs.map +1 -1
- package/package.json +33 -2
- package/types/ship-ui-core-sh-form-field-experimental.d.ts +2 -0
- package/types/ship-ui-core-ship-a11y-keybindings.d.ts +102 -0
- package/types/ship-ui-core-ship-blueprint.d.ts +2 -1
- package/types/ship-ui-core-ship-checkbox.d.ts +2 -1
- package/types/ship-ui-core-ship-datepicker.d.ts +2 -0
- package/types/ship-ui-core-ship-editor.d.ts +168 -0
- package/types/ship-ui-core-ship-radio.d.ts +2 -1
- package/types/ship-ui-core-ship-spotlight.d.ts +1 -1
- package/types/ship-ui-core-ship-table.d.ts +13 -1
- package/types/ship-ui-core-ship-toggle-card.d.ts +1 -0
- package/types/ship-ui-core-ship-toggle.d.ts +2 -1
- package/types/ship-ui-core.d.ts +3 -0
- package/bin/ship-fg-scanner +0 -0
|
@@ -231,6 +231,430 @@
|
|
|
231
231
|
],
|
|
232
232
|
"examples": []
|
|
233
233
|
},
|
|
234
|
+
{
|
|
235
|
+
"name": "ShipEditor",
|
|
236
|
+
"selector": "sh-editor",
|
|
237
|
+
"path": "core/projects/ship-ui/ship-editor/ship-editor.ts",
|
|
238
|
+
"description": "### Usage\n\nShipEditor is a premium, config-driven WYSIWYG rich text editor. It allows typing in design view, editing raw source code, and can serialize to `html`, `markdown`, or `json` configuration dynamically.\n\n### Features\n\n- Three output formats: HTML strings, Markdown text, and custom structured block/mark JSON.\n- ControlValueAccessor integration for Reactive and Template-driven forms.\n- Monospace code-view for editing source code directly.\n- Dynamic toolbar selection tracking.\n- Word and character count reporting in the status footer.",
|
|
239
|
+
"keywords": [
|
|
240
|
+
"editor",
|
|
241
|
+
"wysiwyg",
|
|
242
|
+
"richtext",
|
|
243
|
+
"html",
|
|
244
|
+
"markdown",
|
|
245
|
+
"json",
|
|
246
|
+
"input",
|
|
247
|
+
"form"
|
|
248
|
+
],
|
|
249
|
+
"inputs": [
|
|
250
|
+
{
|
|
251
|
+
"name": "format",
|
|
252
|
+
"type": "'json' | 'html' | 'markdown'",
|
|
253
|
+
"description": "",
|
|
254
|
+
"defaultValue": "'html'"
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
"name": "placeholder",
|
|
258
|
+
"type": "string",
|
|
259
|
+
"description": "",
|
|
260
|
+
"defaultValue": "'Type your content here...'"
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
"name": "readonly",
|
|
264
|
+
"type": "boolean",
|
|
265
|
+
"description": "",
|
|
266
|
+
"defaultValue": "false"
|
|
267
|
+
},
|
|
268
|
+
{
|
|
269
|
+
"name": "toolbar",
|
|
270
|
+
"type": "boolean",
|
|
271
|
+
"description": "",
|
|
272
|
+
"defaultValue": "true"
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
"name": "color",
|
|
276
|
+
"type": "ShipColor | null",
|
|
277
|
+
"description": "",
|
|
278
|
+
"defaultValue": "null"
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
"name": "variant",
|
|
282
|
+
"type": "'base' | 'type-b' | null",
|
|
283
|
+
"description": "",
|
|
284
|
+
"defaultValue": "'base'"
|
|
285
|
+
},
|
|
286
|
+
{
|
|
287
|
+
"name": "customCommands",
|
|
288
|
+
"type": "ShipEditorCommand[]",
|
|
289
|
+
"description": "",
|
|
290
|
+
"defaultValue": "[]"
|
|
291
|
+
},
|
|
292
|
+
{
|
|
293
|
+
"name": "slashCommands",
|
|
294
|
+
"type": "boolean | string[]",
|
|
295
|
+
"description": "",
|
|
296
|
+
"defaultValue": "true"
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
"name": "showFormats",
|
|
300
|
+
"type": "boolean",
|
|
301
|
+
"description": "",
|
|
302
|
+
"defaultValue": "true"
|
|
303
|
+
},
|
|
304
|
+
{
|
|
305
|
+
"name": "showBlocks",
|
|
306
|
+
"type": "boolean",
|
|
307
|
+
"description": "",
|
|
308
|
+
"defaultValue": "true"
|
|
309
|
+
},
|
|
310
|
+
{
|
|
311
|
+
"name": "showLists",
|
|
312
|
+
"type": "boolean",
|
|
313
|
+
"description": "",
|
|
314
|
+
"defaultValue": "true"
|
|
315
|
+
},
|
|
316
|
+
{
|
|
317
|
+
"name": "showAlignments",
|
|
318
|
+
"type": "boolean",
|
|
319
|
+
"description": "",
|
|
320
|
+
"defaultValue": "true"
|
|
321
|
+
},
|
|
322
|
+
{
|
|
323
|
+
"name": "showInsertions",
|
|
324
|
+
"type": "boolean",
|
|
325
|
+
"description": "",
|
|
326
|
+
"defaultValue": "true"
|
|
327
|
+
},
|
|
328
|
+
{
|
|
329
|
+
"name": "showHistory",
|
|
330
|
+
"type": "boolean",
|
|
331
|
+
"description": "",
|
|
332
|
+
"defaultValue": "true"
|
|
333
|
+
},
|
|
334
|
+
{
|
|
335
|
+
"name": "customUpload",
|
|
336
|
+
"type": "boolean",
|
|
337
|
+
"description": "",
|
|
338
|
+
"defaultValue": "false"
|
|
339
|
+
},
|
|
340
|
+
{
|
|
341
|
+
"name": "imageUploadEnabled",
|
|
342
|
+
"type": "boolean",
|
|
343
|
+
"description": "",
|
|
344
|
+
"defaultValue": "true"
|
|
345
|
+
},
|
|
346
|
+
{
|
|
347
|
+
"name": "value",
|
|
348
|
+
"type": "string | ShipEditorDocument | null",
|
|
349
|
+
"description": "",
|
|
350
|
+
"defaultValue": "''"
|
|
351
|
+
}
|
|
352
|
+
],
|
|
353
|
+
"outputs": [
|
|
354
|
+
{
|
|
355
|
+
"name": "imageUpload",
|
|
356
|
+
"type": "File",
|
|
357
|
+
"description": ""
|
|
358
|
+
}
|
|
359
|
+
],
|
|
360
|
+
"methods": [
|
|
361
|
+
{
|
|
362
|
+
"name": "execCommand",
|
|
363
|
+
"parameters": "commandId: string, showUI?: boolean, value?: string",
|
|
364
|
+
"returnType": "boolean;\n queryCommandState(commandId: string): boolean;\n queryCommandEnabled(commandId: string): boolean;\n}\n\n// Value Accessor Provider\nconst SHIP_EDITOR_VALUE_ACCESSOR: Provider =",
|
|
365
|
+
"description": ""
|
|
366
|
+
},
|
|
367
|
+
{
|
|
368
|
+
"name": "effect",
|
|
369
|
+
"parameters": "() => {\n this.showFormats();\n this.showBlocks();\n this.showLists();\n this.showAlignments();\n this.showInsertions();\n this.showHistory();\n this.toolbar();\n this.readonly();\n this.#initializeToolbarTabindexes();\n });\n }\n\n ngOnInit(",
|
|
370
|
+
"returnType": "any",
|
|
371
|
+
"description": ""
|
|
372
|
+
},
|
|
373
|
+
{
|
|
374
|
+
"name": "clearTimeout",
|
|
375
|
+
"parameters": "this.#typingTimeout);\n }\n\n if (saveImmediately",
|
|
376
|
+
"returnType": "any",
|
|
377
|
+
"description": ""
|
|
378
|
+
},
|
|
379
|
+
{
|
|
380
|
+
"name": "registerOnChange",
|
|
381
|
+
"parameters": "fn: (value: ShipEditorValue) => void",
|
|
382
|
+
"returnType": "void",
|
|
383
|
+
"description": ""
|
|
384
|
+
},
|
|
385
|
+
{
|
|
386
|
+
"name": "registerOnTouched",
|
|
387
|
+
"parameters": "fn: () => void",
|
|
388
|
+
"returnType": "void",
|
|
389
|
+
"description": ""
|
|
390
|
+
},
|
|
391
|
+
{
|
|
392
|
+
"name": "setDisabledState",
|
|
393
|
+
"parameters": "isDisabled: boolean",
|
|
394
|
+
"returnType": "void",
|
|
395
|
+
"description": ""
|
|
396
|
+
},
|
|
397
|
+
{
|
|
398
|
+
"name": "formatText",
|
|
399
|
+
"parameters": "command: string, value: string = ''",
|
|
400
|
+
"returnType": "any",
|
|
401
|
+
"description": ""
|
|
402
|
+
},
|
|
403
|
+
{
|
|
404
|
+
"name": "applyInlineStyle",
|
|
405
|
+
"parameters": "tag: string",
|
|
406
|
+
"returnType": "any",
|
|
407
|
+
"description": ""
|
|
408
|
+
},
|
|
409
|
+
{
|
|
410
|
+
"name": "while",
|
|
411
|
+
"parameters": "node",
|
|
412
|
+
"returnType": "any",
|
|
413
|
+
"description": ""
|
|
414
|
+
},
|
|
415
|
+
{
|
|
416
|
+
"name": "toggleLink",
|
|
417
|
+
"parameters": "url: string",
|
|
418
|
+
"returnType": "any",
|
|
419
|
+
"description": ""
|
|
420
|
+
},
|
|
421
|
+
{
|
|
422
|
+
"name": "setBlockType",
|
|
423
|
+
"parameters": "tag: string",
|
|
424
|
+
"returnType": "any",
|
|
425
|
+
"description": ""
|
|
426
|
+
},
|
|
427
|
+
{
|
|
428
|
+
"name": "toggleList",
|
|
429
|
+
"parameters": "listType: 'ul' | 'ol'",
|
|
430
|
+
"returnType": "any",
|
|
431
|
+
"description": ""
|
|
432
|
+
},
|
|
433
|
+
{
|
|
434
|
+
"name": "insertHorizontalRule",
|
|
435
|
+
"parameters": "",
|
|
436
|
+
"returnType": "any",
|
|
437
|
+
"description": ""
|
|
438
|
+
},
|
|
439
|
+
{
|
|
440
|
+
"name": "setAlign",
|
|
441
|
+
"parameters": "direction: 'left' | 'center' | 'right'",
|
|
442
|
+
"returnType": "any",
|
|
443
|
+
"description": ""
|
|
444
|
+
},
|
|
445
|
+
{
|
|
446
|
+
"name": "removeFormat",
|
|
447
|
+
"parameters": "",
|
|
448
|
+
"returnType": "any",
|
|
449
|
+
"description": ""
|
|
450
|
+
},
|
|
451
|
+
{
|
|
452
|
+
"name": "undo",
|
|
453
|
+
"parameters": "",
|
|
454
|
+
"returnType": "any",
|
|
455
|
+
"description": ""
|
|
456
|
+
},
|
|
457
|
+
{
|
|
458
|
+
"name": "openLinkModal",
|
|
459
|
+
"parameters": "",
|
|
460
|
+
"returnType": "any",
|
|
461
|
+
"description": ""
|
|
462
|
+
},
|
|
463
|
+
{
|
|
464
|
+
"name": "openImageModal",
|
|
465
|
+
"parameters": "",
|
|
466
|
+
"returnType": "any",
|
|
467
|
+
"description": ""
|
|
468
|
+
},
|
|
469
|
+
{
|
|
470
|
+
"name": "onComponentFocusIn",
|
|
471
|
+
"parameters": "event: FocusEvent",
|
|
472
|
+
"returnType": "any",
|
|
473
|
+
"description": ""
|
|
474
|
+
},
|
|
475
|
+
{
|
|
476
|
+
"name": "onComponentClick",
|
|
477
|
+
"parameters": "event: MouseEvent",
|
|
478
|
+
"returnType": "any",
|
|
479
|
+
"description": ""
|
|
480
|
+
},
|
|
481
|
+
{
|
|
482
|
+
"name": "onKeyDown",
|
|
483
|
+
"parameters": "event: KeyboardEvent",
|
|
484
|
+
"returnType": "any",
|
|
485
|
+
"description": ""
|
|
486
|
+
},
|
|
487
|
+
{
|
|
488
|
+
"name": "onKeyUp",
|
|
489
|
+
"parameters": "event: KeyboardEvent",
|
|
490
|
+
"returnType": "any",
|
|
491
|
+
"description": ""
|
|
492
|
+
},
|
|
493
|
+
{
|
|
494
|
+
"name": "executeCommand",
|
|
495
|
+
"parameters": "cmd: ShipEditorCommand",
|
|
496
|
+
"returnType": "any",
|
|
497
|
+
"description": ""
|
|
498
|
+
},
|
|
499
|
+
{
|
|
500
|
+
"name": "getBlockLabel",
|
|
501
|
+
"parameters": "",
|
|
502
|
+
"returnType": "string",
|
|
503
|
+
"description": ""
|
|
504
|
+
},
|
|
505
|
+
{
|
|
506
|
+
"name": "onComponentScroll",
|
|
507
|
+
"parameters": "event: Event",
|
|
508
|
+
"returnType": "any",
|
|
509
|
+
"description": ""
|
|
510
|
+
},
|
|
511
|
+
{
|
|
512
|
+
"name": "onWindowResize",
|
|
513
|
+
"parameters": "",
|
|
514
|
+
"returnType": "any",
|
|
515
|
+
"description": ""
|
|
516
|
+
},
|
|
517
|
+
{
|
|
518
|
+
"name": "updateImgToolbarPosition",
|
|
519
|
+
"parameters": "",
|
|
520
|
+
"returnType": "any",
|
|
521
|
+
"description": ""
|
|
522
|
+
},
|
|
523
|
+
{
|
|
524
|
+
"name": "onFileSelected",
|
|
525
|
+
"parameters": "event: Event",
|
|
526
|
+
"returnType": "any",
|
|
527
|
+
"description": ""
|
|
528
|
+
},
|
|
529
|
+
{
|
|
530
|
+
"name": "onDrop",
|
|
531
|
+
"parameters": "event: DragEvent",
|
|
532
|
+
"returnType": "any",
|
|
533
|
+
"description": ""
|
|
534
|
+
},
|
|
535
|
+
{
|
|
536
|
+
"name": "onPaste",
|
|
537
|
+
"parameters": "event: ClipboardEvent",
|
|
538
|
+
"returnType": "any",
|
|
539
|
+
"description": ""
|
|
540
|
+
},
|
|
541
|
+
{
|
|
542
|
+
"name": "insertImage",
|
|
543
|
+
"parameters": "url: string",
|
|
544
|
+
"returnType": "any",
|
|
545
|
+
"description": ""
|
|
546
|
+
},
|
|
547
|
+
{
|
|
548
|
+
"name": "getHTML",
|
|
549
|
+
"parameters": "",
|
|
550
|
+
"returnType": "string",
|
|
551
|
+
"description": ""
|
|
552
|
+
},
|
|
553
|
+
{
|
|
554
|
+
"name": "getMarkdown",
|
|
555
|
+
"parameters": "",
|
|
556
|
+
"returnType": "string",
|
|
557
|
+
"description": ""
|
|
558
|
+
},
|
|
559
|
+
{
|
|
560
|
+
"name": "getJSON",
|
|
561
|
+
"parameters": "",
|
|
562
|
+
"returnType": "ShipEditorDocument",
|
|
563
|
+
"description": ""
|
|
564
|
+
},
|
|
565
|
+
{
|
|
566
|
+
"name": "setHTML",
|
|
567
|
+
"parameters": "html: string",
|
|
568
|
+
"returnType": "any",
|
|
569
|
+
"description": ""
|
|
570
|
+
},
|
|
571
|
+
{
|
|
572
|
+
"name": "setMarkdown",
|
|
573
|
+
"parameters": "md: string",
|
|
574
|
+
"returnType": "any",
|
|
575
|
+
"description": ""
|
|
576
|
+
},
|
|
577
|
+
{
|
|
578
|
+
"name": "setJSON",
|
|
579
|
+
"parameters": "json: ShipEditorDocument",
|
|
580
|
+
"returnType": "any",
|
|
581
|
+
"description": ""
|
|
582
|
+
},
|
|
583
|
+
{
|
|
584
|
+
"name": "clear",
|
|
585
|
+
"parameters": "",
|
|
586
|
+
"returnType": "any",
|
|
587
|
+
"description": ""
|
|
588
|
+
},
|
|
589
|
+
{
|
|
590
|
+
"name": "traverse",
|
|
591
|
+
"parameters": "child);\n }\n\n return md;\n }\n\n // Markdown => HTML Converter\n #markdownToHTML(markdown: string",
|
|
592
|
+
"returnType": "string",
|
|
593
|
+
"description": ""
|
|
594
|
+
}
|
|
595
|
+
],
|
|
596
|
+
"cssVariables": [
|
|
597
|
+
{
|
|
598
|
+
"name": "--editor-bg",
|
|
599
|
+
"defaultValue": "var(--base-1)"
|
|
600
|
+
},
|
|
601
|
+
{
|
|
602
|
+
"name": "--editor-code-bg",
|
|
603
|
+
"defaultValue": "var(--base-2)"
|
|
604
|
+
},
|
|
605
|
+
{
|
|
606
|
+
"name": "--editor-border-color",
|
|
607
|
+
"defaultValue": "var(--base-4)"
|
|
608
|
+
},
|
|
609
|
+
{
|
|
610
|
+
"name": "--editor-toolbar-bg",
|
|
611
|
+
"defaultValue": "rgb(from var(--base-1) r g b / 70%)"
|
|
612
|
+
},
|
|
613
|
+
{
|
|
614
|
+
"name": "--editor-toolbar-border",
|
|
615
|
+
"defaultValue": "var(--base-4)"
|
|
616
|
+
},
|
|
617
|
+
{
|
|
618
|
+
"name": "--editor-btn-hover",
|
|
619
|
+
"defaultValue": "var(--base-3)"
|
|
620
|
+
},
|
|
621
|
+
{
|
|
622
|
+
"name": "--editor-btn-active-bg",
|
|
623
|
+
"defaultValue": "var(--primary-3)"
|
|
624
|
+
},
|
|
625
|
+
{
|
|
626
|
+
"name": "--editor-btn-active-c",
|
|
627
|
+
"defaultValue": "var(--primary-11)"
|
|
628
|
+
},
|
|
629
|
+
{
|
|
630
|
+
"name": "--editor-footer-bg",
|
|
631
|
+
"defaultValue": "var(--base-2)"
|
|
632
|
+
},
|
|
633
|
+
{
|
|
634
|
+
"name": "--editor-p",
|
|
635
|
+
"defaultValue": "#{p2r(16)} #{p2r(20)}"
|
|
636
|
+
},
|
|
637
|
+
{
|
|
638
|
+
"name": "--editor-min-h",
|
|
639
|
+
"defaultValue": "#{p2r(220)}"
|
|
640
|
+
},
|
|
641
|
+
{
|
|
642
|
+
"name": "--editor-border-focus",
|
|
643
|
+
"defaultValue": "var(--primary-8)"
|
|
644
|
+
},
|
|
645
|
+
{
|
|
646
|
+
"name": "--editor-shadow-focus",
|
|
647
|
+
"defaultValue": "0 0 0 3px var(--primary-3)"
|
|
648
|
+
}
|
|
649
|
+
],
|
|
650
|
+
"examples": [
|
|
651
|
+
{
|
|
652
|
+
"name": "sandbox-editor",
|
|
653
|
+
"html": "<div class=\"sandbox-container\">\n <div class=\"sandbox-sidebar\">\n <sh-card class=\"type-b control-card\">\n <h3>Options</h3>\n\n <div class=\"control-group\">\n <label class=\"control-label\">Storage Format</label>\n <sh-button-group size=\"small\" [value]=\"selectedFormat()\" (valueChange)=\"onFormatChange($event)\">\n <button value=\"html\">HTML</button>\n <button value=\"markdown\">Markdown</button>\n <button value=\"json\">JSON</button>\n </sh-button-group>\n </div>\n\n <div class=\"control-group inline\">\n <label for=\"readonly-toggle\" class=\"control-label\">Read-Only</label>\n <sh-toggle id=\"readonly-toggle\" [(checked)]=\"isReadonly\"></sh-toggle>\n </div>\n\n <div class=\"control-group inline\">\n <label for=\"toolbar-toggle\" class=\"control-label\">Show Toolbar</label>\n <sh-toggle id=\"toolbar-toggle\" [(checked)]=\"showToolbar\"></sh-toggle>\n </div>\n\n <div class=\"control-group inline\">\n <label for=\"image-upload-toggle\" class=\"control-label\">Image Upload</label>\n <sh-toggle id=\"image-upload-toggle\" [(checked)]=\"imageUploadEnabled\"></sh-toggle>\n </div>\n\n @if (showToolbar()) {\n <div class=\"control-group-section-title\">Toolbar Groups</div>\n\n <div class=\"control-group inline\">\n <label for=\"formats-toggle\" class=\"control-label\">Formats</label>\n <sh-toggle id=\"formats-toggle\" [(checked)]=\"showFormats\"></sh-toggle>\n </div>\n\n <div class=\"control-group inline\">\n <label for=\"blocks-toggle\" class=\"control-label\">Blocks/Headings</label>\n <sh-toggle id=\"blocks-toggle\" [(checked)]=\"showBlocks\"></sh-toggle>\n </div>\n\n <div class=\"control-group inline\">\n <label for=\"lists-toggle\" class=\"control-label\">Lists</label>\n <sh-toggle id=\"lists-toggle\" [(checked)]=\"showLists\"></sh-toggle>\n </div>\n\n <div class=\"control-group inline\">\n <label for=\"align-toggle\" class=\"control-label\">Alignments</label>\n <sh-toggle id=\"align-toggle\" [(checked)]=\"showAlignments\"></sh-toggle>\n </div>\n\n <div class=\"control-group inline\">\n <label for=\"insert-toggle\" class=\"control-label\">Insertions</label>\n <sh-toggle id=\"insert-toggle\" [(checked)]=\"showInsertions\"></sh-toggle>\n </div>\n\n <div class=\"control-group inline\">\n <label for=\"history-toggle\" class=\"control-label\">History/Code</label>\n <sh-toggle id=\"history-toggle\" [(checked)]=\"showHistory\"></sh-toggle>\n </div>\n }\n </sh-card>\n </div>\n\n <div class=\"sandbox-editor-wrapper\">\n <sh-editor\n [(value)]=\"editorValue\"\n [format]=\"selectedFormat()\"\n [readonly]=\"isReadonly()\"\n [toolbar]=\"showToolbar()\"\n [showFormats]=\"showFormats()\"\n [showBlocks]=\"showBlocks()\"\n [showLists]=\"showLists()\"\n [showAlignments]=\"showAlignments()\"\n [showInsertions]=\"showInsertions()\"\n [showHistory]=\"showHistory()\"\n [customCommands]=\"customEditorCommands()\"\n [imageUploadEnabled]=\"imageUploadEnabled()\"\n placeholder=\"Start writing your premium content here...\">\n <!-- Projected custom toolbar action -->\n <button type=\"button\" class=\"sh-editor-btn\" (click)=\"customAction()\" shTooltip=\"Projected Action (Star)\">\n <sh-icon>star</sh-icon>\n </button>\n </sh-editor>\n\n <sh-card class=\"type-b output-card\">\n <div class=\"output-header\">\n <h3>Database Output</h3>\n <button shButton variant=\"outlined\" size=\"small\" (click)=\"copyToClipboard()\">\n <sh-icon>copy</sh-icon>\n Copy\n </button>\n </div>\n <p class=\"output-desc\">This is exactly what would be saved in your database:</p>\n <pre class=\"serialized-preview\"><code>{{ serializedOutput() }}</code></pre>\n </sh-card>\n </div>\n</div>\n",
|
|
654
|
+
"ts": "import { ChangeDetectionStrategy, Component, computed, signal, viewChild } from '@angular/core';\nimport { ShipColor } from '@ship-ui/core';\nimport { ShipButton } from '@ship-ui/core/ship-button';\nimport { ShipButtonGroup } from '@ship-ui/core/ship-button-group';\nimport { ShipCard } from '@ship-ui/core/ship-card';\nimport { ShipEditor, ShipEditorCommand, ShipEditorValue } from '@ship-ui/core/ship-editor';\nimport { ShipIcon } from '@ship-ui/core/ship-icon';\nimport { ShipToggle } from '@ship-ui/core/ship-toggle';\n\n@Component({\n selector: 'app-sandbox-editor',\n standalone: true,\n imports: [ShipEditor, ShipButton, ShipCard, ShipToggle, ShipIcon, ShipButtonGroup],\n templateUrl: './sandbox-editor.html',\n styleUrl: './sandbox-editor.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class SandboxEditor {\n // Current format selection\n selectedFormat = signal<'html' | 'markdown' | 'json'>('html');\n\n editor = viewChild(ShipEditor);\n\n // Editor states\n isReadonly = signal<boolean>(false);\n showToolbar = signal<boolean>(true);\n selectedColor = signal<ShipColor | null>(null);\n imageUploadEnabled = signal<boolean>(true);\n\n // Toolbar group configurations\n showFormats = signal<boolean>(true);\n showBlocks = signal<boolean>(true);\n showLists = signal<boolean>(true);\n showAlignments = signal<boolean>(true);\n showInsertions = signal<boolean>(true);\n showHistory = signal<boolean>(true);\n\n // Custom action for projected button\n customAction() {\n alert('Custom Projected Button Clicked!');\n }\n\n customEditorCommands = signal<ShipEditorCommand[]>([\n {\n id: 'highlight',\n label: 'Highlight Text',\n icon: 'star',\n description: 'Highlight selected text in yellow',\n action: (editor) => {\n editor.formatText('backColor', '#fff2b2');\n },\n },\n {\n id: 'info-callout',\n label: 'Info Callout',\n icon: 'terminal',\n description: 'Insert an info callout box',\n action: (editor) => {\n editor.formatText(\n 'insertHTML',\n '<div style=\"background-color: var(--primary-2); border-left: 4px solid var(--primary-9); padding: 12px 16px; margin: 12px 0; border-radius: 4px; color: var(--base-12);\"><strong>Info:</strong> Start typing callout contents here...</div>'\n );\n },\n },\n ]);\n\n // Pre-populated initial contents for each format\n initialHtml = `<h1>Ship WYSIWYG Editor</h1><p>Welcome! This is a <strong>config-driven</strong> rich-text editor designed to support flexible storage formats.</p><ul><li><strong>Two-way binding</strong> with <code>ControlValueAccessor</code></li><li>Instant conversion to <strong>HTML</strong>, <strong>Markdown</strong>, or <strong>JSON</strong></li><li>Sticky blur-toolbar, light/dark mode support, and word counting</li></ul><blockquote>\"A beautiful interface makes editing content a delight.\"</blockquote><hr><p>Try changing the storage format below to see the serialized output update in real time!</p>`;\n\n // Current value model\n editorValue = signal<ShipEditorValue>(this.initialHtml);\n\n // Stringified preview for database presentation\n serializedOutput = computed(() => {\n const val = this.editorValue();\n if (val === null || val === undefined) return '';\n if (typeof val === 'string') return val;\n return JSON.stringify(val, null, 2);\n });\n\n // Handle format swapping, converting the value appropriately\n onFormatChange(format: string | null) {\n if (format === 'html' || format === 'markdown' || format === 'json') {\n this.selectedFormat.set(format);\n }\n }\n\n // Copy serialized contents to clipboard\n copyToClipboard() {\n navigator.clipboard.writeText(this.serializedOutput());\n }\n}\n"
|
|
655
|
+
}
|
|
656
|
+
]
|
|
657
|
+
},
|
|
234
658
|
{
|
|
235
659
|
"name": "ShipBlueprint",
|
|
236
660
|
"selector": "sh-blueprint",
|
|
@@ -1467,6 +1891,12 @@
|
|
|
1467
1891
|
"description": "",
|
|
1468
1892
|
"defaultValue": "'start'"
|
|
1469
1893
|
},
|
|
1894
|
+
{
|
|
1895
|
+
"name": "grid",
|
|
1896
|
+
"type": "boolean",
|
|
1897
|
+
"description": "",
|
|
1898
|
+
"defaultValue": "false"
|
|
1899
|
+
},
|
|
1470
1900
|
{
|
|
1471
1901
|
"name": "loading",
|
|
1472
1902
|
"type": "boolean",
|
|
@@ -1524,6 +1954,18 @@
|
|
|
1524
1954
|
}
|
|
1525
1955
|
],
|
|
1526
1956
|
"methods": [
|
|
1957
|
+
{
|
|
1958
|
+
"name": "effect",
|
|
1959
|
+
"parameters": "() => {\n if (this.#sort()) return;\n\n const hostEl = this.#el.nativeElement;\n if (this.resizable()",
|
|
1960
|
+
"returnType": "any",
|
|
1961
|
+
"description": ""
|
|
1962
|
+
},
|
|
1963
|
+
{
|
|
1964
|
+
"name": "onKeyDown",
|
|
1965
|
+
"parameters": "event: KeyboardEvent",
|
|
1966
|
+
"returnType": "any",
|
|
1967
|
+
"description": ""
|
|
1968
|
+
},
|
|
1527
1969
|
{
|
|
1528
1970
|
"name": "onMouseMove",
|
|
1529
1971
|
"parameters": "event: MouseEvent",
|
|
@@ -1532,8 +1974,20 @@
|
|
|
1532
1974
|
},
|
|
1533
1975
|
{
|
|
1534
1976
|
"name": "cancelAnimationFrame",
|
|
1535
|
-
"parameters": "this.#animationFrameRequest);\n }\n }\n}\n\n@Directive({\n selector: '[shSort]',\n standalone: true,\n host: {\n role: 'columnheader',\n '[class.sortable]': '!!shSort()',\n '[attr.tabindex]': '
|
|
1536
|
-
"returnType": "null',\n '
|
|
1977
|
+
"parameters": "this.#animationFrameRequest);\n }\n }\n}\n\n@Directive({\n selector: '[shSort]',\n standalone: true,\n host: {\n role: 'columnheader',\n '[class.sortable]': '!!shSort()',\n '[attr.tabindex]': 'tabIndex()',\n '(click)': 'shSort() ? toggleSort($event",
|
|
1978
|
+
"returnType": "null',\n '[attr.aria-sort]': 'shSort() ? ariaSort() : null',\n '[class.sort-asc]': 'sortAsc()',\n '[class.sort-desc]': 'sortDesc()',\n '[attr.aria-keyshortcuts]': 'ariaKeyshortcuts()',\n },\n})\nexport class ShipSort",
|
|
1979
|
+
"description": ""
|
|
1980
|
+
},
|
|
1981
|
+
{
|
|
1982
|
+
"name": "onCleanup",
|
|
1983
|
+
"parameters": "() => observer.disconnect());\n }\n });\n\n bodyEffect = effect(() => {\n const body = this.tbody()?.nativeElement;\n const head = this.thead()?.nativeElement;\n\n if (!body || !head) return;\n\n const stickyHeaderHeight = this.stickyHeaderHeight();\n\n queueMicrotask(() => {\n const hasStickyRowHeaderStartElement = head.querySelectorAll('tr.sticky').length > 0;\n const stickyBodyRows = body.querySelectorAll('tr.sticky');\n const hasStickyRowStartElement = stickyBodyRows.length > 0;\n\n if (hasStickyRowStartElement && hasStickyRowHeaderStartElement",
|
|
1984
|
+
"returnType": "any",
|
|
1985
|
+
"description": ""
|
|
1986
|
+
},
|
|
1987
|
+
{
|
|
1988
|
+
"name": "untracked",
|
|
1989
|
+
"parameters": "() => {\n const host = this.#el.nativeElement;\n const allCells = host.querySelectorAll('th, td');\n\n if (gridActive",
|
|
1990
|
+
"returnType": "any",
|
|
1537
1991
|
"description": ""
|
|
1538
1992
|
},
|
|
1539
1993
|
{
|
|
@@ -1554,6 +2008,12 @@
|
|
|
1554
2008
|
"returnType": "any",
|
|
1555
2009
|
"description": ""
|
|
1556
2010
|
},
|
|
2011
|
+
{
|
|
2012
|
+
"name": "onGridKeyDown",
|
|
2013
|
+
"parameters": "event: KeyboardEvent",
|
|
2014
|
+
"returnType": "any",
|
|
2015
|
+
"description": ""
|
|
2016
|
+
},
|
|
1557
2017
|
{
|
|
1558
2018
|
"name": "toggleSort",
|
|
1559
2019
|
"parameters": "column: string",
|
|
@@ -1565,6 +2025,12 @@
|
|
|
1565
2025
|
"parameters": "row: any, col: ShipTableColumn",
|
|
1566
2026
|
"returnType": "any",
|
|
1567
2027
|
"description": ""
|
|
2028
|
+
},
|
|
2029
|
+
{
|
|
2030
|
+
"name": "formatDate",
|
|
2031
|
+
"parameters": "value: any",
|
|
2032
|
+
"returnType": "string",
|
|
2033
|
+
"description": ""
|
|
1568
2034
|
}
|
|
1569
2035
|
],
|
|
1570
2036
|
"cssVariables": [
|
|
@@ -1681,8 +2147,8 @@
|
|
|
1681
2147
|
},
|
|
1682
2148
|
{
|
|
1683
2149
|
"name": "config-table",
|
|
1684
|
-
"html": "<sh-table\n [loading]=\"isLoading()\"\n [data]=\"users()\"\n (dataChange)=\"users.set($event)\"\n [variant]=\"variant()\"\n aria-label=\"User Management Configuration Table\">\n <button shButton actionbar class=\"small primary\" (click)=\"toggleLoading()\">\n <sh-icon>spinner</sh-icon>\n Toggle Loading\n </button>\n <button shButton actionbar class=\"small flat\" (click)=\"resetData()\">\n <sh-icon>arrow-counter-clockwise</sh-icon>\n Reset Data\n </button>\n\n <sh-table-content [columns]=\"columns()\" [data]=\"users()\" />\n\n <!-- Custom User Column Template -->\n <ng-template #nameTemplate let-row>\n <div class=\"user-cell\">\n <span class=\"user-name\">{{ row.name }}</span>\n <span class=\"user-email\">{{ row.email }}</span>\n </div>\n </ng-template>\n\n <!-- Custom Actions Column Template -->\n <ng-template #actionsTemplate let-row>\n <button shButton class=\"small text primary\" (click)=\"editUser(row)\" title=\"Edit user\">\n <sh-icon>pencil-simple</sh-icon>\n </button>\n <button shButton class=\"small text error\" (click)=\"deleteUser(row.id)\" title=\"Delete user\">\n <sh-icon>trash</sh-icon>\n </button>\n </ng-template>\n</sh-table>\n",
|
|
1685
|
-
"ts": "import { CommonModule } from '@angular/common';\nimport { ChangeDetectionStrategy, Component, computed, input, signal, TemplateRef, viewChild } from '@angular/core';\nimport { ShipTableVariant } from '@ship-ui/core';\nimport { ShipButton } from '@ship-ui/core/ship-button';\nimport { ShipIcon } from '@ship-ui/core/ship-icon';\nimport { ShipTable, ShipTableColumn, ShipTableContent } from '@ship-ui/core/ship-table';\n\nexport interface ConfigUserElement {\n id: number;\n name: string;\n email: string;\n role: string;\n active: boolean;\n joined: string;\n bio: string;\n}\n\nconst INITIAL_USERS: ConfigUserElement[] = [\n {\n id: 1,\n name: 'Alice Vance',\n email: 'alice@shipui.com',\n role: 'Administrator',\n active: true,\n joined: '2025-01-15T09:30:00',\n bio: 'Lead platform engineer and architect of the design system components.',\n },\n {\n id: 2,\n name: 'Bob Smith',\n email: 'bob@shipui.com',\n role: 'Developer',\n active: true,\n joined: '2025-03-22T17:15:00',\n bio: 'Frontend engineer specializing in accessible interactive charts and graphs.',\n },\n {\n id: 3,\n name: 'Charlie Brown',\n email: 'charlie@shipui.com',\n role: 'Support',\n active: false,\n joined: '2025-06-05T11:00:00',\n bio: 'Customer success advocate passionate about documentation and developer experience.',\n },\n {\n id: 4,\n name: 'Diana Prince',\n email: 'diana@shipui.com',\n role: 'Product Manager',\n active: true,\n joined: '2024-10-12T08:45:00',\n bio: 'Product strategist steering the integration of AI-assisted coding tools.',\n },\n {\n id: 5,\n name: 'Evan Wright',\n email: 'evan@shipui.com',\n role: 'Designer',\n active: false,\n joined: '2024-12-01T14:20:00',\n bio: 'UI/UX specialist designer who crafted the glassmorphism aesthetic guidelines.',\n },\n];\n\n@Component({\n selector: 'config-table',\n standalone: true,\n imports: [CommonModule, ShipTable, ShipTableContent, ShipButton, ShipIcon],\n templateUrl: './config-table.html',\n styleUrl: './config-table.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class ConfigTable {\n variant = input<ShipTableVariant | null>(null);\n\n // Sample data\n users = signal<ConfigUserElement[]>([...INITIAL_USERS]);\n isLoading = signal<boolean>(false);\n\n // Template viewChild references\n nameTemplate = viewChild.required<TemplateRef<any>>('nameTemplate');\n actionsTemplate = viewChild.required<TemplateRef<any>>('actionsTemplate');\n\n // Reactively build columns once templates are loaded\n columns = computed<ShipTableColumn<ConfigUserElement>[]>(() => [\n {\n id: 'id',\n header: 'ID',\n type: 'number',\n sortable: true,\n size: '60px',\n sticky: 'start',\n },\n {\n id: 'name',\n header: 'User',\n type: 'string',\n sortable: true,\n size: '220px',\n sticky: 'start',\n cellTemplate: this.nameTemplate(),\n rowHeader: true,\n },\n {\n id: 'role',\n header: 'Role',\n type: 'badge',\n sortable: true,\n size: '150px',\n },\n {\n id: 'active',\n header: 'Active Status',\n type: 'boolean',\n sortable: true,\n size: '120px',\n },\n {\n id: 'joined',\n header: 'Joined Date',\n type: 'date',\n sortable: true,\n size: '140px',\n },\n {\n id: 'bio',\n header: 'Bio',\n type: 'string',\n resizable: true,\n size: '1fr',\n },\n {\n id: 'actions',\n header: 'Actions',\n sticky: 'end',\n cellTemplate: this.actionsTemplate(),\n },\n ]);\n\n toggleLoading() {\n this.isLoading.set(true);\n setTimeout(() => {\n this.isLoading.set(false);\n }, 1200);\n }\n\n deleteUser(id: number) {\n this.users.update((current) => current.filter((u) => u.id !== id));\n }\n\n editUser(user: ConfigUserElement) {\n alert(`Editing user: ${user.name}`);\n }\n\n resetData() {\n this.users.set([...INITIAL_USERS]);\n }\n}\n"
|
|
2150
|
+
"html": "<sh-table\n [loading]=\"isLoading()\"\n [data]=\"users()\"\n (dataChange)=\"users.set($event)\"\n [variant]=\"variant()\"\n [grid]=\"gridMode()\"\n aria-label=\"User Management Configuration Table\">\n <button shButton actionbar class=\"small primary\" (click)=\"toggleLoading()\">\n <sh-icon>spinner</sh-icon>\n Toggle Loading\n </button>\n <button shButton actionbar class=\"small outline\" [class.primary]=\"gridMode()\" (click)=\"gridMode.set(!gridMode())\">\n <sh-icon>{{ gridMode() ? 'check-square' : 'square' }}</sh-icon>\n Interactive Grid: {{ gridMode() ? 'ON' : 'OFF' }}\n </button>\n <button shButton actionbar class=\"small flat\" (click)=\"resetData()\">\n <sh-icon>arrow-counter-clockwise</sh-icon>\n Reset Data\n </button>\n\n <sh-table-content [columns]=\"columns()\" [data]=\"users()\" />\n\n <!-- Custom User Column Template -->\n <ng-template #nameTemplate let-row>\n <div class=\"user-cell\">\n <span class=\"user-name\">{{ row.name }}</span>\n <span class=\"user-email\">{{ row.email }}</span>\n </div>\n </ng-template>\n\n <!-- Custom Actions Column Template -->\n <ng-template #actionsTemplate let-row>\n <button shButton class=\"small text primary\" (click)=\"editUser(row)\" title=\"Edit user\">\n <sh-icon>pencil-simple</sh-icon>\n </button>\n <button shButton class=\"small text error\" (click)=\"deleteUser(row.id)\" title=\"Delete user\">\n <sh-icon>trash</sh-icon>\n </button>\n </ng-template>\n</sh-table>\n",
|
|
2151
|
+
"ts": "import { CommonModule } from '@angular/common';\nimport { ChangeDetectionStrategy, Component, computed, input, signal, TemplateRef, viewChild } from '@angular/core';\nimport { ShipTableVariant } from '@ship-ui/core';\nimport { ShipButton } from '@ship-ui/core/ship-button';\nimport { ShipIcon } from '@ship-ui/core/ship-icon';\nimport { ShipTable, ShipTableColumn, ShipTableContent } from '@ship-ui/core/ship-table';\n\nexport interface ConfigUserElement {\n id: number;\n name: string;\n email: string;\n role: string;\n active: boolean;\n joined: string;\n bio: string;\n}\n\nconst INITIAL_USERS: ConfigUserElement[] = [\n {\n id: 1,\n name: 'Alice Vance',\n email: 'alice@shipui.com',\n role: 'Administrator',\n active: true,\n joined: '2025-01-15T09:30:00',\n bio: 'Lead platform engineer and architect of the design system components.',\n },\n {\n id: 2,\n name: 'Bob Smith',\n email: 'bob@shipui.com',\n role: 'Developer',\n active: true,\n joined: '2025-03-22T17:15:00',\n bio: 'Frontend engineer specializing in accessible interactive charts and graphs.',\n },\n {\n id: 3,\n name: 'Charlie Brown',\n email: 'charlie@shipui.com',\n role: 'Support',\n active: false,\n joined: '2025-06-05T11:00:00',\n bio: 'Customer success advocate passionate about documentation and developer experience.',\n },\n {\n id: 4,\n name: 'Diana Prince',\n email: 'diana@shipui.com',\n role: 'Product Manager',\n active: true,\n joined: '2024-10-12T08:45:00',\n bio: 'Product strategist steering the integration of AI-assisted coding tools.',\n },\n {\n id: 5,\n name: 'Evan Wright',\n email: 'evan@shipui.com',\n role: 'Designer',\n active: false,\n joined: '2024-12-01T14:20:00',\n bio: 'UI/UX specialist designer who crafted the glassmorphism aesthetic guidelines.',\n },\n];\n\n@Component({\n selector: 'config-table',\n standalone: true,\n imports: [CommonModule, ShipTable, ShipTableContent, ShipButton, ShipIcon],\n templateUrl: './config-table.html',\n styleUrl: './config-table.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class ConfigTable {\n variant = input<ShipTableVariant | null>(null);\n\n // Sample data\n users = signal<ConfigUserElement[]>([...INITIAL_USERS]);\n isLoading = signal<boolean>(false);\n gridMode = signal<boolean>(false);\n\n // Template viewChild references\n nameTemplate = viewChild.required<TemplateRef<any>>('nameTemplate');\n actionsTemplate = viewChild.required<TemplateRef<any>>('actionsTemplate');\n\n // Reactively build columns once templates are loaded\n columns = computed<ShipTableColumn<ConfigUserElement>[]>(() => [\n {\n id: 'id',\n header: 'ID',\n type: 'number',\n sortable: true,\n size: '60px',\n sticky: 'start',\n },\n {\n id: 'name',\n header: 'User',\n type: 'string',\n sortable: true,\n size: '220px',\n sticky: 'start',\n cellTemplate: this.nameTemplate(),\n rowHeader: true,\n },\n {\n id: 'role',\n header: 'Role',\n type: 'badge',\n sortable: true,\n size: '150px',\n },\n {\n id: 'active',\n header: 'Active Status',\n type: 'boolean',\n sortable: true,\n size: '120px',\n },\n {\n id: 'joined',\n header: 'Joined Date',\n type: 'date',\n sortable: true,\n size: '140px',\n },\n {\n id: 'bio',\n header: 'Bio',\n type: 'string',\n resizable: true,\n size: '1fr',\n },\n {\n id: 'actions',\n header: 'Actions',\n sticky: 'end',\n cellTemplate: this.actionsTemplate(),\n },\n ]);\n\n toggleLoading() {\n this.isLoading.set(true);\n setTimeout(() => {\n this.isLoading.set(false);\n }, 1200);\n }\n\n deleteUser(id: number) {\n this.users.update((current) => current.filter((u) => u.id !== id));\n }\n\n editUser(user: ConfigUserElement) {\n alert(`Editing user: ${user.name}`);\n }\n\n resetData() {\n this.users.set([...INITIAL_USERS]);\n }\n}\n"
|
|
1686
2152
|
},
|
|
1687
2153
|
{
|
|
1688
2154
|
"name": "multi-table-header",
|
|
@@ -2772,6 +3238,12 @@
|
|
|
2772
3238
|
"parameters": "",
|
|
2773
3239
|
"returnType": "any",
|
|
2774
3240
|
"description": ""
|
|
3241
|
+
},
|
|
3242
|
+
{
|
|
3243
|
+
"name": "handleKeyDown",
|
|
3244
|
+
"parameters": "event: KeyboardEvent",
|
|
3245
|
+
"returnType": "any",
|
|
3246
|
+
"description": ""
|
|
2775
3247
|
}
|
|
2776
3248
|
],
|
|
2777
3249
|
"cssVariables": [],
|
|
@@ -4386,6 +4858,135 @@
|
|
|
4386
4858
|
}
|
|
4387
4859
|
]
|
|
4388
4860
|
},
|
|
4861
|
+
{
|
|
4862
|
+
"name": "ShipA11yKeybindingsDirective",
|
|
4863
|
+
"selector": "[shA11yKeybinding]",
|
|
4864
|
+
"path": "core/projects/ship-ui/ship-a11y-keybindings/ship-a11y-keybindings.directive.ts",
|
|
4865
|
+
"description": "### Overview & Accessibility\n\nThe A11y Keybindings engine provides a unified system for handling keyboard shortcuts. The\n`[shA11yKeybinding]` directive dynamically sets standard `aria-keyshortcuts` attributes\non host elements to announce active hotkeys to assistive technologies.\n\n### Provider Overrides\n\nDefault shortcuts registered by components can be overridden globally at the application level.\nProvide the `SHIP_A11Y_KEYBINDINGS_OVERRIDE` injection token with a mapping object:\n`{ provide: SHIP_A11Y_KEYBINDINGS_OVERRIDE, useValue: { 'action.name': 'ctrl+s' } }`.\n\n### OS Adaptive Keys (Command vs Control)\n\nWe support the `ctrlOrCmd` modifier keyword in shortcuts. It maps dynamically at runtime:\nresolves to `Command (⌘)` on macOS and `Control (Ctrl)` on Windows/Linux. Display formats are also customized for each OS (e.g. `⌘⇧K` vs `Ctrl+Shift+K`).\n\n### Collisions & Input Handling\n\nWhen in `mode=\"global\"`, the directive automatically ignores keypresses that originate\nfrom input boxes, selects, textareas, or content-editables (unless modifier keys like Ctrl, Cmd, or Alt are pressed),\nensuring keyboard shortcuts do not interfere with typing.",
|
|
4866
|
+
"keywords": [
|
|
4867
|
+
"accessibility",
|
|
4868
|
+
"keybindings",
|
|
4869
|
+
"hotkeys",
|
|
4870
|
+
"shortcuts",
|
|
4871
|
+
"keyboard",
|
|
4872
|
+
"global",
|
|
4873
|
+
"local",
|
|
4874
|
+
"cmd",
|
|
4875
|
+
"ctrl",
|
|
4876
|
+
"modifiers",
|
|
4877
|
+
"focus"
|
|
4878
|
+
],
|
|
4879
|
+
"inputs": [
|
|
4880
|
+
{
|
|
4881
|
+
"name": "mode",
|
|
4882
|
+
"type": "'global' | 'local'",
|
|
4883
|
+
"description": "The registered action name (e.g. 'table.next-page', 'dialog.close'). / shA11yKeybinding = input.required<string>(); /** Defines whether the keybinding listener is 'local' (host element keydown) or 'global' (window keydown). Default is 'local'.",
|
|
4884
|
+
"defaultValue": "'local'"
|
|
4885
|
+
},
|
|
4886
|
+
{
|
|
4887
|
+
"name": "preventDefault",
|
|
4888
|
+
"type": "boolean",
|
|
4889
|
+
"description": "The registered action name (e.g. 'table.next-page', 'dialog.close'). / shA11yKeybinding = input.required<string>(); /** Defines whether the keybinding listener is 'local' (host element keydown) or 'global' (window keydown). Default is 'local'. / mode = input<'global' | 'local'>('local'); /** Whether to prevent the default action when the keybinding matches.",
|
|
4890
|
+
"defaultValue": "true"
|
|
4891
|
+
},
|
|
4892
|
+
{
|
|
4893
|
+
"name": "stopPropagation",
|
|
4894
|
+
"type": "boolean",
|
|
4895
|
+
"description": "The registered action name (e.g. 'table.next-page', 'dialog.close'). / shA11yKeybinding = input.required<string>(); /** Defines whether the keybinding listener is 'local' (host element keydown) or 'global' (window keydown). Default is 'local'. / mode = input<'global' | 'local'>('local'); /** Whether to prevent the default action when the keybinding matches. / preventDefault = input<boolean>(true); /** Whether to stop event propagation when the keybinding matches.",
|
|
4896
|
+
"defaultValue": "true"
|
|
4897
|
+
}
|
|
4898
|
+
],
|
|
4899
|
+
"outputs": [
|
|
4900
|
+
{
|
|
4901
|
+
"name": "triggered",
|
|
4902
|
+
"type": "KeyboardEvent",
|
|
4903
|
+
"description": "The registered action name (e.g. 'table.next-page', 'dialog.close'). / shA11yKeybinding = input.required<string>(); /** Defines whether the keybinding listener is 'local' (host element keydown) or 'global' (window keydown). Default is 'local'. / mode = input<'global' | 'local'>('local'); /** Whether to prevent the default action when the keybinding matches. / preventDefault = input<boolean>(true); /** Whether to stop event propagation when the keybinding matches. / stopPropagation = input<boolean>(true); /** Event emitted when the keybinding is triggered."
|
|
4904
|
+
}
|
|
4905
|
+
],
|
|
4906
|
+
"methods": [
|
|
4907
|
+
{
|
|
4908
|
+
"name": "effect",
|
|
4909
|
+
"parameters": "(onCleanup) => {\n if (this.mode() === 'global' && isPlatformBrowser(this.#platformId)",
|
|
4910
|
+
"returnType": "any",
|
|
4911
|
+
"description": ""
|
|
4912
|
+
},
|
|
4913
|
+
{
|
|
4914
|
+
"name": "onCleanup",
|
|
4915
|
+
"parameters": "() => {\n window.removeEventListener('keydown', listener);\n });\n }\n });\n }\n\n /**\n * Local keydown listener when mode is 'local'\n */\n @HostListener('keydown', ['$event'])\n onKeyDown(event: KeyboardEvent",
|
|
4916
|
+
"returnType": "void",
|
|
4917
|
+
"description": ""
|
|
4918
|
+
}
|
|
4919
|
+
],
|
|
4920
|
+
"cssVariables": [],
|
|
4921
|
+
"examples": [
|
|
4922
|
+
{
|
|
4923
|
+
"name": "basic-keybindings",
|
|
4924
|
+
"html": "<div class=\"example-wrapper\">\n <div class=\"card-grid\">\n <div class=\"card\">\n <h3>Local Trigger</h3>\n <p class=\"description\">\n Only triggers when the button itself is focused.\n </p>\n <div class=\"shortcut-info\">\n Shortcut: <code class=\"kbd-badge\">{{ service.getDisplayShortcut('example.increment-local') }}</code>\n </div>\n <button \n shButton \n [shA11yKeybinding]=\"'example.increment-local'\" \n mode=\"local\" \n (triggered)=\"incrementLocal()\">\n Local Increment ({{ localCounter() }})\n </button>\n </div>\n\n <div class=\"card\">\n <h3>Global Trigger</h3>\n <p class=\"description\">\n Triggers from anywhere on the page (ignored when focused in inputs).\n </p>\n <div class=\"shortcut-info\">\n Shortcut: <code class=\"kbd-badge\">{{ service.getDisplayShortcut('example.increment-global') }}</code>\n </div>\n <button \n shButton \n variant=\"flat\"\n [shA11yKeybinding]=\"'example.increment-global'\" \n mode=\"global\" \n (triggered)=\"incrementGlobal()\">\n Global Increment ({{ globalCounter() }})\n </button>\n </div>\n </div>\n\n <div class=\"controls-panel\">\n <button shButton variant=\"outlined\" size=\"small\" (click)=\"changeShortcut()\">\n Swap Local Shortcut to: {{ service.getShortcut('example.increment-local') === 'enter' ? 'Space' : 'Enter' }}\n </button>\n </div>\n</div>\n",
|
|
4925
|
+
"ts": "import { ChangeDetectionStrategy, Component, inject, signal } from '@angular/core';\nimport { ShipA11yKeybindingsDirective, ShipA11yKeybindingsService } from '@ship-ui/core/ship-a11y-keybindings';\nimport { ShipButton } from '@ship-ui/core/ship-button';\n\n@Component({\n selector: 'app-basic-keybindings',\n imports: [ShipA11yKeybindingsDirective, ShipButton],\n templateUrl: './basic-keybindings.html',\n styleUrl: './basic-keybindings.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class BasicKeybindingsComponent {\n readonly service = inject(ShipA11yKeybindingsService);\n\n localCounter = signal(0);\n globalCounter = signal(0);\n\n constructor() {\n // Register defaults for our example actions\n this.service.registerDefaults({\n 'example.increment-local': 'enter',\n 'example.increment-global': 'ctrlOrCmd+shift+s',\n });\n }\n\n incrementLocal() {\n this.localCounter.update((c) => c + 1);\n }\n\n incrementGlobal() {\n this.globalCounter.update((c) => c + 1);\n }\n\n changeShortcut() {\n // Override a keybinding at runtime\n const currentLocal = this.service.getShortcut('example.increment-local');\n const nextLocal = currentLocal === 'enter' ? 'space' : 'enter';\n\n this.service.registerOverrides({\n 'example.increment-local': nextLocal,\n });\n }\n}\n"
|
|
4926
|
+
}
|
|
4927
|
+
]
|
|
4928
|
+
},
|
|
4929
|
+
{
|
|
4930
|
+
"name": "ShipA11yKeybindingsService",
|
|
4931
|
+
"selector": "ship-a11y-keybindings-service",
|
|
4932
|
+
"path": "core/projects/ship-ui/ship-a11y-keybindings/ship-a11y-keybindings.service.ts",
|
|
4933
|
+
"description": "### Overview & Accessibility\n\nThe A11y Keybindings engine provides a unified system for handling keyboard shortcuts. The\n`[shA11yKeybinding]` directive dynamically sets standard `aria-keyshortcuts` attributes\non host elements to announce active hotkeys to assistive technologies.\n\n### Provider Overrides\n\nDefault shortcuts registered by components can be overridden globally at the application level.\nProvide the `SHIP_A11Y_KEYBINDINGS_OVERRIDE` injection token with a mapping object:\n`{ provide: SHIP_A11Y_KEYBINDINGS_OVERRIDE, useValue: { 'action.name': 'ctrl+s' } }`.\n\n### OS Adaptive Keys (Command vs Control)\n\nWe support the `ctrlOrCmd` modifier keyword in shortcuts. It maps dynamically at runtime:\nresolves to `Command (⌘)` on macOS and `Control (Ctrl)` on Windows/Linux. Display formats are also customized for each OS (e.g. `⌘⇧K` vs `Ctrl+Shift+K`).\n\n### Collisions & Input Handling\n\nWhen in `mode=\"global\"`, the directive automatically ignores keypresses that originate\nfrom input boxes, selects, textareas, or content-editables (unless modifier keys like Ctrl, Cmd, or Alt are pressed),\nensuring keyboard shortcuts do not interfere with typing.",
|
|
4934
|
+
"keywords": [
|
|
4935
|
+
"accessibility",
|
|
4936
|
+
"keybindings",
|
|
4937
|
+
"hotkeys",
|
|
4938
|
+
"shortcuts",
|
|
4939
|
+
"keyboard",
|
|
4940
|
+
"global",
|
|
4941
|
+
"local",
|
|
4942
|
+
"cmd",
|
|
4943
|
+
"ctrl",
|
|
4944
|
+
"modifiers",
|
|
4945
|
+
"focus"
|
|
4946
|
+
],
|
|
4947
|
+
"inputs": [],
|
|
4948
|
+
"outputs": [],
|
|
4949
|
+
"methods": [
|
|
4950
|
+
{
|
|
4951
|
+
"name": "registerDefaults",
|
|
4952
|
+
"parameters": "defaults: Record<string, string>",
|
|
4953
|
+
"returnType": "void",
|
|
4954
|
+
"description": "Returns true if the platform is running on macOS. / get isMac(): boolean { if (!isPlatformBrowser(this.#platformId)) return false; return navigator.userAgent.toLowerCase().includes('mac'); } constructor() { this.registerDefaults(DEFAULT_KEYBINDINGS); if (this.#overrides) { this.registerOverrides(this.#overrides); } } /** Registers default shortcuts for actions. If an override already exists for a given action, the override takes precedence."
|
|
4955
|
+
},
|
|
4956
|
+
{
|
|
4957
|
+
"name": "registerOverrides",
|
|
4958
|
+
"parameters": "overrides: Record<string, string>",
|
|
4959
|
+
"returnType": "void",
|
|
4960
|
+
"description": "Returns true if the platform is running on macOS. / get isMac(): boolean { if (!isPlatformBrowser(this.#platformId)) return false; return navigator.userAgent.toLowerCase().includes('mac'); } constructor() { this.registerDefaults(DEFAULT_KEYBINDINGS); if (this.#overrides) { this.registerOverrides(this.#overrides); } } /** Registers default shortcuts for actions. If an override already exists for a given action, the override takes precedence. / registerDefaults(defaults: Record<string, string>): void { for (const [action, shortcut] of Object.entries(defaults)) { this.#defaults.set(action, shortcut); // If we don't have an override/binding for this action, set it as the active binding if (!this.#bindings.has(action)) { this.#bindings.set(action, shortcut); } } } /** Registers overrides for actions. Overwrites any existing or default bindings."
|
|
4961
|
+
},
|
|
4962
|
+
{
|
|
4963
|
+
"name": "getShortcut",
|
|
4964
|
+
"parameters": "action: string",
|
|
4965
|
+
"returnType": "string | undefined",
|
|
4966
|
+
"description": "Returns true if the platform is running on macOS. / get isMac(): boolean { if (!isPlatformBrowser(this.#platformId)) return false; return navigator.userAgent.toLowerCase().includes('mac'); } constructor() { this.registerDefaults(DEFAULT_KEYBINDINGS); if (this.#overrides) { this.registerOverrides(this.#overrides); } } /** Registers default shortcuts for actions. If an override already exists for a given action, the override takes precedence. / registerDefaults(defaults: Record<string, string>): void { for (const [action, shortcut] of Object.entries(defaults)) { this.#defaults.set(action, shortcut); // If we don't have an override/binding for this action, set it as the active binding if (!this.#bindings.has(action)) { this.#bindings.set(action, shortcut); } } } /** Registers overrides for actions. Overwrites any existing or default bindings. / registerOverrides(overrides: Record<string, string>): void { for (const [action, shortcut] of Object.entries(overrides)) { this.#bindings.set(action, shortcut); } } /** Retrieves the active shortcut string for a registered action."
|
|
4967
|
+
},
|
|
4968
|
+
{
|
|
4969
|
+
"name": "getDefaultShortcut",
|
|
4970
|
+
"parameters": "action: string",
|
|
4971
|
+
"returnType": "string | undefined",
|
|
4972
|
+
"description": "Returns true if the platform is running on macOS. / get isMac(): boolean { if (!isPlatformBrowser(this.#platformId)) return false; return navigator.userAgent.toLowerCase().includes('mac'); } constructor() { this.registerDefaults(DEFAULT_KEYBINDINGS); if (this.#overrides) { this.registerOverrides(this.#overrides); } } /** Registers default shortcuts for actions. If an override already exists for a given action, the override takes precedence. / registerDefaults(defaults: Record<string, string>): void { for (const [action, shortcut] of Object.entries(defaults)) { this.#defaults.set(action, shortcut); // If we don't have an override/binding for this action, set it as the active binding if (!this.#bindings.has(action)) { this.#bindings.set(action, shortcut); } } } /** Registers overrides for actions. Overwrites any existing or default bindings. / registerOverrides(overrides: Record<string, string>): void { for (const [action, shortcut] of Object.entries(overrides)) { this.#bindings.set(action, shortcut); } } /** Retrieves the active shortcut string for a registered action. / getShortcut(action: string): string | undefined { return this.#bindings.get(action); } /** Retrieves the default shortcut string for a registered action."
|
|
4973
|
+
},
|
|
4974
|
+
{
|
|
4975
|
+
"name": "getDisplayShortcut",
|
|
4976
|
+
"parameters": "action: string",
|
|
4977
|
+
"returnType": "string | undefined",
|
|
4978
|
+
"description": "Returns true if the platform is running on macOS. / get isMac(): boolean { if (!isPlatformBrowser(this.#platformId)) return false; return navigator.userAgent.toLowerCase().includes('mac'); } constructor() { this.registerDefaults(DEFAULT_KEYBINDINGS); if (this.#overrides) { this.registerOverrides(this.#overrides); } } /** Registers default shortcuts for actions. If an override already exists for a given action, the override takes precedence. / registerDefaults(defaults: Record<string, string>): void { for (const [action, shortcut] of Object.entries(defaults)) { this.#defaults.set(action, shortcut); // If we don't have an override/binding for this action, set it as the active binding if (!this.#bindings.has(action)) { this.#bindings.set(action, shortcut); } } } /** Registers overrides for actions. Overwrites any existing or default bindings. / registerOverrides(overrides: Record<string, string>): void { for (const [action, shortcut] of Object.entries(overrides)) { this.#bindings.set(action, shortcut); } } /** Retrieves the active shortcut string for a registered action. / getShortcut(action: string): string | undefined { return this.#bindings.get(action); } /** Retrieves the default shortcut string for a registered action. / getDefaultShortcut(action: string): string | undefined { return this.#defaults.get(action); } /** Gets a formatted, user-friendly shortcut string for display. E.g. 'ctrlOrCmd+Shift+KeyK' -> '⌘⇧K' on macOS, 'Ctrl+Shift+K' on Windows/Linux. Supports comma-separated multiple shortcuts (e.g. 'ArrowRight, d' -> 'ArrowRight, D')."
|
|
4979
|
+
}
|
|
4980
|
+
],
|
|
4981
|
+
"cssVariables": [],
|
|
4982
|
+
"examples": [
|
|
4983
|
+
{
|
|
4984
|
+
"name": "basic-keybindings",
|
|
4985
|
+
"html": "<div class=\"example-wrapper\">\n <div class=\"card-grid\">\n <div class=\"card\">\n <h3>Local Trigger</h3>\n <p class=\"description\">\n Only triggers when the button itself is focused.\n </p>\n <div class=\"shortcut-info\">\n Shortcut: <code class=\"kbd-badge\">{{ service.getDisplayShortcut('example.increment-local') }}</code>\n </div>\n <button \n shButton \n [shA11yKeybinding]=\"'example.increment-local'\" \n mode=\"local\" \n (triggered)=\"incrementLocal()\">\n Local Increment ({{ localCounter() }})\n </button>\n </div>\n\n <div class=\"card\">\n <h3>Global Trigger</h3>\n <p class=\"description\">\n Triggers from anywhere on the page (ignored when focused in inputs).\n </p>\n <div class=\"shortcut-info\">\n Shortcut: <code class=\"kbd-badge\">{{ service.getDisplayShortcut('example.increment-global') }}</code>\n </div>\n <button \n shButton \n variant=\"flat\"\n [shA11yKeybinding]=\"'example.increment-global'\" \n mode=\"global\" \n (triggered)=\"incrementGlobal()\">\n Global Increment ({{ globalCounter() }})\n </button>\n </div>\n </div>\n\n <div class=\"controls-panel\">\n <button shButton variant=\"outlined\" size=\"small\" (click)=\"changeShortcut()\">\n Swap Local Shortcut to: {{ service.getShortcut('example.increment-local') === 'enter' ? 'Space' : 'Enter' }}\n </button>\n </div>\n</div>\n",
|
|
4986
|
+
"ts": "import { ChangeDetectionStrategy, Component, inject, signal } from '@angular/core';\nimport { ShipA11yKeybindingsDirective, ShipA11yKeybindingsService } from '@ship-ui/core/ship-a11y-keybindings';\nimport { ShipButton } from '@ship-ui/core/ship-button';\n\n@Component({\n selector: 'app-basic-keybindings',\n imports: [ShipA11yKeybindingsDirective, ShipButton],\n templateUrl: './basic-keybindings.html',\n styleUrl: './basic-keybindings.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class BasicKeybindingsComponent {\n readonly service = inject(ShipA11yKeybindingsService);\n\n localCounter = signal(0);\n globalCounter = signal(0);\n\n constructor() {\n // Register defaults for our example actions\n this.service.registerDefaults({\n 'example.increment-local': 'enter',\n 'example.increment-global': 'ctrlOrCmd+shift+s',\n });\n }\n\n incrementLocal() {\n this.localCounter.update((c) => c + 1);\n }\n\n incrementGlobal() {\n this.globalCounter.update((c) => c + 1);\n }\n\n changeShortcut() {\n // Override a keybinding at runtime\n const currentLocal = this.service.getShortcut('example.increment-local');\n const nextLocal = currentLocal === 'enter' ? 'space' : 'enter';\n\n this.service.registerOverrides({\n 'example.increment-local': nextLocal,\n });\n }\n}\n"
|
|
4987
|
+
}
|
|
4988
|
+
]
|
|
4989
|
+
},
|
|
4389
4990
|
{
|
|
4390
4991
|
"name": "ShipButton",
|
|
4391
4992
|
"selector": "[shButton]",
|