@fiscozen/checkbox 1.0.1 → 1.1.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.
Files changed (41) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +142 -0
  3. package/coverage/base.css +224 -0
  4. package/coverage/block-navigation.js +87 -0
  5. package/coverage/clover.xml +1031 -0
  6. package/coverage/coverage-final.json +7 -0
  7. package/coverage/favicon.png +0 -0
  8. package/coverage/index.html +131 -0
  9. package/coverage/prettify.css +1 -0
  10. package/coverage/prettify.js +2 -0
  11. package/coverage/sort-arrow-sprite.png +0 -0
  12. package/coverage/sorter.js +196 -0
  13. package/coverage/src/FzCheckbox.vue.html +1489 -0
  14. package/coverage/src/FzCheckboxGroup.vue.html +682 -0
  15. package/coverage/src/common.ts.html +157 -0
  16. package/coverage/src/components/ErrorAlert.vue.html +268 -0
  17. package/coverage/src/components/FzCheckboxGroupOption.vue.html +652 -0
  18. package/coverage/src/components/index.html +131 -0
  19. package/coverage/src/index.html +161 -0
  20. package/coverage/src/utils.ts.html +265 -0
  21. package/dist/checkbox.js +736 -555
  22. package/dist/checkbox.umd.cjs +2 -2
  23. package/dist/src/FzCheckboxCard.vue.d.ts +240 -0
  24. package/dist/src/FzCheckboxGroup.vue.d.ts +8 -2
  25. package/dist/src/__tests__/FzCheckboxCard.spec.d.ts +1 -0
  26. package/dist/src/common.d.ts +16 -5
  27. package/dist/src/index.d.ts +2 -1
  28. package/dist/src/types.d.ts +71 -2
  29. package/dist/style.css +1 -1
  30. package/package.json +4 -4
  31. package/src/FzCheckboxCard.vue +209 -0
  32. package/src/FzCheckboxGroup.vue +33 -10
  33. package/src/__tests__/FzCheckboxCard.spec.ts +423 -0
  34. package/src/__tests__/__snapshots__/FzCheckbox.spec.ts.snap +5 -11
  35. package/src/__tests__/__snapshots__/FzCheckboxCard.spec.ts.snap +17 -0
  36. package/src/__tests__/__snapshots__/FzCheckboxGroup.spec.ts.snap +5 -11
  37. package/src/common.ts +19 -0
  38. package/src/components/ErrorAlert.vue +4 -4
  39. package/src/index.ts +6 -1
  40. package/src/types.ts +86 -1
  41. package/tsconfig.tsbuildinfo +1 -1
package/CHANGELOG.md ADDED
@@ -0,0 +1,14 @@
1
+ # @fiscozen/checkbox
2
+
3
+ ## 1.1.2
4
+
5
+ ### Patch Changes
6
+
7
+ - 457253d: Use FzAlert variant text for error message
8
+
9
+ ## 1.1.1
10
+
11
+ ### Patch Changes
12
+
13
+ - Updated dependencies [a42b3b4]
14
+ - @fiscozen/alert@1.1.0
package/README.md CHANGED
@@ -7,6 +7,7 @@ A fully accessible, WCAG 2.1 AA compliant checkbox component library for Vue 3 a
7
7
  - **WCAG 2.1 AA Compliant** - Full accessibility support with comprehensive ARIA attributes
8
8
  - **Flexible Data Models** - Support for boolean (single) and array (multi-select) v-model binding
9
9
  - **Hierarchical Structures** - Parent-child checkbox relationships with automatic indeterminate state
10
+ - **Card-style Checkboxes** - Rich selection cards with images, titles, subtitles, and tooltips
10
11
  - **Keyboard Navigation** - Complete keyboard accessibility with visible focus indicators
11
12
  - **Screen Reader Optimized** - Proper semantic markup and live region announcements
12
13
  - **Error Handling** - Built-in validation states with accessible error messages
@@ -113,6 +114,37 @@ const options = [
113
114
  </template>
114
115
  ```
115
116
 
117
+ ### Checkbox Card
118
+
119
+ ```vue
120
+ <script setup lang="ts">
121
+ import { ref } from 'vue';
122
+ import { FzCheckboxCard } from '@fiscozen/checkbox';
123
+
124
+ const selected = ref<(string | number)[]>([]);
125
+ </script>
126
+
127
+ <template>
128
+ <div style="display: flex; gap: 12px;">
129
+ <FzCheckboxCard
130
+ v-model="selected"
131
+ label="option-a"
132
+ value="option-a"
133
+ title="Option A"
134
+ subtitle="Description for option A"
135
+ />
136
+
137
+ <FzCheckboxCard
138
+ v-model="selected"
139
+ label="option-b"
140
+ value="option-b"
141
+ title="Option B"
142
+ subtitle="Description for option B"
143
+ />
144
+ </div>
145
+ </template>
146
+ ```
147
+
116
148
  ## API Reference
117
149
 
118
150
  ### FzCheckbox
@@ -191,6 +223,45 @@ v-model: string[]
191
223
 
192
224
  ---
193
225
 
226
+ ### FzCheckboxCard
227
+
228
+ A card-style checkbox component with support for title, subtitle, optional image, and tooltip. Available in horizontal and vertical layout variants. Uses array v-model for multi-select patterns.
229
+
230
+ #### Props
231
+
232
+ | Prop | Type | Default | Description |
233
+ |------|------|---------|-------------|
234
+ | `label` | `string` | **required** | Accessible label for the underlying checkbox input |
235
+ | `value` | `string \| number` | `label` | Value tracked in the array v-model |
236
+ | `title` | `string` | **required** | Primary title text displayed in the card |
237
+ | `subtitle` | `string` | `undefined` | Optional secondary text below the title |
238
+ | `variant` | `'horizontal' \| 'vertical'` | `'horizontal'` | Layout direction of the card |
239
+ | `imageUrl` | `string` | `undefined` | URL of the card image (required when `variant` is `'vertical'`) |
240
+ | `imageAlt` | `string` | `undefined` | Alt text for the card image |
241
+ | `hasCheckbox` | `boolean` | `true` | Whether to show the checkbox icon inside the card |
242
+ | `emphasis` | `boolean` | `false` | Applies emphasis styling (blue icon when checked) |
243
+ | `disabled` | `boolean` | `false` | Disables the card |
244
+ | `error` | `boolean` | `false` | Shows error state (red checkbox icon) |
245
+ | `required` | `boolean` | `false` | Marks the input as required |
246
+ | `tooltip` | `string` | `undefined` | Text to display in the info tooltip |
247
+ | `tooltipStatus` | `FzTooltipStatus` | `'neutral'` | Tooltip color/status variant |
248
+ | `name` | `string` | `undefined` | Input `name` attribute for form submission |
249
+
250
+ > **Note:** When `variant` is `'vertical'`, the `imageUrl` prop is required since the vertical layout is designed around a full-width image.
251
+
252
+ #### v-model
253
+
254
+ ```typescript
255
+ v-model: (string | number | boolean)[]
256
+ ```
257
+
258
+ #### Variant Layouts
259
+
260
+ - **Horizontal** (default) — Checkbox icon, optional thumbnail image (58×58), and text are arranged in a row. Compact layout suited for lists.
261
+ - **Vertical** — Full-width 16:9 image on top, checkbox icon overlaid, and text below. Designed for visually rich selections.
262
+
263
+ ---
264
+
194
265
  ## Advanced Usage
195
266
 
196
267
  ### With Error Validation
@@ -368,6 +439,67 @@ const dayOptions = [
368
439
  </template>
369
440
  ```
370
441
 
442
+ ### Checkbox Card — Vertical with Image
443
+
444
+ ```vue
445
+ <script setup lang="ts">
446
+ import { ref } from 'vue';
447
+ import { FzCheckboxCard } from '@fiscozen/checkbox';
448
+
449
+ const selected = ref<(string | number)[]>([]);
450
+ </script>
451
+
452
+ <template>
453
+ <div style="display: flex; gap: 16px; max-width: 720px;">
454
+ <FzCheckboxCard
455
+ v-model="selected"
456
+ label="plan-basic"
457
+ value="basic"
458
+ variant="vertical"
459
+ image-url="/images/basic-plan.jpg"
460
+ image-alt="Basic plan illustration"
461
+ title="Basic Plan"
462
+ subtitle="For individuals getting started"
463
+ tooltip="Includes 5 GB storage"
464
+ />
465
+
466
+ <FzCheckboxCard
467
+ v-model="selected"
468
+ label="plan-pro"
469
+ value="pro"
470
+ variant="vertical"
471
+ image-url="/images/pro-plan.jpg"
472
+ image-alt="Pro plan illustration"
473
+ title="Pro Plan"
474
+ subtitle="For growing teams"
475
+ tooltip="Includes 50 GB storage"
476
+ />
477
+ </div>
478
+ </template>
479
+ ```
480
+
481
+ ### Checkbox Card — Without Checkbox Icon
482
+
483
+ ```vue
484
+ <script setup lang="ts">
485
+ import { ref } from 'vue';
486
+ import { FzCheckboxCard } from '@fiscozen/checkbox';
487
+
488
+ const selected = ref<(string | number)[]>([]);
489
+ </script>
490
+
491
+ <template>
492
+ <FzCheckboxCard
493
+ v-model="selected"
494
+ label="option"
495
+ value="option"
496
+ title="Selectable Card"
497
+ subtitle="The card border highlights on selection"
498
+ :has-checkbox="false"
499
+ />
500
+ </template>
501
+ ```
502
+
371
503
  ### Disabled State
372
504
 
373
505
  ```vue
@@ -458,6 +590,7 @@ This package is written in TypeScript and provides full type definitions.
458
590
  import type {
459
591
  FzCheckboxProps,
460
592
  FzCheckboxGroupProps,
593
+ FzCheckboxCardProps,
461
594
  ParentCheckbox,
462
595
  ChildCheckbox
463
596
  } from '@fiscozen/checkbox';
@@ -473,6 +606,15 @@ const options: ParentCheckbox[] = [
473
606
  ]
474
607
  }
475
608
  ];
609
+
610
+ // Example: Typed card props (vertical variant requires imageUrl)
611
+ const verticalCard: FzCheckboxCardProps = {
612
+ label: 'card',
613
+ title: 'Card Title',
614
+ subtitle: 'Description',
615
+ variant: 'vertical',
616
+ imageUrl: '/images/card.jpg',
617
+ };
476
618
  ```
477
619
 
478
620
  ## Accessibility Features
@@ -0,0 +1,224 @@
1
+ body, html {
2
+ margin:0; padding: 0;
3
+ height: 100%;
4
+ }
5
+ body {
6
+ font-family: Helvetica Neue, Helvetica, Arial;
7
+ font-size: 14px;
8
+ color:#333;
9
+ }
10
+ .small { font-size: 12px; }
11
+ *, *:after, *:before {
12
+ -webkit-box-sizing:border-box;
13
+ -moz-box-sizing:border-box;
14
+ box-sizing:border-box;
15
+ }
16
+ h1 { font-size: 20px; margin: 0;}
17
+ h2 { font-size: 14px; }
18
+ pre {
19
+ font: 12px/1.4 Consolas, "Liberation Mono", Menlo, Courier, monospace;
20
+ margin: 0;
21
+ padding: 0;
22
+ -moz-tab-size: 2;
23
+ -o-tab-size: 2;
24
+ tab-size: 2;
25
+ }
26
+ a { color:#0074D9; text-decoration:none; }
27
+ a:hover { text-decoration:underline; }
28
+ .strong { font-weight: bold; }
29
+ .space-top1 { padding: 10px 0 0 0; }
30
+ .pad2y { padding: 20px 0; }
31
+ .pad1y { padding: 10px 0; }
32
+ .pad2x { padding: 0 20px; }
33
+ .pad2 { padding: 20px; }
34
+ .pad1 { padding: 10px; }
35
+ .space-left2 { padding-left:55px; }
36
+ .space-right2 { padding-right:20px; }
37
+ .center { text-align:center; }
38
+ .clearfix { display:block; }
39
+ .clearfix:after {
40
+ content:'';
41
+ display:block;
42
+ height:0;
43
+ clear:both;
44
+ visibility:hidden;
45
+ }
46
+ .fl { float: left; }
47
+ @media only screen and (max-width:640px) {
48
+ .col3 { width:100%; max-width:100%; }
49
+ .hide-mobile { display:none!important; }
50
+ }
51
+
52
+ .quiet {
53
+ color: #7f7f7f;
54
+ color: rgba(0,0,0,0.5);
55
+ }
56
+ .quiet a { opacity: 0.7; }
57
+
58
+ .fraction {
59
+ font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace;
60
+ font-size: 10px;
61
+ color: #555;
62
+ background: #E8E8E8;
63
+ padding: 4px 5px;
64
+ border-radius: 3px;
65
+ vertical-align: middle;
66
+ }
67
+
68
+ div.path a:link, div.path a:visited { color: #333; }
69
+ table.coverage {
70
+ border-collapse: collapse;
71
+ margin: 10px 0 0 0;
72
+ padding: 0;
73
+ }
74
+
75
+ table.coverage td {
76
+ margin: 0;
77
+ padding: 0;
78
+ vertical-align: top;
79
+ }
80
+ table.coverage td.line-count {
81
+ text-align: right;
82
+ padding: 0 5px 0 20px;
83
+ }
84
+ table.coverage td.line-coverage {
85
+ text-align: right;
86
+ padding-right: 10px;
87
+ min-width:20px;
88
+ }
89
+
90
+ table.coverage td span.cline-any {
91
+ display: inline-block;
92
+ padding: 0 5px;
93
+ width: 100%;
94
+ }
95
+ .missing-if-branch {
96
+ display: inline-block;
97
+ margin-right: 5px;
98
+ border-radius: 3px;
99
+ position: relative;
100
+ padding: 0 4px;
101
+ background: #333;
102
+ color: yellow;
103
+ }
104
+
105
+ .skip-if-branch {
106
+ display: none;
107
+ margin-right: 10px;
108
+ position: relative;
109
+ padding: 0 4px;
110
+ background: #ccc;
111
+ color: white;
112
+ }
113
+ .missing-if-branch .typ, .skip-if-branch .typ {
114
+ color: inherit !important;
115
+ }
116
+ .coverage-summary {
117
+ border-collapse: collapse;
118
+ width: 100%;
119
+ }
120
+ .coverage-summary tr { border-bottom: 1px solid #bbb; }
121
+ .keyline-all { border: 1px solid #ddd; }
122
+ .coverage-summary td, .coverage-summary th { padding: 10px; }
123
+ .coverage-summary tbody { border: 1px solid #bbb; }
124
+ .coverage-summary td { border-right: 1px solid #bbb; }
125
+ .coverage-summary td:last-child { border-right: none; }
126
+ .coverage-summary th {
127
+ text-align: left;
128
+ font-weight: normal;
129
+ white-space: nowrap;
130
+ }
131
+ .coverage-summary th.file { border-right: none !important; }
132
+ .coverage-summary th.pct { }
133
+ .coverage-summary th.pic,
134
+ .coverage-summary th.abs,
135
+ .coverage-summary td.pct,
136
+ .coverage-summary td.abs { text-align: right; }
137
+ .coverage-summary td.file { white-space: nowrap; }
138
+ .coverage-summary td.pic { min-width: 120px !important; }
139
+ .coverage-summary tfoot td { }
140
+
141
+ .coverage-summary .sorter {
142
+ height: 10px;
143
+ width: 7px;
144
+ display: inline-block;
145
+ margin-left: 0.5em;
146
+ background: url(sort-arrow-sprite.png) no-repeat scroll 0 0 transparent;
147
+ }
148
+ .coverage-summary .sorted .sorter {
149
+ background-position: 0 -20px;
150
+ }
151
+ .coverage-summary .sorted-desc .sorter {
152
+ background-position: 0 -10px;
153
+ }
154
+ .status-line { height: 10px; }
155
+ /* yellow */
156
+ .cbranch-no { background: yellow !important; color: #111; }
157
+ /* dark red */
158
+ .red.solid, .status-line.low, .low .cover-fill { background:#C21F39 }
159
+ .low .chart { border:1px solid #C21F39 }
160
+ .highlighted,
161
+ .highlighted .cstat-no, .highlighted .fstat-no, .highlighted .cbranch-no{
162
+ background: #C21F39 !important;
163
+ }
164
+ /* medium red */
165
+ .cstat-no, .fstat-no, .cbranch-no, .cbranch-no { background:#F6C6CE }
166
+ /* light red */
167
+ .low, .cline-no { background:#FCE1E5 }
168
+ /* light green */
169
+ .high, .cline-yes { background:rgb(230,245,208) }
170
+ /* medium green */
171
+ .cstat-yes { background:rgb(161,215,106) }
172
+ /* dark green */
173
+ .status-line.high, .high .cover-fill { background:rgb(77,146,33) }
174
+ .high .chart { border:1px solid rgb(77,146,33) }
175
+ /* dark yellow (gold) */
176
+ .status-line.medium, .medium .cover-fill { background: #f9cd0b; }
177
+ .medium .chart { border:1px solid #f9cd0b; }
178
+ /* light yellow */
179
+ .medium { background: #fff4c2; }
180
+
181
+ .cstat-skip { background: #ddd; color: #111; }
182
+ .fstat-skip { background: #ddd; color: #111 !important; }
183
+ .cbranch-skip { background: #ddd !important; color: #111; }
184
+
185
+ span.cline-neutral { background: #eaeaea; }
186
+
187
+ .coverage-summary td.empty {
188
+ opacity: .5;
189
+ padding-top: 4px;
190
+ padding-bottom: 4px;
191
+ line-height: 1;
192
+ color: #888;
193
+ }
194
+
195
+ .cover-fill, .cover-empty {
196
+ display:inline-block;
197
+ height: 12px;
198
+ }
199
+ .chart {
200
+ line-height: 0;
201
+ }
202
+ .cover-empty {
203
+ background: white;
204
+ }
205
+ .cover-full {
206
+ border-right: none !important;
207
+ }
208
+ pre.prettyprint {
209
+ border: none !important;
210
+ padding: 0 !important;
211
+ margin: 0 !important;
212
+ }
213
+ .com { color: #999 !important; }
214
+ .ignore-none { color: #999; font-weight: normal; }
215
+
216
+ .wrapper {
217
+ min-height: 100%;
218
+ height: auto !important;
219
+ height: 100%;
220
+ margin: 0 auto -48px;
221
+ }
222
+ .footer, .push {
223
+ height: 48px;
224
+ }
@@ -0,0 +1,87 @@
1
+ /* eslint-disable */
2
+ var jumpToCode = (function init() {
3
+ // Classes of code we would like to highlight in the file view
4
+ var missingCoverageClasses = ['.cbranch-no', '.cstat-no', '.fstat-no'];
5
+
6
+ // Elements to highlight in the file listing view
7
+ var fileListingElements = ['td.pct.low'];
8
+
9
+ // We don't want to select elements that are direct descendants of another match
10
+ var notSelector = ':not(' + missingCoverageClasses.join('):not(') + ') > '; // becomes `:not(a):not(b) > `
11
+
12
+ // Selecter that finds elements on the page to which we can jump
13
+ var selector =
14
+ fileListingElements.join(', ') +
15
+ ', ' +
16
+ notSelector +
17
+ missingCoverageClasses.join(', ' + notSelector); // becomes `:not(a):not(b) > a, :not(a):not(b) > b`
18
+
19
+ // The NodeList of matching elements
20
+ var missingCoverageElements = document.querySelectorAll(selector);
21
+
22
+ var currentIndex;
23
+
24
+ function toggleClass(index) {
25
+ missingCoverageElements
26
+ .item(currentIndex)
27
+ .classList.remove('highlighted');
28
+ missingCoverageElements.item(index).classList.add('highlighted');
29
+ }
30
+
31
+ function makeCurrent(index) {
32
+ toggleClass(index);
33
+ currentIndex = index;
34
+ missingCoverageElements.item(index).scrollIntoView({
35
+ behavior: 'smooth',
36
+ block: 'center',
37
+ inline: 'center'
38
+ });
39
+ }
40
+
41
+ function goToPrevious() {
42
+ var nextIndex = 0;
43
+ if (typeof currentIndex !== 'number' || currentIndex === 0) {
44
+ nextIndex = missingCoverageElements.length - 1;
45
+ } else if (missingCoverageElements.length > 1) {
46
+ nextIndex = currentIndex - 1;
47
+ }
48
+
49
+ makeCurrent(nextIndex);
50
+ }
51
+
52
+ function goToNext() {
53
+ var nextIndex = 0;
54
+
55
+ if (
56
+ typeof currentIndex === 'number' &&
57
+ currentIndex < missingCoverageElements.length - 1
58
+ ) {
59
+ nextIndex = currentIndex + 1;
60
+ }
61
+
62
+ makeCurrent(nextIndex);
63
+ }
64
+
65
+ return function jump(event) {
66
+ if (
67
+ document.getElementById('fileSearch') === document.activeElement &&
68
+ document.activeElement != null
69
+ ) {
70
+ // if we're currently focused on the search input, we don't want to navigate
71
+ return;
72
+ }
73
+
74
+ switch (event.which) {
75
+ case 78: // n
76
+ case 74: // j
77
+ goToNext();
78
+ break;
79
+ case 66: // b
80
+ case 75: // k
81
+ case 80: // p
82
+ goToPrevious();
83
+ break;
84
+ }
85
+ };
86
+ })();
87
+ window.addEventListener('keydown', jumpToCode);