@cahyo-dimas/freeday 1.7.0
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/CHANGELOG.md +447 -0
- package/LICENSE +21 -0
- package/README.md +205 -0
- package/adapters/blazor/freeday-blazor.js +79 -0
- package/adapters/react/components/FdyAutocomplete.tsx +159 -0
- package/adapters/react/components/FdyCascade.tsx +230 -0
- package/adapters/react/components/FdyCfl.tsx +362 -0
- package/adapters/react/components/FdyChart.tsx +69 -0
- package/adapters/react/components/FdyCombo.tsx +166 -0
- package/adapters/react/components/FdyDateRange.tsx +66 -0
- package/adapters/react/components/FdyDatepicker.tsx +354 -0
- package/adapters/react/index.d.ts +37 -0
- package/adapters/react/index.js +9 -0
- package/adapters/react/tsconfig.json +14 -0
- package/adapters/react/useFreeday.js +44 -0
- package/adapters/react/usePopover.ts +64 -0
- package/adapters/vue/components/FdyAutocomplete.vue +174 -0
- package/adapters/vue/components/FdyCascade.vue +241 -0
- package/adapters/vue/components/FdyCfl.vue +354 -0
- package/adapters/vue/components/FdyChart.vue +89 -0
- package/adapters/vue/components/FdyCombo.vue +232 -0
- package/adapters/vue/components/FdyDateRange.vue +78 -0
- package/adapters/vue/components/FdyDatepicker.vue +333 -0
- package/adapters/vue/index.d.ts +44 -0
- package/adapters/vue/index.js +8 -0
- package/adapters/vue/useFreeday.js +44 -0
- package/adapters/vue/usePopover.ts +78 -0
- package/dist/freeday-autocomplete.js +135 -0
- package/dist/freeday-breakpoint.js +51 -0
- package/dist/freeday-carousel.js +111 -0
- package/dist/freeday-cascade.js +256 -0
- package/dist/freeday-cfl.js +213 -0
- package/dist/freeday-chart.js +403 -0
- package/dist/freeday-chip.js +83 -0
- package/dist/freeday-datepicker.js +321 -0
- package/dist/freeday-datetime.js +59 -0
- package/dist/freeday-drawer.js +43 -0
- package/dist/freeday-form.js +181 -0
- package/dist/freeday-mask.js +114 -0
- package/dist/freeday-menu.js +93 -0
- package/dist/freeday-popover.js +69 -0
- package/dist/freeday-rating.js +50 -0
- package/dist/freeday-select.js +194 -0
- package/dist/freeday-slider.js +34 -0
- package/dist/freeday-stepper.js +90 -0
- package/dist/freeday-table.js +475 -0
- package/dist/freeday-tabs.js +68 -0
- package/dist/freeday-timepicker.js +180 -0
- package/dist/freeday-toast.js +84 -0
- package/dist/freeday-tree.js +94 -0
- package/dist/freeday-upload.js +206 -0
- package/dist/freeday.bundle.css +1352 -0
- package/dist/freeday.css +1007 -0
- package/dist/freeday.js +3632 -0
- package/dist/freeday.tokens.css +343 -0
- package/package.json +72 -0
- package/src/base.css +20 -0
- package/src/components/.gitkeep +0 -0
- package/src/components/accordion.css +13 -0
- package/src/components/alert.css +18 -0
- package/src/components/app-shell.css +88 -0
- package/src/components/appbar.css +15 -0
- package/src/components/autocomplete.css +12 -0
- package/src/components/avatar.css +8 -0
- package/src/components/badge.css +27 -0
- package/src/components/breadcrumb.css +9 -0
- package/src/components/breakpoints.css +15 -0
- package/src/components/button.css +58 -0
- package/src/components/card.css +9 -0
- package/src/components/carousel.css +17 -0
- package/src/components/cascade.css +36 -0
- package/src/components/cfl.css +28 -0
- package/src/components/chart.css +55 -0
- package/src/components/chip.css +18 -0
- package/src/components/combo.css +27 -0
- package/src/components/datepicker.css +44 -0
- package/src/components/datetimepicker.css +8 -0
- package/src/components/description-list.css +6 -0
- package/src/components/divider.css +3 -0
- package/src/components/drawer.css +25 -0
- package/src/components/file-upload.css +34 -0
- package/src/components/filterbar.css +25 -0
- package/src/components/form-grid.css +8 -0
- package/src/components/input-group.css +22 -0
- package/src/components/input.css +13 -0
- package/src/components/kbd.css +2 -0
- package/src/components/menu.css +28 -0
- package/src/components/modal.css +22 -0
- package/src/components/pagination.css +8 -0
- package/src/components/progress.css +7 -0
- package/src/components/rating.css +10 -0
- package/src/components/selection.css +25 -0
- package/src/components/skeleton.css +7 -0
- package/src/components/slider.css +12 -0
- package/src/components/spinner.css +6 -0
- package/src/components/states.css +8 -0
- package/src/components/stepper.css +21 -0
- package/src/components/table.css +55 -0
- package/src/components/tabs.css +10 -0
- package/src/components/timeline.css +12 -0
- package/src/components/timepicker.css +22 -0
- package/src/components/toast.css +17 -0
- package/src/components/tooltip.css +6 -0
- package/src/components/tree.css +21 -0
- package/src/freeday-autocomplete.js +135 -0
- package/src/freeday-breakpoint.js +51 -0
- package/src/freeday-carousel.js +111 -0
- package/src/freeday-cascade.js +256 -0
- package/src/freeday-cfl.js +213 -0
- package/src/freeday-chart.js +403 -0
- package/src/freeday-chip.js +83 -0
- package/src/freeday-datepicker.js +321 -0
- package/src/freeday-datetime.js +59 -0
- package/src/freeday-drawer.js +43 -0
- package/src/freeday-form.js +181 -0
- package/src/freeday-mask.js +114 -0
- package/src/freeday-menu.js +93 -0
- package/src/freeday-popover.js +69 -0
- package/src/freeday-rating.js +50 -0
- package/src/freeday-select.js +194 -0
- package/src/freeday-slider.js +34 -0
- package/src/freeday-stepper.js +90 -0
- package/src/freeday-table.js +475 -0
- package/src/freeday-tabs.js +68 -0
- package/src/freeday-timepicker.js +180 -0
- package/src/freeday-toast.js +84 -0
- package/src/freeday-tree.js +94 -0
- package/src/freeday-upload.js +206 -0
- package/tokens/breakpoints.mjs +4 -0
- package/tokens/tokens.json +123 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/* Freeday — Selection controls */
|
|
2
|
+
.fdy-check,.fdy-radio,.fdy-switch{display:inline-flex;align-items:center;gap:var(--space-2);font-size:var(--text-sm);color:var(--color-text);cursor:pointer;}
|
|
3
|
+
.fdy-check input,.fdy-radio input{appearance:none;-webkit-appearance:none;width:var(--control-box);height:var(--control-box);margin:0;border:1.5px solid var(--color-control-border);background:var(--color-surface);box-shadow:inset 0 1px 2px var(--color-shadow-inset);cursor:pointer;display:grid;place-content:center;transition:background var(--dur-fast) var(--ease-standard),border-color var(--dur-fast) var(--ease-standard);}
|
|
4
|
+
.fdy-check input{border-radius:var(--radius-sm);}
|
|
5
|
+
.fdy-radio input{border-radius:50%;}
|
|
6
|
+
.fdy-check input::before{content:"";width:.62rem;height:.62rem;transform:scale(0);transition:transform var(--dur-fast) var(--ease-spring);box-shadow:inset 1rem 1rem var(--color-on-primary);clip-path:polygon(14% 44%,0 65%,50% 100%,100% 16%,80% 0,43% 62%);}
|
|
7
|
+
.fdy-radio input::before{content:"";width:.55rem;height:.55rem;border-radius:50%;transform:scale(0);transition:transform var(--dur-fast) var(--ease-spring);box-shadow:inset 1rem 1rem var(--color-on-primary);}
|
|
8
|
+
.fdy-check input:checked,.fdy-radio input:checked{background:var(--color-primary);border-color:var(--color-primary);}
|
|
9
|
+
.fdy-check input:checked::before,.fdy-radio input:checked::before{transform:scale(1);}
|
|
10
|
+
.fdy-check input:focus-visible,.fdy-radio input:focus-visible,.fdy-switch input:focus-visible{outline:none;box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary) 26%,transparent);}
|
|
11
|
+
.fdy-check input:disabled,.fdy-radio input:disabled,.fdy-switch input:disabled{opacity:.5;cursor:not-allowed;}
|
|
12
|
+
.fdy-check:has(input:disabled),.fdy-radio:has(input:disabled),.fdy-switch:has(input:disabled){cursor:not-allowed;}
|
|
13
|
+
.fdy-switch input{appearance:none;-webkit-appearance:none;position:relative;width:var(--control-switch-w);height:var(--control-switch-h);margin:0;border-radius:var(--radius-full);background:var(--color-control-border);cursor:pointer;transition:background var(--dur-base) var(--ease-standard);}
|
|
14
|
+
.fdy-switch input::before{content:"";position:absolute;top:2px;left:2px;width:calc(var(--control-switch-h) - 4px);height:calc(var(--control-switch-h) - 4px);border-radius:50%;background:var(--color-switch-thumb);box-shadow:var(--shadow-1);transition:transform var(--dur-base) var(--ease-spring);}
|
|
15
|
+
.fdy-switch input:checked{background:var(--color-primary);}
|
|
16
|
+
.fdy-switch input:checked::before{transform:translateX(calc(var(--control-switch-w) - var(--control-switch-h)));}
|
|
17
|
+
|
|
18
|
+
/* Standalone checkbox (e.g. table row/select-all) — supports :indeterminate */
|
|
19
|
+
.fdy-checkbox{appearance:none;-webkit-appearance:none;width:var(--control-box);height:var(--control-box);margin:0;border:1.5px solid var(--color-control-border);border-radius:var(--radius-sm);background:var(--color-surface);box-shadow:inset 0 1px 2px var(--color-shadow-inset);cursor:pointer;display:grid;place-content:center;transition:background var(--dur-fast) var(--ease-standard),border-color var(--dur-fast) var(--ease-standard);}
|
|
20
|
+
.fdy-checkbox::before{content:"";width:.62rem;height:.62rem;transform:scale(0);transition:transform var(--dur-fast) var(--ease-spring);box-shadow:inset 1rem 1rem var(--color-on-primary);clip-path:polygon(14% 44%,0 65%,50% 100%,100% 16%,80% 0,43% 62%);}
|
|
21
|
+
.fdy-checkbox:checked,.fdy-checkbox:indeterminate{background:var(--color-primary);border-color:var(--color-primary);}
|
|
22
|
+
.fdy-checkbox:checked::before{transform:scale(1);}
|
|
23
|
+
.fdy-checkbox:indeterminate::before{width:.6rem;height:2px;transform:scale(1);clip-path:none;box-shadow:none;background:var(--color-on-primary);}
|
|
24
|
+
.fdy-checkbox:focus-visible{outline:none;box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary) 26%,transparent);}
|
|
25
|
+
.fdy-checkbox:disabled{opacity:.5;cursor:not-allowed;}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/* Freeday — Skeleton */
|
|
2
|
+
.fdy-skeleton{display:block;border-radius:var(--radius-sm);background:var(--color-surface-3);background-image:linear-gradient(90deg,transparent,color-mix(in srgb,var(--color-surface) 60%,transparent),transparent);background-size:200% 100%;background-repeat:no-repeat;animation:fdy-skeleton 1.4s ease-in-out infinite;}
|
|
3
|
+
.fdy-skeleton--text{height:.8rem;margin:.3rem 0;}
|
|
4
|
+
.fdy-skeleton--title{height:1.2rem;width:40%;}
|
|
5
|
+
.fdy-skeleton--circle{border-radius:var(--radius-full);}
|
|
6
|
+
@keyframes fdy-skeleton{to{background-position:-200% 0;}}
|
|
7
|
+
@media (prefers-reduced-motion:reduce){.fdy-skeleton{animation:none;}}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/* Freeday — Slider (native <input type="range"> themed via accent-color: keeps native
|
|
2
|
+
* keyboard + ARIA, filled track and thumb come free). Optional value output + scale.
|
|
3
|
+
* freeday-slider.js just mirrors the value into an <output for="…">. */
|
|
4
|
+
.fdy-slider{width:100%;max-width:22rem;height:1.5rem;accent-color:var(--color-primary);cursor:pointer;}
|
|
5
|
+
.fdy-slider:disabled{cursor:not-allowed;opacity:.6;}
|
|
6
|
+
.fdy-slider--accent{accent-color:var(--color-accent);}
|
|
7
|
+
.fdy-slider--danger{accent-color:var(--color-danger);}
|
|
8
|
+
|
|
9
|
+
.fdy-slider-field{display:flex;flex-direction:column;gap:var(--space-2);max-width:22rem;}
|
|
10
|
+
.fdy-slider-field__head{display:flex;align-items:baseline;justify-content:space-between;gap:var(--space-3);}
|
|
11
|
+
.fdy-slider__value{font-family:var(--font-mono);font-size:var(--text-sm);font-weight:var(--weight-semibold);color:var(--color-primary-strong);font-variant-numeric:tabular-nums;}
|
|
12
|
+
.fdy-slider__scale{display:flex;justify-content:space-between;font-size:var(--text-xs);color:var(--color-text-muted);font-variant-numeric:tabular-nums;}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/* Freeday — Spinner */
|
|
2
|
+
.fdy-spinner{display:inline-block;width:1.5rem;height:1.5rem;border:2.5px solid var(--color-border-strong);border-top-color:var(--color-primary);border-radius:var(--radius-full);animation:fdy-spin .7s linear infinite;}
|
|
3
|
+
.fdy-spinner--sm{width:1rem;height:1rem;border-width:2px;}
|
|
4
|
+
.fdy-spinner--lg{width:2.5rem;height:2.5rem;border-width:3px;}
|
|
5
|
+
@keyframes fdy-spin{to{transform:rotate(360deg);}}
|
|
6
|
+
@media (prefers-reduced-motion:reduce){.fdy-spinner{animation-duration:2s;}}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/* Freeday — States (empty / loading / error placeholders) */
|
|
2
|
+
.fdy-state{display:flex;flex-direction:column;align-items:center;text-align:center;gap:var(--space-2);padding:var(--space-10) var(--space-6);}
|
|
3
|
+
.fdy-state__icon{display:inline-flex;align-items:center;justify-content:center;width:2.5rem;height:2.5rem;color:var(--color-text-subtle);}
|
|
4
|
+
.fdy-state__icon svg{width:100%;height:100%;}
|
|
5
|
+
.fdy-state__title{font-family:var(--font-display);font-size:var(--text-lg);font-weight:var(--weight-semibold);color:var(--color-text);}
|
|
6
|
+
.fdy-state__text{font-size:var(--text-sm);color:var(--color-text-muted);max-width:24rem;line-height:var(--leading-normal);}
|
|
7
|
+
.fdy-state__actions{margin-top:var(--space-2);display:flex;gap:var(--space-2);flex-wrap:wrap;justify-content:center;}
|
|
8
|
+
.fdy-state--danger .fdy-state__icon{color:var(--color-danger-strong);}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/* Freeday — Stepper / Wizard (progress header + optional panels).
|
|
2
|
+
* Enhanced by freeday-stepper.js for prev/next navigation. */
|
|
3
|
+
.fdy-stepper{display:flex;list-style:none;margin:0;padding:0;}
|
|
4
|
+
.fdy-step{flex:1;position:relative;display:flex;flex-direction:column;align-items:center;text-align:center;min-width:0;}
|
|
5
|
+
/* connector from the previous marker's centre to this one (behind the markers) */
|
|
6
|
+
.fdy-step:not(:first-child)::before{content:"";position:absolute;top:.875rem;left:-50%;width:100%;height:2px;transform:translateY(-50%);background:var(--color-border-strong);z-index:0;}
|
|
7
|
+
.fdy-step.is-active::before,.fdy-step.is-complete::before{background:var(--color-primary);}
|
|
8
|
+
.fdy-step__btn{position:relative;z-index:1;display:flex;flex-direction:column;align-items:center;gap:var(--space-2);padding:0 var(--space-2);background:none;border:0;font:inherit;color:inherit;cursor:pointer;max-width:100%;}
|
|
9
|
+
.fdy-step__btn:disabled{cursor:default;}
|
|
10
|
+
.fdy-step__marker{display:inline-flex;align-items:center;justify-content:center;width:1.75rem;height:1.75rem;flex:none;border-radius:var(--radius-full);background:var(--color-surface);border:2px solid var(--color-border-strong);color:var(--color-text-muted);font-size:var(--text-sm);font-weight:var(--weight-semibold);font-variant-numeric:tabular-nums;}
|
|
11
|
+
.fdy-step__marker svg{display:block;width:1rem;height:1rem;}
|
|
12
|
+
.fdy-step.is-active .fdy-step__marker{border-color:var(--color-primary);background:var(--color-primary-soft);color:var(--color-primary-strong);}
|
|
13
|
+
.fdy-step.is-complete .fdy-step__marker{border-color:var(--color-primary);background:var(--color-primary);color:var(--color-on-primary);}
|
|
14
|
+
.fdy-step.is-error .fdy-step__marker{border-color:var(--color-danger);background:var(--color-danger-btn);color:var(--color-on-danger);}
|
|
15
|
+
.fdy-step__label{font-size:var(--text-sm);color:var(--color-text-muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%;}
|
|
16
|
+
.fdy-step.is-active .fdy-step__label{color:var(--color-text);font-weight:var(--weight-semibold);}
|
|
17
|
+
.fdy-step__btn:focus-visible{outline:none;}
|
|
18
|
+
.fdy-step__btn:focus-visible .fdy-step__marker{box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary) 30%,transparent);}
|
|
19
|
+
|
|
20
|
+
.fdy-step-panels{margin-top:var(--space-5);}
|
|
21
|
+
.fdy-step-nav{display:flex;justify-content:space-between;gap:var(--space-2);margin-top:var(--space-5);}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/* Freeday — Table */
|
|
2
|
+
.fdy-table-wrap{overflow-x:auto;border:var(--bw) solid var(--color-border);border-radius:var(--radius-lg);box-shadow:var(--shadow-1);background:var(--color-surface);}
|
|
3
|
+
.fdy-table{width:100%;border-collapse:collapse;font-size:var(--text-sm);}
|
|
4
|
+
.fdy-table caption{text-align:left;padding:var(--space-3) var(--space-4);font-weight:var(--weight-semibold);color:var(--color-text);}
|
|
5
|
+
.fdy-table th{text-align:left;padding:var(--space-3) var(--space-4);font-size:var(--text-xs);font-weight:var(--weight-semibold);text-transform:uppercase;letter-spacing:var(--tracking-wide);color:var(--color-text-muted);background:var(--color-surface-2);border-bottom:var(--bw) solid var(--color-border);white-space:nowrap;}
|
|
6
|
+
.fdy-table td{padding:var(--space-3) var(--space-4);border-bottom:var(--bw) solid var(--color-border-muted);color:var(--color-text);}
|
|
7
|
+
.fdy-table tbody tr:last-child td{border-bottom:0;}
|
|
8
|
+
.fdy-table tbody tr{transition:background-color var(--dur-fast) var(--ease-standard);}
|
|
9
|
+
.fdy-table tbody tr:hover{background:var(--color-surface-2);}
|
|
10
|
+
.fdy-table__num{text-align:right;font-variant-numeric:tabular-nums;font-family:var(--font-mono);}
|
|
11
|
+
.fdy-table th[aria-sort]{cursor:pointer;}
|
|
12
|
+
.fdy-table th[aria-sort="ascending"]::after{content:" ↑";color:var(--color-primary);}
|
|
13
|
+
.fdy-table th[aria-sort="descending"]::after{content:" ↓";color:var(--color-primary);}
|
|
14
|
+
|
|
15
|
+
/* Sortable header button (accessible click target inside th) */
|
|
16
|
+
.fdy-table__sortbtn{appearance:none;border:0;background:transparent;font:inherit;color:inherit;text-transform:inherit;letter-spacing:inherit;font-weight:inherit;cursor:pointer;padding:0;}
|
|
17
|
+
.fdy-table__sortbtn:hover{color:var(--color-text);}
|
|
18
|
+
.fdy-table__sortbtn:focus-visible{outline:none;border-radius:var(--radius-sm);box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary) 26%,transparent);}
|
|
19
|
+
|
|
20
|
+
/* Selection column + selected row */
|
|
21
|
+
.fdy-table__selcol{width:1px;white-space:nowrap;}
|
|
22
|
+
.fdy-table tbody tr[aria-selected="true"],.fdy-table tbody tr[aria-selected="true"]:hover{background:var(--color-primary-soft);}
|
|
23
|
+
|
|
24
|
+
/* Data-table shell (toolbar + scroll + footer stay put while the table scrolls) */
|
|
25
|
+
.fdy-datatable{border:var(--bw) solid var(--color-border);border-radius:var(--radius-lg);box-shadow:var(--shadow-1);background:var(--color-surface);overflow:hidden;}
|
|
26
|
+
.fdy-table-scroll{overflow-x:auto;}
|
|
27
|
+
.fdy-table-toolbar{display:flex;align-items:center;gap:var(--space-3);flex-wrap:wrap;padding:var(--space-3) var(--space-4);border-bottom:var(--bw) solid var(--color-border);}
|
|
28
|
+
.fdy-table-toolbar__search{max-width:18rem;}
|
|
29
|
+
.fdy-table-toolbar__spacer{flex:1;}
|
|
30
|
+
.fdy-table-toolbar__count{font-size:var(--text-sm);color:var(--color-text-muted);white-space:nowrap;}
|
|
31
|
+
.fdy-table-footer{display:flex;align-items:center;justify-content:space-between;gap:var(--space-3);flex-wrap:wrap;padding:var(--space-3) var(--space-4);border-top:var(--bw) solid var(--color-border);}
|
|
32
|
+
.fdy-table-footer__info{font-size:var(--text-sm);color:var(--color-text-muted);}
|
|
33
|
+
|
|
34
|
+
/* Column filter — funnel button injected into a filterable <th> */
|
|
35
|
+
.fdy-table__filterbtn{appearance:none;display:inline-flex;align-items:center;justify-content:center;width:1.5rem;height:1.5rem;margin-left:var(--space-1);padding:0;vertical-align:middle;border:0;border-radius:var(--radius-sm);background:transparent;color:var(--color-text-subtle);cursor:pointer;}
|
|
36
|
+
.fdy-table__filterbtn:hover{background:var(--color-surface-3);color:var(--color-text);}
|
|
37
|
+
.fdy-table__filterbtn:focus-visible{outline:none;box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary) 26%,transparent);}
|
|
38
|
+
.fdy-table__filterbtn.is-active{color:var(--color-primary-strong);}
|
|
39
|
+
.fdy-table__filterbtn svg{width:.85rem;height:.85rem;}
|
|
40
|
+
|
|
41
|
+
/* Filter popover (position:fixed, appended to <body> so overflow can't clip it) */
|
|
42
|
+
.fdy-filter{position:fixed;z-index:130;min-width:14rem;max-width:18rem;padding:var(--space-3);background:var(--color-surface);border:var(--bw) solid var(--color-border-strong);border-radius:var(--radius-md);box-shadow:var(--shadow-3);text-transform:none;letter-spacing:normal;font-weight:var(--weight-regular);color:var(--color-text);}
|
|
43
|
+
.fdy-filter__title{font-size:var(--text-xs);font-weight:var(--weight-semibold);color:var(--color-text-muted);text-transform:uppercase;letter-spacing:var(--tracking-wide);margin-bottom:var(--space-2);}
|
|
44
|
+
.fdy-filter__list{display:flex;flex-direction:column;gap:var(--space-1);max-height:12rem;overflow:auto;}
|
|
45
|
+
.fdy-filter__check{display:flex;align-items:center;gap:var(--space-2);font-size:var(--text-sm);color:var(--color-text);cursor:pointer;}
|
|
46
|
+
.fdy-filter__range{display:flex;align-items:center;gap:var(--space-2);}
|
|
47
|
+
.fdy-filter__range .fdy-input{width:6rem;}
|
|
48
|
+
.fdy-filter__foot{display:flex;justify-content:space-between;gap:var(--space-2);margin-top:var(--space-3);}
|
|
49
|
+
|
|
50
|
+
/* Bulk-action bar — appears above the table body once rows are selected */
|
|
51
|
+
.fdy-table-bulkbar{display:flex;align-items:center;gap:var(--space-3);padding:var(--space-2) var(--space-4);background:var(--color-primary-soft);border-bottom:var(--bw) solid var(--color-border);color:var(--color-primary-strong);font-size:var(--text-sm);}
|
|
52
|
+
.fdy-table-bulkbar[hidden]{display:none;}
|
|
53
|
+
.fdy-table-bulkbar__count{font-weight:var(--weight-semibold);white-space:nowrap;}
|
|
54
|
+
.fdy-table-bulkbar__spacer{flex:1;}
|
|
55
|
+
.fdy-table-bulkbar__actions{display:flex;gap:var(--space-2);}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/* Freeday — Tabs (WAI-ARIA APG) */
|
|
2
|
+
.fdy-tabs__list{display:flex;gap:var(--space-1);border-bottom:var(--bw) solid var(--color-border);overflow-x:auto;}
|
|
3
|
+
.fdy-tabs__tab{appearance:none;border:0;background:transparent;cursor:pointer;white-space:nowrap;font-family:var(--font-body);font-size:var(--text-sm);font-weight:var(--weight-medium);color:var(--color-text-muted);padding:var(--space-3) var(--space-4);border-bottom:2px solid transparent;margin-bottom:-1px;transition:color var(--dur-fast) var(--ease-standard),border-color var(--dur-fast) var(--ease-standard);}
|
|
4
|
+
.fdy-tabs__tab:hover{color:var(--color-text);}
|
|
5
|
+
.fdy-tabs__tab[aria-selected="true"]{color:var(--color-primary-strong);border-bottom-color:var(--color-primary);}
|
|
6
|
+
.fdy-tabs__tab:focus-visible{outline:none;border-radius:var(--radius-sm);box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary) 26%,transparent);}
|
|
7
|
+
.fdy-tabs__panel{padding:var(--space-5) 0;}
|
|
8
|
+
.fdy-tabs__panel[hidden]{display:none;}
|
|
9
|
+
/* Reveal the newly-shown panel on tab switch (fade + rise), reduced-motion gated. */
|
|
10
|
+
@media (prefers-reduced-motion:no-preference){.fdy-tabs__panel:not([hidden]){animation:fdy-combo-in var(--dur-base) var(--ease-standard);}}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/* Freeday — Timeline (vertical event feed: dot markers on a connector line). Pure CSS. */
|
|
2
|
+
.fdy-timeline{list-style:none;margin:0;padding:0;}
|
|
3
|
+
.fdy-timeline__item{position:relative;padding:0 0 var(--space-5) var(--space-6);border-left:2px solid var(--color-border);}
|
|
4
|
+
.fdy-timeline__item:last-child{border-left-color:transparent;padding-bottom:0;}
|
|
5
|
+
.fdy-timeline__marker{position:absolute;left:0;top:.15rem;transform:translateX(-50%);width:.85rem;height:.85rem;border-radius:var(--radius-full);background:var(--color-primary);border:2px solid var(--color-surface);box-shadow:0 0 0 2px var(--color-border);}
|
|
6
|
+
.fdy-timeline__item--success .fdy-timeline__marker{background:var(--color-success);}
|
|
7
|
+
.fdy-timeline__item--warning .fdy-timeline__marker{background:var(--color-warning);}
|
|
8
|
+
.fdy-timeline__item--danger .fdy-timeline__marker{background:var(--color-danger);}
|
|
9
|
+
.fdy-timeline__item--muted .fdy-timeline__marker{background:var(--color-border-strong);}
|
|
10
|
+
.fdy-timeline__time{font-size:var(--text-xs);color:var(--color-text-muted);font-variant-numeric:tabular-nums;}
|
|
11
|
+
.fdy-timeline__title{margin:0;font-size:var(--text-sm);font-weight:var(--weight-semibold);color:var(--color-text);}
|
|
12
|
+
.fdy-timeline__text{margin:var(--space-1) 0 0;font-size:var(--text-sm);color:var(--color-text-muted);line-height:var(--leading-normal);}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/* Freeday — Time picker (input-styled trigger + scrollable time-list popover).
|
|
2
|
+
* Built by freeday-timepicker.js from an empty [data-fdy-timepicker] wrapper.
|
|
3
|
+
* Trigger visual mirrors the date picker for a consistent control family. */
|
|
4
|
+
.fdy-timepicker{position:relative;display:inline-block;width:100%;max-width:11rem;}
|
|
5
|
+
.fdy-timepicker__trigger{display:inline-flex;align-items:center;gap:var(--space-2);width:100%;height:var(--control-h);padding:0 var(--space-3);text-align:left;cursor:pointer;font-family:var(--font-body);font-size:var(--text-sm);color:var(--color-text);background:var(--color-surface);border:1.5px solid var(--color-control-border);border-radius:var(--radius-md);box-shadow:inset 0 1px 2px rgba(16,14,30,.06);transition:border-color var(--dur-fast) var(--ease-standard),box-shadow var(--dur-fast) var(--ease-standard);}
|
|
6
|
+
.fdy-timepicker__trigger:hover{border-color:var(--color-text-muted);}
|
|
7
|
+
.fdy-timepicker__trigger:focus-visible{outline:none;border-color:var(--color-primary);box-shadow:inset 0 1px 2px rgba(16,14,30,.04),0 0 0 3px color-mix(in srgb,var(--color-primary) 26%,transparent);}
|
|
8
|
+
.fdy-timepicker__trigger.is-open{border-color:var(--color-primary);box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary) 26%,transparent);}
|
|
9
|
+
.fdy-timepicker--error .fdy-timepicker__trigger,.fdy-timepicker__trigger[aria-invalid="true"]{border-color:var(--color-danger);}
|
|
10
|
+
.fdy-timepicker--error .fdy-timepicker__trigger:focus-visible,.fdy-timepicker--error .fdy-timepicker__trigger.is-open,.fdy-timepicker__trigger[aria-invalid="true"]:focus-visible{border-color:var(--color-danger);box-shadow:0 0 0 3px color-mix(in srgb,var(--color-danger) 26%,transparent);}
|
|
11
|
+
.fdy-timepicker__value{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-variant-numeric:tabular-nums;}
|
|
12
|
+
.fdy-timepicker__value--placeholder{color:var(--color-text-subtle);}
|
|
13
|
+
.fdy-timepicker__icon{flex:none;display:inline-flex;color:var(--color-text-subtle);}
|
|
14
|
+
.fdy-timepicker__icon svg{display:block;width:1.05rem;height:1.05rem;}
|
|
15
|
+
|
|
16
|
+
.fdy-timepicker__panel{position:absolute;top:calc(100% + var(--space-1));left:0;z-index:130;min-width:100%;max-height:15rem;overflow-y:auto;margin:0;padding:var(--space-1);list-style:none;background:var(--color-surface);color:var(--color-text);border:var(--bw) solid var(--color-border-strong);border-radius:var(--radius-md);box-shadow:var(--shadow-3);}
|
|
17
|
+
.fdy-timepicker__panel[hidden]{display:none;}
|
|
18
|
+
.fdy-timepicker__panel:focus-visible{outline:none;}
|
|
19
|
+
.fdy-timepicker__opt{display:flex;align-items:center;padding:var(--space-2) var(--space-3);border-radius:var(--radius-sm);font-size:var(--text-sm);color:var(--color-text);font-variant-numeric:tabular-nums;cursor:pointer;white-space:nowrap;}
|
|
20
|
+
.fdy-timepicker__opt:hover,.fdy-timepicker__opt.is-active{background:var(--color-surface-3);}
|
|
21
|
+
.fdy-timepicker__opt[aria-selected="true"]{background:var(--color-primary-soft);color:var(--color-primary-strong);font-weight:var(--weight-semibold);}
|
|
22
|
+
@media (prefers-reduced-motion:no-preference){.fdy-timepicker__panel:not([hidden]){animation:fdy-combo-in var(--dur-fast) var(--ease-standard);}}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/* Freeday — Toast (transient notification) */
|
|
2
|
+
.fdy-toast-region{position:fixed;z-index:200;bottom:var(--space-5);right:var(--space-5);display:flex;flex-direction:column;gap:var(--space-3);width:min(24rem,calc(100vw - var(--space-8)));pointer-events:none;}
|
|
3
|
+
.fdy-toast{pointer-events:auto;display:flex;gap:var(--space-3);align-items:flex-start;padding:var(--space-3) var(--space-4);border-radius:var(--radius-md);background:var(--color-inverse-surface);color:var(--color-inverse-text);box-shadow:var(--shadow-3);}
|
|
4
|
+
.fdy-toast__accent{flex:none;width:.25rem;align-self:stretch;border-radius:var(--radius-full);background:var(--color-primary);}
|
|
5
|
+
.fdy-toast--success .fdy-toast__accent{background:var(--color-success);}
|
|
6
|
+
.fdy-toast--warning .fdy-toast__accent{background:var(--color-warning);}
|
|
7
|
+
.fdy-toast--danger .fdy-toast__accent{background:var(--color-danger);}
|
|
8
|
+
.fdy-toast--info .fdy-toast__accent{background:var(--color-info);}
|
|
9
|
+
.fdy-toast__body{flex:1;min-width:0;display:flex;flex-direction:column;gap:var(--space-1);font-size:var(--text-sm);}
|
|
10
|
+
.fdy-toast__title{font-weight:var(--weight-semibold);}
|
|
11
|
+
.fdy-toast__text{opacity:.88;line-height:var(--leading-normal);}
|
|
12
|
+
.fdy-toast__close{flex:none;border:0;background:transparent;color:var(--color-inverse-text);opacity:.7;cursor:pointer;font-size:var(--text-lg);line-height:1;padding:0 var(--space-1);border-radius:var(--radius-sm);}
|
|
13
|
+
.fdy-toast__close:hover{opacity:1;}
|
|
14
|
+
.fdy-toast__close:focus-visible{outline:none;opacity:1;box-shadow:0 0 0 3px color-mix(in srgb,var(--color-inverse-text) 40%,transparent);}
|
|
15
|
+
.fdy-toast.is-leaving{opacity:0;transform:translateX(16px);transition:opacity var(--dur-base) var(--ease-standard),transform var(--dur-base) var(--ease-standard);}
|
|
16
|
+
@media (prefers-reduced-motion:no-preference){.fdy-toast{animation:fdy-toast-in var(--dur-base) var(--ease-spring);}}
|
|
17
|
+
@keyframes fdy-toast-in{from{opacity:0;transform:translateY(14px);}to{opacity:1;transform:none;}}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/* Freeday — Tooltip (CSS-only, hover/focus) */
|
|
2
|
+
.fdy-tooltip-wrap{position:relative;display:inline-flex;}
|
|
3
|
+
.fdy-tooltip{position:absolute;bottom:calc(100% + var(--space-2));left:50%;z-index:150;max-width:16rem;padding:var(--space-1) var(--space-2);border-radius:var(--radius-sm);font-size:var(--text-xs);font-weight:var(--weight-medium);line-height:var(--leading-snug);color:var(--color-inverse-text);background:var(--color-inverse-surface);box-shadow:var(--shadow-2);opacity:0;visibility:hidden;pointer-events:none;transform:translateX(-50%) translateY(4px);transition:opacity var(--dur-fast) var(--ease-standard),transform var(--dur-fast) var(--ease-standard);}
|
|
4
|
+
.fdy-tooltip::after{content:"";position:absolute;top:100%;left:50%;transform:translateX(-50%);border:.3rem solid transparent;border-top-color:var(--color-inverse-surface);}
|
|
5
|
+
.fdy-tooltip-wrap:hover .fdy-tooltip,.fdy-tooltip-wrap:focus-within .fdy-tooltip{opacity:1;visibility:visible;transform:translateX(-50%) translateY(0);}
|
|
6
|
+
@media (prefers-reduced-motion:reduce){.fdy-tooltip{transition:none;transform:translateX(-50%);}}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/* Freeday — Tree view (hierarchy via nested native <details>: keyboard-toggle, zero-JS).
|
|
2
|
+
* Branches expand/collapse; leaves are plain rows. Chevron + optional folder/file icons. */
|
|
3
|
+
.fdy-tree{list-style:none;margin:0;padding:0;font-size:var(--text-sm);}
|
|
4
|
+
.fdy-tree ul{list-style:none;margin:0;padding-left:var(--space-4);}
|
|
5
|
+
.fdy-tree__branch > summary,.fdy-tree__leaf{display:flex;align-items:center;gap:var(--space-2);padding:var(--space-1) var(--space-2);border-radius:var(--radius-sm);color:var(--color-text);}
|
|
6
|
+
.fdy-tree__branch > summary{list-style:none;cursor:pointer;}
|
|
7
|
+
.fdy-tree__branch > summary::-webkit-details-marker{display:none;}
|
|
8
|
+
.fdy-tree__branch > summary:hover,.fdy-tree__leaf:hover{background:var(--color-surface-2);}
|
|
9
|
+
.fdy-tree__branch > summary:focus-visible{outline:none;box-shadow:inset 0 0 0 2px color-mix(in srgb,var(--color-primary) 45%,transparent);}
|
|
10
|
+
.fdy-tree__chevron{flex:none;width:.9rem;height:.9rem;color:var(--color-text-subtle);transition:transform var(--dur-fast) var(--ease-standard);}
|
|
11
|
+
.fdy-tree__branch[open] > summary .fdy-tree__chevron{transform:rotate(90deg);}
|
|
12
|
+
.fdy-tree__icon{flex:none;width:1rem;height:1rem;color:var(--color-text-muted);}
|
|
13
|
+
.fdy-tree__leaf{padding-left:calc(var(--space-2) * 2 + .9rem);}
|
|
14
|
+
.fdy-tree__leaf .fdy-tree__icon{color:var(--color-text-subtle);}
|
|
15
|
+
|
|
16
|
+
/* Checkbox variant — a selectable tree (cascading parent/child via freeday-tree.js).
|
|
17
|
+
* A native <input class="fdy-checkbox"> sits at the start of every row; a partially
|
|
18
|
+
* selected branch shows the native :indeterminate dash. The checkbox stops click
|
|
19
|
+
* propagation, so toggling selection never expands/collapses the branch. */
|
|
20
|
+
.fdy-tree--checkbox .fdy-tree__check{flex:none;}
|
|
21
|
+
.fdy-tree--checkbox .fdy-tree__leaf{padding-left:calc(var(--space-2) + .9rem + var(--space-2));}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/* Freeday — autocomplete enhancer (optional, zero-dependency).
|
|
2
|
+
* Editable combobox (WAI-ARIA APG): a text input filters a role="listbox" of role="option"
|
|
3
|
+
* items as you type; select fills the input. Auto-inits [data-fdy-autocomplete]. Options are
|
|
4
|
+
* static markup filtered client-side — in an app, fetch/filter server-side and re-render options.
|
|
5
|
+
*
|
|
6
|
+
* Markup: <div data-fdy-autocomplete class="fdy-autocomplete">
|
|
7
|
+
* <input class="fdy-input" role="combobox" aria-expanded="false" aria-autocomplete="list"
|
|
8
|
+
* aria-controls="listId" autocomplete="off">
|
|
9
|
+
* <ul class="fdy-autocomplete__listbox" id="listId" role="listbox" hidden>
|
|
10
|
+
* <li class="fdy-autocomplete__option" role="option">…</li>…
|
|
11
|
+
* <li class="fdy-autocomplete__empty" hidden>Tak ada hasil</li></ul></div>
|
|
12
|
+
* Emits a bubbling "fdy-autocomplete-select" CustomEvent (detail {value, option}).
|
|
13
|
+
*/
|
|
14
|
+
(function () {
|
|
15
|
+
'use strict';
|
|
16
|
+
|
|
17
|
+
var seq = 0;
|
|
18
|
+
|
|
19
|
+
function initAutocomplete(root) {
|
|
20
|
+
if (root.dataset.fdyAcReady === '1') return;
|
|
21
|
+
root.dataset.fdyAcReady = '1';
|
|
22
|
+
|
|
23
|
+
var input = root.querySelector('input[role="combobox"]') || root.querySelector('input');
|
|
24
|
+
var listbox = root.querySelector('[role="listbox"]');
|
|
25
|
+
if (!input || !listbox) return;
|
|
26
|
+
var options = Array.prototype.slice.call(listbox.querySelectorAll('[role="option"]'));
|
|
27
|
+
var emptyEl = listbox.querySelector('.fdy-autocomplete__empty');
|
|
28
|
+
var active = -1;
|
|
29
|
+
|
|
30
|
+
options.forEach(function (o) {
|
|
31
|
+
if (!o.id) { seq += 1; o.id = 'fdy-ac-opt-' + seq; }
|
|
32
|
+
o.dataset.label = o.textContent.trim();
|
|
33
|
+
});
|
|
34
|
+
listbox.hidden = true;
|
|
35
|
+
input.setAttribute('aria-expanded', 'false');
|
|
36
|
+
|
|
37
|
+
function visible() { return options.filter(function (o) { return !o.hidden; }); }
|
|
38
|
+
function clearHighlight() { options.forEach(function (o) { o.classList.remove('is-highlighted'); }); }
|
|
39
|
+
|
|
40
|
+
var _pop = null;
|
|
41
|
+
function popCtl() { if (_pop === null && window.FreedayPopover) _pop = window.FreedayPopover.attach(listbox, input); return _pop; }
|
|
42
|
+
function open() {
|
|
43
|
+
if (!listbox.hidden) return;
|
|
44
|
+
var p = popCtl(); if (p) p.show(); else listbox.hidden = false;
|
|
45
|
+
input.setAttribute('aria-expanded', 'true');
|
|
46
|
+
document.addEventListener('click', onDocClick, true);
|
|
47
|
+
}
|
|
48
|
+
function close() {
|
|
49
|
+
if (listbox.hidden) return;
|
|
50
|
+
var p = popCtl(); if (p) p.hide(); else listbox.hidden = true;
|
|
51
|
+
input.setAttribute('aria-expanded', 'false');
|
|
52
|
+
input.removeAttribute('aria-activedescendant');
|
|
53
|
+
active = -1;
|
|
54
|
+
clearHighlight();
|
|
55
|
+
document.removeEventListener('click', onDocClick, true);
|
|
56
|
+
}
|
|
57
|
+
function onDocClick(e) { if (!root.contains(e.target)) close(); }
|
|
58
|
+
|
|
59
|
+
function highlight(i) {
|
|
60
|
+
var vis = visible();
|
|
61
|
+
clearHighlight();
|
|
62
|
+
if (!vis.length || i < 0 || i >= vis.length) { active = -1; input.removeAttribute('aria-activedescendant'); return; }
|
|
63
|
+
active = i;
|
|
64
|
+
vis[i].classList.add('is-highlighted');
|
|
65
|
+
input.setAttribute('aria-activedescendant', vis[i].id);
|
|
66
|
+
vis[i].scrollIntoView({ block: 'nearest' });
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function applyFilter() {
|
|
70
|
+
var q = input.value.trim().toLowerCase();
|
|
71
|
+
var shown = 0;
|
|
72
|
+
options.forEach(function (o) {
|
|
73
|
+
var match = !q || o.dataset.label.toLowerCase().indexOf(q) !== -1;
|
|
74
|
+
o.hidden = !match;
|
|
75
|
+
if (match) shown++;
|
|
76
|
+
});
|
|
77
|
+
if (emptyEl) emptyEl.hidden = shown !== 0;
|
|
78
|
+
active = -1;
|
|
79
|
+
input.removeAttribute('aria-activedescendant');
|
|
80
|
+
clearHighlight();
|
|
81
|
+
open();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function choose(opt) {
|
|
85
|
+
input.value = opt.dataset.label;
|
|
86
|
+
options.forEach(function (o) { o.setAttribute('aria-selected', o === opt ? 'true' : 'false'); });
|
|
87
|
+
close();
|
|
88
|
+
root.dispatchEvent(new CustomEvent('fdy-autocomplete-select', { bubbles: true, detail: { value: opt.dataset.label, option: opt } }));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
input.addEventListener('input', applyFilter);
|
|
92
|
+
input.addEventListener('focus', function () { applyFilter(); });
|
|
93
|
+
input.addEventListener('keydown', function (e) {
|
|
94
|
+
var vis = visible();
|
|
95
|
+
switch (e.key) {
|
|
96
|
+
case 'ArrowDown':
|
|
97
|
+
e.preventDefault();
|
|
98
|
+
if (listbox.hidden) open();
|
|
99
|
+
highlight(active + 1 < vis.length ? active + 1 : 0);
|
|
100
|
+
break;
|
|
101
|
+
case 'ArrowUp':
|
|
102
|
+
e.preventDefault();
|
|
103
|
+
if (listbox.hidden) open();
|
|
104
|
+
highlight(active - 1 >= 0 ? active - 1 : vis.length - 1);
|
|
105
|
+
break;
|
|
106
|
+
case 'Enter':
|
|
107
|
+
if (!listbox.hidden && active >= 0) { e.preventDefault(); choose(vis[active]); }
|
|
108
|
+
break;
|
|
109
|
+
case 'Escape':
|
|
110
|
+
if (!listbox.hidden) { e.preventDefault(); close(); }
|
|
111
|
+
break;
|
|
112
|
+
case 'Tab':
|
|
113
|
+
close();
|
|
114
|
+
break;
|
|
115
|
+
default: break;
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
listbox.addEventListener('click', function (e) {
|
|
119
|
+
var opt = e.target.closest('[role="option"]');
|
|
120
|
+
if (opt && !opt.hidden) choose(opt);
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function initAll(context) {
|
|
125
|
+
Array.prototype.forEach.call((context || document).querySelectorAll('[data-fdy-autocomplete]'), initAutocomplete);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (document.readyState === 'loading') {
|
|
129
|
+
document.addEventListener('DOMContentLoaded', function () { initAll(); });
|
|
130
|
+
} else {
|
|
131
|
+
initAll();
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
window.FreedayAutocomplete = { init: initAutocomplete, initAll: initAll };
|
|
135
|
+
})();
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/* Freeday — breakpoint provider (optional, zero-dependency).
|
|
2
|
+
* The MudBreakpointProvider equivalent for JS consumers: stamps the current breakpoint on
|
|
3
|
+
* <html data-breakpoint="xs|sm|md|lg|xl"> and emits a bubbling "fdy-breakpoint-change"
|
|
4
|
+
* CustomEvent (detail {breakpoint}) whenever it changes. Also fills any element with
|
|
5
|
+
* [data-fdy-breakpoint-label] with the current name. Read it live via FreedayBreakpoint.get().
|
|
6
|
+
* Scale (min-width, px): sm 600 · md 960 · lg 1280 · xl 1920 (matches breakpoints.css).
|
|
7
|
+
*/
|
|
8
|
+
(function () {
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
var STEPS = [['xl', 1920], ['lg', 1280], ['md', 960], ['sm', 600], ['xs', 0]];
|
|
12
|
+
|
|
13
|
+
function current() {
|
|
14
|
+
var w = window.innerWidth;
|
|
15
|
+
for (var i = 0; i < STEPS.length; i++) {
|
|
16
|
+
if (w >= STEPS[i][1]) return STEPS[i][0];
|
|
17
|
+
}
|
|
18
|
+
return 'xs';
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
var root = document.documentElement;
|
|
22
|
+
var last = null;
|
|
23
|
+
|
|
24
|
+
function update() {
|
|
25
|
+
var bp = current();
|
|
26
|
+
if (bp === last) return;
|
|
27
|
+
last = bp;
|
|
28
|
+
root.setAttribute('data-breakpoint', bp);
|
|
29
|
+
Array.prototype.forEach.call(document.querySelectorAll('[data-fdy-breakpoint-label]'), function (el) {
|
|
30
|
+
el.textContent = bp;
|
|
31
|
+
});
|
|
32
|
+
root.dispatchEvent(new CustomEvent('fdy-breakpoint-change', { bubbles: true, detail: { breakpoint: bp } }));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
window.addEventListener('resize', update, { passive: true });
|
|
36
|
+
if (document.readyState === 'loading') {
|
|
37
|
+
document.addEventListener('DOMContentLoaded', update);
|
|
38
|
+
} else {
|
|
39
|
+
update();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
window.FreedayBreakpoint = {
|
|
43
|
+
get: function () { return last || current(); },
|
|
44
|
+
current: current,
|
|
45
|
+
// true if the current breakpoint is `name` or wider (e.g. is('md') → md, lg, or xl)
|
|
46
|
+
isUp: function (name) {
|
|
47
|
+
var order = ['xs', 'sm', 'md', 'lg', 'xl'];
|
|
48
|
+
return order.indexOf(this.get()) >= order.indexOf(name);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
})();
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/* Freeday — carousel enhancer (optional, zero-dependency).
|
|
2
|
+
* Scroll-snap slider with prev/next arrows, generated dot indicators, keyboard (←/→),
|
|
3
|
+
* and optional autoplay (data-fdy-autoplay="4000", pauses on hover/focus). Auto-inits
|
|
4
|
+
* [data-fdy-carousel].
|
|
5
|
+
*
|
|
6
|
+
* Markup: <div class="fdy-carousel" data-fdy-carousel role="region"
|
|
7
|
+
* aria-roledescription="carousel" aria-label="…" tabindex="0">
|
|
8
|
+
* <button class="fdy-carousel__arrow fdy-carousel__arrow--prev" aria-label="Sebelumnya">…</button>
|
|
9
|
+
* <div class="fdy-carousel__viewport">
|
|
10
|
+
* <div class="fdy-carousel__slide" role="group" aria-roledescription="slide">…</div>…
|
|
11
|
+
* </div>
|
|
12
|
+
* <button class="fdy-carousel__arrow fdy-carousel__arrow--next" aria-label="Berikutnya">…</button>
|
|
13
|
+
* <div class="fdy-carousel__dots"></div> <!-- dots generated here -->
|
|
14
|
+
* </div>
|
|
15
|
+
* Emits a bubbling "fdy-carousel-change" CustomEvent (detail {index}).
|
|
16
|
+
*/
|
|
17
|
+
(function () {
|
|
18
|
+
'use strict';
|
|
19
|
+
|
|
20
|
+
function initCarousel(root) {
|
|
21
|
+
if (root.dataset.fdyCarouselReady === '1') return;
|
|
22
|
+
root.dataset.fdyCarouselReady = '1';
|
|
23
|
+
|
|
24
|
+
var viewport = root.querySelector('.fdy-carousel__viewport');
|
|
25
|
+
var slides = Array.prototype.slice.call(root.querySelectorAll('.fdy-carousel__slide'));
|
|
26
|
+
if (!viewport || !slides.length) return;
|
|
27
|
+
var prev = root.querySelector('.fdy-carousel__arrow--prev');
|
|
28
|
+
var next = root.querySelector('.fdy-carousel__arrow--next');
|
|
29
|
+
var dotsWrap = root.querySelector('.fdy-carousel__dots');
|
|
30
|
+
var index = 0;
|
|
31
|
+
|
|
32
|
+
slides.forEach(function (s, i) {
|
|
33
|
+
if (!s.hasAttribute('aria-label')) s.setAttribute('aria-label', (i + 1) + ' dari ' + slides.length);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
var dots = [];
|
|
37
|
+
if (dotsWrap) {
|
|
38
|
+
slides.forEach(function (s, i) {
|
|
39
|
+
var b = document.createElement('button');
|
|
40
|
+
b.type = 'button';
|
|
41
|
+
b.className = 'fdy-carousel__dot';
|
|
42
|
+
b.setAttribute('aria-label', 'Slide ' + (i + 1));
|
|
43
|
+
b.addEventListener('click', function () { goTo(i); });
|
|
44
|
+
dotsWrap.appendChild(b);
|
|
45
|
+
dots.push(b);
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function clamp(i) { return Math.max(0, Math.min(slides.length - 1, i)); }
|
|
50
|
+
|
|
51
|
+
function sync() {
|
|
52
|
+
dots.forEach(function (d, i) { d.setAttribute('aria-current', i === index ? 'true' : 'false'); });
|
|
53
|
+
if (prev) prev.disabled = index === 0;
|
|
54
|
+
if (next) next.disabled = index === slides.length - 1;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function goTo(i) {
|
|
58
|
+
index = clamp(i);
|
|
59
|
+
viewport.scrollTo({ left: index * viewport.clientWidth, behavior: 'smooth' });
|
|
60
|
+
sync();
|
|
61
|
+
root.dispatchEvent(new CustomEvent('fdy-carousel-change', { bubbles: true, detail: { index: index } }));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (prev) prev.addEventListener('click', function () { goTo(index - 1); });
|
|
65
|
+
if (next) next.addEventListener('click', function () { goTo(index + 1); });
|
|
66
|
+
root.addEventListener('keydown', function (e) {
|
|
67
|
+
if (e.key === 'ArrowLeft') { e.preventDefault(); goTo(index - 1); }
|
|
68
|
+
else if (e.key === 'ArrowRight') { e.preventDefault(); goTo(index + 1); }
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
// Keep index in sync when the user swipes/scrolls the viewport directly.
|
|
72
|
+
var ticking = false;
|
|
73
|
+
viewport.addEventListener('scroll', function () {
|
|
74
|
+
if (ticking) return;
|
|
75
|
+
ticking = true;
|
|
76
|
+
requestAnimationFrame(function () {
|
|
77
|
+
ticking = false;
|
|
78
|
+
var w = viewport.clientWidth || 1;
|
|
79
|
+
var i = clamp(Math.round(viewport.scrollLeft / w));
|
|
80
|
+
if (i !== index) { index = i; sync(); }
|
|
81
|
+
});
|
|
82
|
+
}, { passive: true });
|
|
83
|
+
|
|
84
|
+
var autoplay = parseInt(root.getAttribute('data-fdy-autoplay'), 10);
|
|
85
|
+
if (autoplay > 0) {
|
|
86
|
+
var timer = null;
|
|
87
|
+
var start = function () { if (!timer) timer = setInterval(function () { goTo(index >= slides.length - 1 ? 0 : index + 1); }, autoplay); };
|
|
88
|
+
var stop = function () { if (timer) { clearInterval(timer); timer = null; } };
|
|
89
|
+
root.addEventListener('mouseenter', stop);
|
|
90
|
+
root.addEventListener('mouseleave', start);
|
|
91
|
+
root.addEventListener('focusin', stop);
|
|
92
|
+
root.addEventListener('focusout', start);
|
|
93
|
+
var reduce = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
94
|
+
if (!reduce) start();
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
sync();
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function initAll(context) {
|
|
101
|
+
Array.prototype.forEach.call((context || document).querySelectorAll('[data-fdy-carousel]'), initCarousel);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (document.readyState === 'loading') {
|
|
105
|
+
document.addEventListener('DOMContentLoaded', function () { initAll(); });
|
|
106
|
+
} else {
|
|
107
|
+
initAll();
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
window.FreedayCarousel = { init: initCarousel, initAll: initAll };
|
|
111
|
+
})();
|