@cahyo-dimas/freeday 3.6.0 → 3.8.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.
Files changed (52) hide show
  1. package/CHANGELOG.md +139 -0
  2. package/COMPONENTS.md +119 -6
  3. package/README.id.md +19 -6
  4. package/README.md +17 -5
  5. package/USAGE.md +42 -0
  6. package/adapters/blazor/FdyDatepicker.razor +3 -1
  7. package/adapters/blazor/FdyDatepicker.razor.cs +22 -1
  8. package/adapters/blazor/FdyTableFooter.razor +6 -6
  9. package/adapters/blazor/FdyTableFooter.razor.cs +36 -0
  10. package/adapters/react/components/FdyDatepicker.tsx +122 -42
  11. package/adapters/react/components/FdyTableFooter.tsx +37 -6
  12. package/adapters/react/index.d.ts +1 -1
  13. package/adapters/vue/components/FdyDatepicker.vue +119 -34
  14. package/adapters/vue/components/FdyTableFooter.vue +39 -6
  15. package/dist/freeday-datepicker.js +68 -14
  16. package/dist/freeday-menu.js +15 -1
  17. package/dist/freeday-popover.js +33 -11
  18. package/dist/freeday-tabs.js +21 -2
  19. package/dist/freeday.bundle.css +493 -117
  20. package/dist/freeday.css +190 -113
  21. package/dist/freeday.js +137 -28
  22. package/dist/freeday.tokens.css +303 -4
  23. package/docs/getting-started.md +1 -1
  24. package/package.json +2 -2
  25. package/src/base.css +1 -1
  26. package/src/components/accordion.css +1 -1
  27. package/src/components/alert.css +5 -5
  28. package/src/components/app-shell.css +2 -2
  29. package/src/components/appbar.css +3 -3
  30. package/src/components/avatar.css +15 -9
  31. package/src/components/badge.css +18 -14
  32. package/src/components/breadcrumb.css +1 -1
  33. package/src/components/button.css +21 -13
  34. package/src/components/carousel.css +3 -3
  35. package/src/components/cascade.css +4 -4
  36. package/src/components/chip.css +33 -15
  37. package/src/components/combo.css +5 -5
  38. package/src/components/datepicker.css +8 -8
  39. package/src/components/file-upload.css +9 -4
  40. package/src/components/input-group.css +2 -2
  41. package/src/components/input.css +2 -2
  42. package/src/components/menu.css +3 -3
  43. package/src/components/pagination.css +1 -1
  44. package/src/components/selection.css +2 -2
  45. package/src/components/skeleton.css +1 -1
  46. package/src/components/stepper.css +1 -1
  47. package/src/components/table.css +12 -7
  48. package/src/components/tabs.css +32 -1
  49. package/src/components/timepicker.css +3 -3
  50. package/src/components/toast.css +1 -1
  51. package/src/components/tree.css +1 -1
  52. package/tokens/tokens.json +57 -4
@@ -8,7 +8,8 @@
8
8
  * Used by the select/combo, datepicker, timepicker, cascade, autocomplete and menu enhancers;
9
9
  * the freeday/vue adapter ships an equivalent `usePopover` composable.
10
10
  *
11
- * attach(panel, trigger) -> { show(), hide() }. show()/hide() also toggle the panel's [hidden]
11
+ * attach(panel, trigger, opts) -> { show(), hide() }, where opts is { matchWidth, align } and
12
+ * both are described on place() below. show()/hide() also toggle the panel's [hidden]
12
13
  * attribute so callers keep using `!panel.hidden` for open-state and the `:not([hidden])`
13
14
  * entrance animation still fires. On browsers without the Popover API it degrades to that
14
15
  * [hidden] toggle over the panel's existing absolute-positioned CSS (clipped, but functional).
@@ -21,35 +22,56 @@
21
22
  && typeof HTMLElement.prototype.showPopover === 'function';
22
23
 
23
24
  // Position `panel` (already shown, so it has a measurable box) against `trigger`'s rect.
24
- // matchWidth (default true) floors the panel to the trigger width, right for input-shaped
25
- // dropdowns (combo/cascade/date/time), skipped for menus that keep their own min-width.
26
- function place(panel, trigger, matchWidth) {
27
- var r = trigger.getBoundingClientRect();
25
+ // opts is the object attach() takes, and both keys are optional:
26
+ // matchWidth (default true) floors the panel to the trigger width, right for input-shaped
27
+ // dropdowns (combo/cascade/date/time), skipped for menus that keep their own min-width.
28
+ // align 'start' (default) lines the panel's LEFT edge up with the trigger's left edge;
29
+ // 'end' lines its RIGHT edge up with the trigger's right edge, which is what a
30
+ // control parked in a right-hand gutter needs. The panel is kept inside the
31
+ // viewport either way, so 'end' near the left edge slides right instead of
32
+ // hanging off it. freeday-menu.js reads it off `.fdy-menu--end`.
33
+ function place(panel, trigger, opts) {
34
+ var o = opts || {};
35
+ /* Take the panel out of the flow BEFORE measuring anything, because until this runs the panel
36
+ * is still `position:absolute` with `top:calc(100% + …)` from its component's stylesheet. An
37
+ * author rule outranks the popover UA style, and in the top layer that `100%` resolves against
38
+ * the viewport, so a panel that has just been shown sits one viewport height down the page and
39
+ * drags the document's scrollable overflow with it. Where scrollbars take layout space (every
40
+ * Windows and Linux browser; macOS overlay scrollbars hide it completely) the document gains a
41
+ * classic scrollbar for the duration, documentElement.clientWidth drops by its width, and the
42
+ * page relays out 15px narrower. A trigger rect read in that instant is a rect from a layout
43
+ * that is about to be undone by the next three lines: measured 961 instead of 976 for an avatar
44
+ * in a 24px gutter, so the menu landed a scrollbar's width to the left of it. */
28
45
  panel.style.position = 'fixed';
29
46
  panel.style.margin = '0';
30
47
  panel.style.inset = 'auto'; // clear the UA popover centering
31
- if (matchWidth !== false) panel.style.minWidth = r.width + 'px'; // at least as wide as the trigger
48
+ var r = trigger.getBoundingClientRect();
49
+ if (o.matchWidth !== false) panel.style.minWidth = r.width + 'px'; // at least as wide as the trigger
32
50
  var ph = panel.offsetHeight, pw = panel.offsetWidth;
33
51
  var vw = document.documentElement.clientWidth, vh = document.documentElement.clientHeight;
34
52
  var below = vh - r.bottom - GAP, above = r.top - GAP;
35
53
  // Prefer below; flip above only when it doesn't fit below and above has more room.
36
54
  var top = (ph <= below || below >= above) ? (r.bottom + GAP) : Math.max(GAP, r.top - GAP - ph);
37
- var left = r.left;
38
- if (left + pw > vw - GAP) left = Math.max(GAP, vw - GAP - pw); // keep within the viewport
55
+ var left = (o.align === 'end') ? (r.right - pw) : r.left;
56
+ /* Clamped on BOTH sides, and that is not belt-and-braces: the first line is what keeps a
57
+ start-aligned panel on screen, the second is what keeps an end-aligned one on screen when
58
+ the panel is wider than the room to the left of its trigger. Keeping only one of them
59
+ trades an overflow on one edge for an overflow on the other. */
60
+ if (left + pw > vw - GAP) left = vw - GAP - pw;
61
+ if (left < GAP) left = GAP;
39
62
  panel.style.top = Math.round(top) + 'px';
40
63
  panel.style.left = Math.round(left) + 'px';
41
64
  }
42
65
 
43
66
  function attach(panel, trigger, opts) {
44
- var matchWidth = !(opts && opts.matchWidth === false);
45
67
  if (supported) panel.setAttribute('popover', 'manual');
46
- function reposition() { if (supported && panel.matches(':popover-open')) place(panel, trigger, matchWidth); }
68
+ function reposition() { if (supported && panel.matches(':popover-open')) place(panel, trigger, opts); }
47
69
  return {
48
70
  show: function () {
49
71
  panel.hidden = false;
50
72
  if (supported) {
51
73
  if (!panel.matches(':popover-open')) panel.showPopover();
52
- place(panel, trigger, matchWidth);
74
+ place(panel, trigger, opts);
53
75
  window.addEventListener('scroll', reposition, true); // capture: any scrolling ancestor
54
76
  window.addEventListener('resize', reposition);
55
77
  }
@@ -4,6 +4,12 @@
4
4
  * Markup: a [data-fdy-tabs] with a [role="tablist"] of
5
5
  * <button role="tab" aria-controls="PANEL" aria-selected> and matching
6
6
  * <div role="tabpanel" id="PANEL" aria-labelledby="TAB" [hidden]>.
7
+ *
8
+ * The arrow AXIS comes from the tablist's `aria-orientation`: `vertical` answers to Up and
9
+ * Down, anything else to Left and Right. One axis per tablist, which is what APG maps, and the
10
+ * other pair is left entirely alone rather than bound as a second axis: Left and Right over a
11
+ * column of tabs belong to whatever else is on the page, and a widget that preventDefaults a key
12
+ * it has no meaning for has taken it from the page silently. Home/End answer on both.
7
13
  */
8
14
  (function () {
9
15
  'use strict';
@@ -14,6 +20,19 @@
14
20
  if (tabs.length === 0) return;
15
21
  root.dataset.fdyTabsReady = '1';
16
22
 
23
+ /* The look follows `.fdy-tabs--vertical`, the behaviour follows `aria-orientation`, and this
24
+ makes the second agree with the first so an app states the axis once. The attribute always
25
+ wins when it is there: it is what an assistive technology announces, so nothing here may
26
+ overwrite one an author wrote. */
27
+ var list = root.querySelector('[role="tablist"]');
28
+ if (list !== null && list.getAttribute('aria-orientation') === null
29
+ && root.classList.contains('fdy-tabs--vertical')) {
30
+ list.setAttribute('aria-orientation', 'vertical');
31
+ }
32
+ var vertical = list !== null && list.getAttribute('aria-orientation') === 'vertical';
33
+ var NEXT_KEY = vertical ? 'ArrowDown' : 'ArrowRight';
34
+ var PREV_KEY = vertical ? 'ArrowUp' : 'ArrowLeft';
35
+
17
36
  function panelFor(tab) {
18
37
  var id = tab.getAttribute('aria-controls');
19
38
  return id ? document.getElementById(id) : null;
@@ -33,9 +52,9 @@
33
52
  tab.addEventListener('click', function () { select(tab, false); });
34
53
  tab.addEventListener('keydown', function (e) {
35
54
  switch (e.key) {
36
- case 'ArrowRight':
55
+ case NEXT_KEY:
37
56
  e.preventDefault(); select(tabs[(i + 1) % tabs.length], true); break;
38
- case 'ArrowLeft':
57
+ case PREV_KEY:
39
58
  e.preventDefault(); select(tabs[(i - 1 + tabs.length) % tabs.length], true); break;
40
59
  case 'Home':
41
60
  e.preventDefault(); select(tabs[0], true); break;