@onyxdesign/core 1.0.0 → 1.1.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/package.json +1 -1
- package/src/onyx.css +244 -2
- package/src/onyx.js +127 -6
package/package.json
CHANGED
package/src/onyx.css
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* Onyx v1.
|
|
2
|
+
* Onyx v1.1.0 — a black & white component library for the web
|
|
3
3
|
* Core: MIT License. Onyx Pro components require a commercial license.
|
|
4
4
|
*/
|
|
5
5
|
|
|
@@ -847,8 +847,84 @@
|
|
|
847
847
|
transition: color var(--ox-duration) var(--ox-ease), background var(--ox-duration) var(--ox-ease);
|
|
848
848
|
}
|
|
849
849
|
.ox-navbar-links a:hover { color: var(--ox-fg); background: var(--ox-bg-muted); }
|
|
850
|
+
|
|
851
|
+
/* Mobile menu: links collapse behind a toggle below 760px
|
|
852
|
+
(or below 1024px with data-collapse="lg" on .ox-navbar) */
|
|
853
|
+
.ox-navbar-toggle { display: none; }
|
|
854
|
+
.ox-navbar-menu { display: none; }
|
|
855
|
+
.ox-navbar-toggle {
|
|
856
|
+
position: relative;
|
|
857
|
+
width: 2.5rem;
|
|
858
|
+
height: 2.5rem;
|
|
859
|
+
padding: 0;
|
|
860
|
+
color: var(--ox-fg);
|
|
861
|
+
background: transparent;
|
|
862
|
+
border: 1px solid var(--ox-border);
|
|
863
|
+
border-radius: var(--ox-radius-full);
|
|
864
|
+
cursor: pointer;
|
|
865
|
+
}
|
|
866
|
+
.ox-navbar-toggle span {
|
|
867
|
+
position: absolute;
|
|
868
|
+
left: 50%;
|
|
869
|
+
top: 50%;
|
|
870
|
+
width: 1rem;
|
|
871
|
+
height: 1.5px;
|
|
872
|
+
margin: -0.75px 0 0 -0.5rem;
|
|
873
|
+
background: currentColor;
|
|
874
|
+
border-radius: 2px;
|
|
875
|
+
transition: transform 260ms var(--ox-ease);
|
|
876
|
+
}
|
|
877
|
+
.ox-navbar-toggle span:first-child { transform: translateY(-3.5px); }
|
|
878
|
+
.ox-navbar-toggle span:last-child { transform: translateY(3.5px); }
|
|
879
|
+
.ox-navbar[data-open="true"] .ox-navbar-toggle span:first-child { transform: rotate(45deg); }
|
|
880
|
+
.ox-navbar[data-open="true"] .ox-navbar-toggle span:last-child { transform: rotate(-45deg); }
|
|
881
|
+
/* Absolute, not fixed: the navbar's backdrop-filter makes it the containing block
|
|
882
|
+
for fixed children, so the menu is sized from the navbar's bottom edge instead */
|
|
883
|
+
.ox-navbar-menu {
|
|
884
|
+
position: absolute;
|
|
885
|
+
top: 100%;
|
|
886
|
+
left: 0;
|
|
887
|
+
right: 0;
|
|
888
|
+
height: calc(100vh - 3.75rem);
|
|
889
|
+
height: calc(100dvh - 3.75rem);
|
|
890
|
+
z-index: 99;
|
|
891
|
+
flex-direction: column;
|
|
892
|
+
gap: var(--ox-space-1);
|
|
893
|
+
padding: var(--ox-space-4) var(--ox-space-4) var(--ox-space-8);
|
|
894
|
+
overflow-y: auto;
|
|
895
|
+
background: var(--ox-bg);
|
|
896
|
+
border-top: 1px solid var(--ox-border);
|
|
897
|
+
opacity: 0;
|
|
898
|
+
transform: translateY(-8px);
|
|
899
|
+
visibility: hidden;
|
|
900
|
+
transition: opacity 220ms var(--ox-ease), transform 260ms var(--ox-ease), visibility 260ms;
|
|
901
|
+
}
|
|
902
|
+
.ox-navbar[data-open="true"] .ox-navbar-menu { opacity: 1; transform: none; visibility: visible; }
|
|
903
|
+
.ox-navbar-menu > a:not(.ox-btn) {
|
|
904
|
+
display: flex;
|
|
905
|
+
align-items: center;
|
|
906
|
+
justify-content: space-between;
|
|
907
|
+
min-height: 3.25rem;
|
|
908
|
+
padding: 0 var(--ox-space-2);
|
|
909
|
+
font-size: var(--ox-text-lg);
|
|
910
|
+
font-weight: 500;
|
|
911
|
+
letter-spacing: -0.01em;
|
|
912
|
+
color: var(--ox-fg);
|
|
913
|
+
text-decoration: none;
|
|
914
|
+
border-bottom: 1px solid var(--ox-border);
|
|
915
|
+
}
|
|
916
|
+
.ox-navbar-menu > a:not(.ox-btn)::after { content: "→"; color: var(--ox-fg-subtle); }
|
|
917
|
+
.ox-navbar-menu .ox-btn { margin-top: var(--ox-space-4); }
|
|
918
|
+
body.ox-nav-locked { overflow: hidden; }
|
|
850
919
|
@media (max-width: 760px) {
|
|
851
920
|
.ox-navbar-links { display: none; }
|
|
921
|
+
.ox-navbar-toggle { display: inline-grid; }
|
|
922
|
+
.ox-navbar-menu { display: flex; }
|
|
923
|
+
}
|
|
924
|
+
@media (max-width: 1023px) {
|
|
925
|
+
.ox-navbar[data-collapse="lg"] .ox-navbar-links { display: none; }
|
|
926
|
+
.ox-navbar[data-collapse="lg"] .ox-navbar-toggle { display: inline-grid; }
|
|
927
|
+
.ox-navbar[data-collapse="lg"] .ox-navbar-menu { display: flex; }
|
|
852
928
|
}
|
|
853
929
|
|
|
854
930
|
/* =========================================================
|
|
@@ -867,7 +943,169 @@
|
|
|
867
943
|
}
|
|
868
944
|
|
|
869
945
|
/* =========================================================
|
|
870
|
-
19.
|
|
946
|
+
19. Breadcrumb & pagination
|
|
947
|
+
========================================================= */
|
|
948
|
+
.ox-breadcrumb ol { display: flex; flex-wrap: wrap; align-items: center; gap: var(--ox-space-2); margin: 0; padding: 0; list-style: none; font-size: var(--ox-text-sm); }
|
|
949
|
+
.ox-breadcrumb li { display: inline-flex; align-items: center; gap: var(--ox-space-2); color: var(--ox-fg-muted); }
|
|
950
|
+
.ox-breadcrumb li + li::before { content: "/"; color: var(--ox-fg-subtle); }
|
|
951
|
+
.ox-breadcrumb a { color: inherit; text-decoration: none; border-radius: var(--ox-radius-sm); transition: color var(--ox-duration); }
|
|
952
|
+
.ox-breadcrumb a:hover { color: var(--ox-fg); }
|
|
953
|
+
.ox-breadcrumb [aria-current="page"] { color: var(--ox-fg); font-weight: 500; }
|
|
954
|
+
|
|
955
|
+
.ox-pagination ul { display: flex; flex-wrap: wrap; align-items: center; gap: var(--ox-space-1); margin: 0; padding: 0; list-style: none; }
|
|
956
|
+
.ox-pagination a,
|
|
957
|
+
.ox-pagination span {
|
|
958
|
+
display: inline-grid;
|
|
959
|
+
place-items: center;
|
|
960
|
+
min-width: 2.25rem;
|
|
961
|
+
height: 2.25rem;
|
|
962
|
+
padding: 0 var(--ox-space-2);
|
|
963
|
+
font-size: var(--ox-text-sm);
|
|
964
|
+
font-variant-numeric: tabular-nums;
|
|
965
|
+
color: var(--ox-fg-muted);
|
|
966
|
+
text-decoration: none;
|
|
967
|
+
border: 1px solid transparent;
|
|
968
|
+
border-radius: var(--ox-radius);
|
|
969
|
+
transition: background var(--ox-duration), color var(--ox-duration), border-color var(--ox-duration);
|
|
970
|
+
}
|
|
971
|
+
.ox-pagination a:hover { color: var(--ox-fg); background: var(--ox-bg-muted); }
|
|
972
|
+
.ox-pagination [aria-current="page"] { color: var(--ox-inverse); background: var(--ox-fg); border-color: var(--ox-fg); }
|
|
973
|
+
.ox-pagination [aria-disabled="true"] { opacity: 0.35; pointer-events: none; }
|
|
974
|
+
.ox-pagination .ox-gap { min-width: 1.5rem; }
|
|
975
|
+
|
|
976
|
+
/* =========================================================
|
|
977
|
+
20. Range slider & number stepper
|
|
978
|
+
========================================================= */
|
|
979
|
+
.ox-range {
|
|
980
|
+
--_pct: 50%;
|
|
981
|
+
-webkit-appearance: none;
|
|
982
|
+
appearance: none;
|
|
983
|
+
width: 100%;
|
|
984
|
+
height: 1.5rem;
|
|
985
|
+
margin: 0;
|
|
986
|
+
background: transparent;
|
|
987
|
+
cursor: pointer;
|
|
988
|
+
}
|
|
989
|
+
.ox-range::-webkit-slider-runnable-track { height: 4px; border-radius: 99px; background: linear-gradient(to right, var(--ox-fg) var(--_pct), var(--ox-bg-muted) var(--_pct)); }
|
|
990
|
+
.ox-range::-moz-range-track { height: 4px; border-radius: 99px; background: var(--ox-bg-muted); }
|
|
991
|
+
.ox-range::-moz-range-progress { height: 4px; border-radius: 99px; background: var(--ox-fg); }
|
|
992
|
+
.ox-range::-webkit-slider-thumb { -webkit-appearance: none; width: 1.25rem; height: 1.25rem; margin-top: calc(2px - 0.625rem); border-radius: 50%; background: var(--ox-bg); border: 2px solid var(--ox-fg); box-shadow: var(--ox-shadow-sm); transition: transform var(--ox-duration) var(--ox-ease); }
|
|
993
|
+
.ox-range::-moz-range-thumb { width: 1rem; height: 1rem; border-radius: 50%; background: var(--ox-bg); border: 2px solid var(--ox-fg); }
|
|
994
|
+
.ox-range:active::-webkit-slider-thumb { transform: scale(1.15); }
|
|
995
|
+
.ox-range:focus-visible { outline: none; }
|
|
996
|
+
.ox-range:focus-visible::-webkit-slider-thumb { box-shadow: 0 0 0 4px var(--ox-ring); }
|
|
997
|
+
.ox-range:focus-visible::-moz-range-thumb { box-shadow: 0 0 0 4px var(--ox-ring); }
|
|
998
|
+
|
|
999
|
+
.ox-stepper { display: inline-flex; align-items: stretch; height: 2.5rem; background: var(--ox-surface); border: 1px solid var(--ox-border); border-radius: var(--ox-radius); overflow: hidden; }
|
|
1000
|
+
.ox-stepper:focus-within { border-color: var(--ox-fg); box-shadow: 0 0 0 3px var(--ox-ring); }
|
|
1001
|
+
.ox-stepper button { width: 2.5rem; padding: 0; font-size: var(--ox-text-lg); line-height: 1; color: var(--ox-fg); background: transparent; border: 0; cursor: pointer; transition: background var(--ox-duration); }
|
|
1002
|
+
.ox-stepper button:hover { background: var(--ox-bg-muted); }
|
|
1003
|
+
.ox-stepper button:disabled { opacity: 0.3; cursor: not-allowed; background: transparent; }
|
|
1004
|
+
.ox-stepper input { width: 3.5rem; padding: 0; font: inherit; font-size: var(--ox-text-sm); font-variant-numeric: tabular-nums; text-align: center; color: var(--ox-fg); background: transparent; border: 0; border-inline: 1px solid var(--ox-border); outline: none; -moz-appearance: textfield; }
|
|
1005
|
+
.ox-stepper input::-webkit-outer-spin-button,
|
|
1006
|
+
.ox-stepper input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
|
|
1007
|
+
|
|
1008
|
+
/* =========================================================
|
|
1009
|
+
21. Chips (toggleable filters)
|
|
1010
|
+
========================================================= */
|
|
1011
|
+
.ox-chips { display: flex; flex-wrap: wrap; gap: var(--ox-space-2); }
|
|
1012
|
+
.ox-chip {
|
|
1013
|
+
display: inline-flex;
|
|
1014
|
+
align-items: center;
|
|
1015
|
+
gap: 0.4rem;
|
|
1016
|
+
height: 2rem;
|
|
1017
|
+
padding: 0 var(--ox-space-3);
|
|
1018
|
+
font: 500 var(--ox-text-sm) var(--ox-font-sans);
|
|
1019
|
+
color: var(--ox-fg-muted);
|
|
1020
|
+
background: var(--ox-surface);
|
|
1021
|
+
border: 1px solid var(--ox-border);
|
|
1022
|
+
border-radius: var(--ox-radius-full);
|
|
1023
|
+
cursor: pointer;
|
|
1024
|
+
transition: color var(--ox-duration), background var(--ox-duration), border-color var(--ox-duration), transform var(--ox-duration) var(--ox-ease);
|
|
1025
|
+
}
|
|
1026
|
+
.ox-chip:hover { color: var(--ox-fg); border-color: var(--ox-border-strong); }
|
|
1027
|
+
.ox-chip:active { transform: scale(0.97); }
|
|
1028
|
+
.ox-chip[aria-pressed="true"] { color: var(--ox-inverse); background: var(--ox-fg); border-color: var(--ox-fg); }
|
|
1029
|
+
.ox-chip[aria-pressed="true"]::before { content: "✓"; font-size: 0.7rem; }
|
|
1030
|
+
|
|
1031
|
+
/* =========================================================
|
|
1032
|
+
22. Table
|
|
1033
|
+
========================================================= */
|
|
1034
|
+
.ox-table-wrap { overflow-x: auto; border: 1px solid var(--ox-border); border-radius: var(--ox-radius-lg); background: var(--ox-surface); }
|
|
1035
|
+
.ox-table { width: 100%; border-collapse: collapse; font-size: var(--ox-text-sm); font-variant-numeric: tabular-nums; }
|
|
1036
|
+
.ox-table caption { padding: var(--ox-space-3) var(--ox-space-4); text-align: left; color: var(--ox-fg-muted); caption-side: bottom; font-size: var(--ox-text-xs); }
|
|
1037
|
+
.ox-table th { padding: var(--ox-space-3) var(--ox-space-4); text-align: left; font-size: var(--ox-text-xs); font-weight: 500; color: var(--ox-fg-muted); background: var(--ox-bg-subtle); border-bottom: 1px solid var(--ox-border); white-space: nowrap; }
|
|
1038
|
+
.ox-table td { padding: var(--ox-space-3) var(--ox-space-4); border-bottom: 1px solid var(--ox-border); }
|
|
1039
|
+
.ox-table tbody tr:last-child td { border-bottom: 0; }
|
|
1040
|
+
.ox-table tbody tr { transition: background var(--ox-duration); }
|
|
1041
|
+
.ox-table tbody tr:hover { background: var(--ox-bg-subtle); }
|
|
1042
|
+
.ox-table .num { text-align: right; }
|
|
1043
|
+
.ox-table-striped tbody tr:nth-child(even) { background: var(--ox-bg-subtle); }
|
|
1044
|
+
|
|
1045
|
+
/* =========================================================
|
|
1046
|
+
23. Empty state & timeline
|
|
1047
|
+
========================================================= */
|
|
1048
|
+
.ox-empty { display: grid; justify-items: center; gap: var(--ox-space-2); padding: var(--ox-space-12) var(--ox-space-6); text-align: center; border: 1.5px dashed var(--ox-border-strong); border-radius: var(--ox-radius-lg); }
|
|
1049
|
+
.ox-empty-icon { display: grid; place-items: center; width: 3rem; height: 3rem; margin-bottom: var(--ox-space-2); font-size: var(--ox-text-xl); color: var(--ox-fg); background: var(--ox-bg-muted); border-radius: 50%; }
|
|
1050
|
+
.ox-empty-title { margin: 0; font-size: var(--ox-text-base); font-weight: 600; }
|
|
1051
|
+
.ox-empty-text { max-width: 36ch; margin: 0 0 var(--ox-space-3); font-size: var(--ox-text-sm); color: var(--ox-fg-muted); }
|
|
1052
|
+
|
|
1053
|
+
.ox-timeline { margin: 0; padding: 0; list-style: none; }
|
|
1054
|
+
.ox-timeline li { position: relative; padding: 0 0 var(--ox-space-6) var(--ox-space-8); }
|
|
1055
|
+
.ox-timeline li::before { content: ""; position: absolute; left: 0.3rem; top: 0.45rem; width: 0.65rem; height: 0.65rem; border-radius: 50%; background: var(--ox-bg); border: 2px solid var(--ox-fg); }
|
|
1056
|
+
.ox-timeline li::after { content: ""; position: absolute; left: calc(0.3rem + 0.325rem - 0.5px); top: 1.4rem; bottom: 0.2rem; width: 1px; background: var(--ox-border-strong); }
|
|
1057
|
+
.ox-timeline li:last-child { padding-bottom: 0; }
|
|
1058
|
+
.ox-timeline li:last-child::after { display: none; }
|
|
1059
|
+
.ox-timeline li[data-state="done"]::before { background: var(--ox-fg); }
|
|
1060
|
+
.ox-timeline li[data-state="upcoming"]::before { border-color: var(--ox-border-strong); border-style: dashed; }
|
|
1061
|
+
.ox-timeline time { display: block; margin-bottom: 2px; font: 400 var(--ox-text-xs) var(--ox-font-mono); color: var(--ox-fg-subtle); }
|
|
1062
|
+
.ox-timeline b { display: block; font-size: var(--ox-text-sm); font-weight: 500; }
|
|
1063
|
+
.ox-timeline p { margin: 2px 0 0; font-size: var(--ox-text-sm); color: var(--ox-fg-muted); }
|
|
1064
|
+
|
|
1065
|
+
/* =========================================================
|
|
1066
|
+
24. Banner (dismissible announcement)
|
|
1067
|
+
========================================================= */
|
|
1068
|
+
.ox-banner {
|
|
1069
|
+
position: relative;
|
|
1070
|
+
display: flex;
|
|
1071
|
+
flex-wrap: wrap;
|
|
1072
|
+
align-items: center;
|
|
1073
|
+
justify-content: center;
|
|
1074
|
+
gap: var(--ox-space-2) var(--ox-space-4);
|
|
1075
|
+
padding: var(--ox-space-2) 3rem;
|
|
1076
|
+
font-size: var(--ox-text-sm);
|
|
1077
|
+
text-align: center;
|
|
1078
|
+
color: var(--ox-inverse);
|
|
1079
|
+
background: var(--ox-fg);
|
|
1080
|
+
}
|
|
1081
|
+
.ox-banner a { color: inherit; font-weight: 500; text-underline-offset: 3px; }
|
|
1082
|
+
.ox-banner-close { position: absolute; right: var(--ox-space-2); top: 50%; display: grid; place-items: center; width: 2rem; height: 2rem; margin-top: -1rem; padding: 0; color: inherit; background: transparent; border: 0; border-radius: var(--ox-radius-sm); opacity: 0.7; cursor: pointer; }
|
|
1083
|
+
.ox-banner-close:hover { opacity: 1; background: color-mix(in srgb, var(--ox-inverse) 15%, transparent); }
|
|
1084
|
+
.ox-banner[hidden] { display: none; }
|
|
1085
|
+
|
|
1086
|
+
/* =========================================================
|
|
1087
|
+
25. Avatar status
|
|
1088
|
+
========================================================= */
|
|
1089
|
+
.ox-avatar[data-status] { position: relative; overflow: visible; }
|
|
1090
|
+
.ox-avatar[data-status]::after { content: ""; position: absolute; right: -1px; bottom: -1px; width: 28%; height: 28%; min-width: 8px; min-height: 8px; border-radius: 50%; border: 2px solid var(--ox-surface); }
|
|
1091
|
+
.ox-avatar[data-status="online"]::after { background: var(--ox-fg); }
|
|
1092
|
+
.ox-avatar[data-status="away"]::after { background: var(--ox-bg); box-shadow: inset 0 0 0 2px var(--ox-fg); }
|
|
1093
|
+
.ox-avatar[data-status="offline"]::after { background: var(--ox-border-strong); }
|
|
1094
|
+
|
|
1095
|
+
/* =========================================================
|
|
1096
|
+
26. Touch screens: comfortable tap targets
|
|
1097
|
+
========================================================= */
|
|
1098
|
+
@media (pointer: coarse) {
|
|
1099
|
+
.ox-btn-sm, .ox-chip, .ox-pagination a, .ox-pagination span { min-height: 2.5rem; }
|
|
1100
|
+
.ox-btn-sm.ox-btn-icon { width: 2.5rem; }
|
|
1101
|
+
.ox-segmented button { height: 2.25rem; }
|
|
1102
|
+
.ox-tab { padding-block: var(--ox-space-3); }
|
|
1103
|
+
.ox-dropdown-item { min-height: 2.75rem; }
|
|
1104
|
+
.ox-checkbox, .ox-radio, .ox-switch { min-height: 2.25rem; }
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
/* =========================================================
|
|
1108
|
+
27. Keyframes & motion preferences
|
|
871
1109
|
========================================================= */
|
|
872
1110
|
@keyframes ox-spin { to { transform: rotate(360deg); } }
|
|
873
1111
|
@keyframes ox-shimmer { from { background-position: 100% 0; } to { background-position: -100% 0; } }
|
|
@@ -884,3 +1122,7 @@
|
|
|
884
1122
|
transition-duration: 1ms !important;
|
|
885
1123
|
}
|
|
886
1124
|
}
|
|
1125
|
+
|
|
1126
|
+
/* Theme switch: the new theme is revealed by a clip-path circle (see Onyx.theme.set) */
|
|
1127
|
+
::view-transition-old(root),
|
|
1128
|
+
::view-transition-new(root) { animation: none; mix-blend-mode: normal; }
|
package/src/onyx.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* Onyx v1.
|
|
2
|
+
* Onyx v1.1.0 — interactive behaviors
|
|
3
3
|
* Zero dependencies. Works via data attributes, or the window.Onyx API.
|
|
4
4
|
*/
|
|
5
5
|
(function (global) {
|
|
@@ -107,11 +107,25 @@
|
|
|
107
107
|
return global.matchMedia && matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
-
function
|
|
110
|
+
function applyTheme(theme) {
|
|
111
111
|
document.documentElement.setAttribute("data-theme", theme);
|
|
112
112
|
try { localStorage.setItem(THEME_KEY, theme); } catch (e) { /* storage unavailable */ }
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
+
// With a point ({ x, y }), the new theme spreads out from it like ink, where the browser supports it
|
|
116
|
+
function setTheme(theme, from) {
|
|
117
|
+
var reduce = global.matchMedia && matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
118
|
+
if (!from || reduce || !document.startViewTransition) { applyTheme(theme); return; }
|
|
119
|
+
var r = Math.hypot(Math.max(from.x, innerWidth - from.x), Math.max(from.y, innerHeight - from.y));
|
|
120
|
+
var vt = document.startViewTransition(function () { applyTheme(theme); });
|
|
121
|
+
vt.ready.then(function () {
|
|
122
|
+
document.documentElement.animate(
|
|
123
|
+
{ clipPath: ["circle(0px at " + from.x + "px " + from.y + "px)", "circle(" + r + "px at " + from.x + "px " + from.y + "px)"] },
|
|
124
|
+
{ duration: 520, easing: "cubic-bezier(0.2, 0.8, 0.2, 1)", pseudoElement: "::view-transition-new(root)" }
|
|
125
|
+
);
|
|
126
|
+
}).catch(function () {});
|
|
127
|
+
}
|
|
128
|
+
|
|
115
129
|
function restoreTheme() {
|
|
116
130
|
try {
|
|
117
131
|
var saved = localStorage.getItem(THEME_KEY);
|
|
@@ -125,10 +139,101 @@
|
|
|
125
139
|
document.querySelectorAll(".ox-navbar").forEach(function (n) { n.setAttribute("data-scrolled", scrolled); });
|
|
126
140
|
}
|
|
127
141
|
|
|
142
|
+
/* ---------- Mobile navigation menu ---------- */
|
|
143
|
+
function setNav(nav, open) {
|
|
144
|
+
if (!nav) return;
|
|
145
|
+
nav.setAttribute("data-open", String(open));
|
|
146
|
+
var toggle = nav.querySelector("[data-ox-nav-toggle]");
|
|
147
|
+
if (toggle) {
|
|
148
|
+
toggle.setAttribute("aria-expanded", String(open));
|
|
149
|
+
toggle.setAttribute("aria-label", open ? "Close menu" : "Open menu");
|
|
150
|
+
}
|
|
151
|
+
document.body.classList.toggle("ox-nav-locked", open);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/* ---------- Range slider fill ---------- */
|
|
155
|
+
function syncRange(r) {
|
|
156
|
+
var min = +r.min || 0, max = r.max === "" ? 100 : +r.max;
|
|
157
|
+
r.style.setProperty("--_pct", ((+r.value - min) / (max - min || 1)) * 100 + "%");
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/* ---------- Number stepper ---------- */
|
|
161
|
+
function step(btn) {
|
|
162
|
+
var box = btn.closest(".ox-stepper");
|
|
163
|
+
var input = box.querySelector("input");
|
|
164
|
+
var by = +btn.getAttribute("data-step") * (+input.step || 1);
|
|
165
|
+
var min = input.min === "" ? -Infinity : +input.min;
|
|
166
|
+
var max = input.max === "" ? Infinity : +input.max;
|
|
167
|
+
var next = Math.min(max, Math.max(min, (+input.value || 0) + by));
|
|
168
|
+
input.value = +next.toFixed(6);
|
|
169
|
+
syncStepper(box);
|
|
170
|
+
input.dispatchEvent(new Event("input", { bubbles: true }));
|
|
171
|
+
input.dispatchEvent(new Event("change", { bubbles: true }));
|
|
172
|
+
}
|
|
173
|
+
function syncStepper(box) {
|
|
174
|
+
var input = box.querySelector("input");
|
|
175
|
+
var v = +input.value;
|
|
176
|
+
box.querySelectorAll("[data-step]").forEach(function (b) {
|
|
177
|
+
var dir = +b.getAttribute("data-step");
|
|
178
|
+
b.disabled = dir < 0 ? input.min !== "" && v <= +input.min : input.max !== "" && v >= +input.max;
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/* ---------- Chips ---------- */
|
|
183
|
+
function toggleChip(chip) {
|
|
184
|
+
var group = chip.closest(".ox-chips");
|
|
185
|
+
var on = chip.getAttribute("aria-pressed") !== "true";
|
|
186
|
+
if (group && group.hasAttribute("data-single")) {
|
|
187
|
+
group.querySelectorAll(".ox-chip").forEach(function (c) { c.setAttribute("aria-pressed", "false"); });
|
|
188
|
+
on = true;
|
|
189
|
+
}
|
|
190
|
+
chip.setAttribute("aria-pressed", String(on));
|
|
191
|
+
if (group) {
|
|
192
|
+
var values = Array.prototype.filter.call(group.querySelectorAll(".ox-chip"), function (c) { return c.getAttribute("aria-pressed") === "true"; })
|
|
193
|
+
.map(function (c) { return c.value || c.textContent.trim(); });
|
|
194
|
+
group.dispatchEvent(new CustomEvent("ox:change", { bubbles: true, detail: { value: values } }));
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function initWidgets(root) {
|
|
199
|
+
(root || document).querySelectorAll(".ox-range").forEach(syncRange);
|
|
200
|
+
(root || document).querySelectorAll(".ox-stepper").forEach(syncStepper);
|
|
201
|
+
// Banners dismissed before stay hidden
|
|
202
|
+
(root || document).querySelectorAll(".ox-banner[data-ox-banner]").forEach(function (b) {
|
|
203
|
+
try { if (localStorage.getItem("ox-banner-" + b.getAttribute("data-ox-banner"))) b.hidden = true; } catch (e) { /* storage unavailable */ }
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
|
|
128
207
|
/* ---------- Event delegation ---------- */
|
|
208
|
+
document.addEventListener("input", function (e) {
|
|
209
|
+
if (e.target.classList && e.target.classList.contains("ox-range")) syncRange(e.target);
|
|
210
|
+
var box = e.target.closest && e.target.closest(".ox-stepper");
|
|
211
|
+
if (box) syncStepper(box);
|
|
212
|
+
});
|
|
213
|
+
|
|
129
214
|
document.addEventListener("click", function (e) {
|
|
130
215
|
var t = e.target;
|
|
131
216
|
|
|
217
|
+
var navToggle = t.closest("[data-ox-nav-toggle]");
|
|
218
|
+
if (navToggle) { var nav = navToggle.closest(".ox-navbar"); setNav(nav, nav.getAttribute("data-open") !== "true"); return; }
|
|
219
|
+
if (t.closest(".ox-navbar-menu a")) setNav(t.closest(".ox-navbar"), false);
|
|
220
|
+
|
|
221
|
+
var stepBtn = t.closest(".ox-stepper [data-step]");
|
|
222
|
+
if (stepBtn) { step(stepBtn); return; }
|
|
223
|
+
|
|
224
|
+
var chip = t.closest(".ox-chip");
|
|
225
|
+
if (chip) { toggleChip(chip); return; }
|
|
226
|
+
|
|
227
|
+
var dismiss = t.closest("[data-ox-dismiss]");
|
|
228
|
+
if (dismiss) {
|
|
229
|
+
var banner = dismiss.closest(".ox-banner");
|
|
230
|
+
if (banner) {
|
|
231
|
+
banner.hidden = true;
|
|
232
|
+
try { if (banner.getAttribute("data-ox-banner")) localStorage.setItem("ox-banner-" + banner.getAttribute("data-ox-banner"), "1"); } catch (err) { /* storage unavailable */ }
|
|
233
|
+
}
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
|
|
132
237
|
var opener = t.closest("[data-ox-modal-open]");
|
|
133
238
|
if (opener) { openModal(opener.getAttribute("data-ox-modal-open")); return; }
|
|
134
239
|
|
|
@@ -160,7 +265,11 @@
|
|
|
160
265
|
var toaster = t.closest("[data-ox-toast]");
|
|
161
266
|
if (toaster) toast(toaster.getAttribute("data-ox-toast"));
|
|
162
267
|
|
|
163
|
-
|
|
268
|
+
var themeBtn = t.closest("[data-ox-theme-toggle]");
|
|
269
|
+
if (themeBtn) {
|
|
270
|
+
var r = themeBtn.getBoundingClientRect();
|
|
271
|
+
setTheme(currentTheme() === "dark" ? "light" : "dark", { x: r.left + r.width / 2, y: r.top + r.height / 2 });
|
|
272
|
+
}
|
|
164
273
|
});
|
|
165
274
|
|
|
166
275
|
document.addEventListener("keydown", function (e) {
|
|
@@ -168,6 +277,8 @@
|
|
|
168
277
|
if (e.key === "Escape") {
|
|
169
278
|
if (openModalEl) closeModal(openModalEl);
|
|
170
279
|
closeAllDropdowns();
|
|
280
|
+
var openNav = document.querySelector('.ox-navbar[data-open="true"]');
|
|
281
|
+
if (openNav) { setNav(openNav, false); var tg = openNav.querySelector("[data-ox-nav-toggle]"); if (tg) tg.focus(); }
|
|
171
282
|
}
|
|
172
283
|
if (e.key === "Tab" && openModalEl) trapFocus(openModalEl, e);
|
|
173
284
|
|
|
@@ -183,14 +294,24 @@
|
|
|
183
294
|
});
|
|
184
295
|
|
|
185
296
|
global.addEventListener("scroll", syncNavbars, { passive: true });
|
|
186
|
-
|
|
297
|
+
// Close the mobile menu if the window grows past the breakpoint while it's open
|
|
298
|
+
global.addEventListener("resize", function () {
|
|
299
|
+
document.querySelectorAll('.ox-navbar[data-open="true"]').forEach(function (n) {
|
|
300
|
+
var toggle = n.querySelector("[data-ox-nav-toggle]");
|
|
301
|
+
if (toggle && getComputedStyle(toggle).display === "none") setNav(n, false);
|
|
302
|
+
});
|
|
303
|
+
});
|
|
304
|
+
document.addEventListener("DOMContentLoaded", function () { syncNavbars(); initWidgets(); });
|
|
305
|
+
if (document.readyState !== "loading") initWidgets();
|
|
187
306
|
restoreTheme();
|
|
188
307
|
|
|
189
308
|
global.Onyx = {
|
|
190
|
-
version: "1.
|
|
309
|
+
version: "1.1.0",
|
|
191
310
|
modal: { open: openModal, close: closeModal },
|
|
192
311
|
toast: toast,
|
|
193
312
|
copy: copy,
|
|
194
|
-
theme: { get: currentTheme, set: setTheme }
|
|
313
|
+
theme: { get: currentTheme, set: setTheme },
|
|
314
|
+
nav: { open: function (n) { setNav(n || document.querySelector(".ox-navbar"), true); }, close: function (n) { setNav(n || document.querySelector(".ox-navbar"), false); } },
|
|
315
|
+
init: initWidgets
|
|
195
316
|
};
|
|
196
317
|
})(window);
|