@mieweb/ui 0.10.0-dev.44 → 0.10.0-dev.46
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/dist/{chunk-4IS3QCOQ.cjs → chunk-2FZYZ4HT.cjs} +4 -3
- package/dist/chunk-2FZYZ4HT.cjs.map +1 -0
- package/dist/{chunk-IT775BYG.js → chunk-2W73UQ3T.js} +4 -3
- package/dist/chunk-2W73UQ3T.js.map +1 -0
- package/dist/{chunk-VEE46VTX.js → chunk-4RI3CQGE.js} +4 -4
- package/dist/{chunk-VEE46VTX.js.map → chunk-4RI3CQGE.js.map} +1 -1
- package/dist/{chunk-DHU3FFC3.cjs → chunk-ATRW3J5Z.cjs} +7 -7
- package/dist/{chunk-DHU3FFC3.cjs.map → chunk-ATRW3J5Z.cjs.map} +1 -1
- package/dist/{chunk-WFL4N5PC.js → chunk-DXFP53KF.js} +3 -3
- package/dist/{chunk-WFL4N5PC.js.map → chunk-DXFP53KF.js.map} +1 -1
- package/dist/{chunk-CVXESGXJ.js → chunk-EBKE4DLU.js} +9 -3
- package/dist/chunk-EBKE4DLU.js.map +1 -0
- package/dist/{chunk-X3RSNAMX.js → chunk-EGPDT7SM.js} +3 -3
- package/dist/{chunk-X3RSNAMX.js.map → chunk-EGPDT7SM.js.map} +1 -1
- package/dist/{chunk-XVW5DPPP.js → chunk-F7SZSOB3.js} +4 -3
- package/dist/chunk-F7SZSOB3.js.map +1 -0
- package/dist/{chunk-S4XCPDEO.cjs → chunk-FBN2VHUV.cjs} +9 -3
- package/dist/chunk-FBN2VHUV.cjs.map +1 -0
- package/dist/{chunk-F2FOG4EG.cjs → chunk-FMEA6WAY.cjs} +9 -9
- package/dist/{chunk-F2FOG4EG.cjs.map → chunk-FMEA6WAY.cjs.map} +1 -1
- package/dist/{chunk-VFFV7EMT.cjs → chunk-GYXSF6BJ.cjs} +6 -6
- package/dist/{chunk-VFFV7EMT.cjs.map → chunk-GYXSF6BJ.cjs.map} +1 -1
- package/dist/{chunk-ZSNFL736.cjs → chunk-IEZXJWHJ.cjs} +5 -5
- package/dist/{chunk-ZSNFL736.cjs.map → chunk-IEZXJWHJ.cjs.map} +1 -1
- package/dist/{chunk-TVU4KB27.cjs → chunk-KIVEYQGA.cjs} +3 -37
- package/dist/chunk-KIVEYQGA.cjs.map +1 -0
- package/dist/{chunk-LUPRGPKP.js → chunk-LAN5VWGC.js} +4 -4
- package/dist/{chunk-LUPRGPKP.js.map → chunk-LAN5VWGC.js.map} +1 -1
- package/dist/{chunk-SACHTD7B.js → chunk-LX323IMT.js} +3 -3
- package/dist/{chunk-SACHTD7B.js.map → chunk-LX323IMT.js.map} +1 -1
- package/dist/{chunk-B4CKCNWD.js → chunk-ME7EVT6F.js} +9 -3
- package/dist/chunk-ME7EVT6F.js.map +1 -0
- package/dist/{chunk-EEOGIGIA.js → chunk-OYUIEJPN.js} +15 -6
- package/dist/chunk-OYUIEJPN.js.map +1 -0
- package/dist/{chunk-C77FAR3F.cjs → chunk-PINIBEIU.cjs} +15 -6
- package/dist/chunk-PINIBEIU.cjs.map +1 -0
- package/dist/{chunk-OIMXMPH2.cjs → chunk-PWXDRFJL.cjs} +6 -6
- package/dist/{chunk-OIMXMPH2.cjs.map → chunk-PWXDRFJL.cjs.map} +1 -1
- package/dist/{chunk-LC7PVBXH.cjs → chunk-S7AMZ4RD.cjs} +9 -3
- package/dist/chunk-S7AMZ4RD.cjs.map +1 -0
- package/dist/{chunk-OSTSZDQC.js → chunk-TJ4GWQPM.js} +6 -4
- package/dist/chunk-TJ4GWQPM.js.map +1 -0
- package/dist/{chunk-DZ3JTMKM.cjs → chunk-U2RS574O.cjs} +12 -10
- package/dist/{chunk-DZ3JTMKM.cjs.map → chunk-U2RS574O.cjs.map} +1 -1
- package/dist/{chunk-W3HXHIDO.cjs → chunk-WSMBIXPU.cjs} +11 -11
- package/dist/{chunk-W3HXHIDO.cjs.map → chunk-WSMBIXPU.cjs.map} +1 -1
- package/dist/{chunk-XDIMTPZU.cjs → chunk-XPWFIDZM.cjs} +4 -3
- package/dist/chunk-XPWFIDZM.cjs.map +1 -0
- package/dist/{chunk-F3EWRDIT.js → chunk-YG7BR76Y.js} +4 -35
- package/dist/{chunk-TVU4KB27.cjs.map → chunk-YG7BR76Y.js.map} +1 -1
- package/dist/chunk-YVKBXNPK.js +41 -0
- package/dist/chunk-YVKBXNPK.js.map +1 -0
- package/dist/{chunk-24AFHH4J.js → chunk-YZCM7CXK.js} +3 -3
- package/dist/{chunk-24AFHH4J.js.map → chunk-YZCM7CXK.js.map} +1 -1
- package/dist/chunk-Z3WMDVMR.cjs +46 -0
- package/dist/chunk-Z3WMDVMR.cjs.map +1 -0
- package/dist/components/BoardView/index.cjs +5 -4
- package/dist/components/BoardView/index.js +2 -1
- package/dist/components/Dropdown/index.cjs +9 -8
- package/dist/components/Dropdown/index.d.cts +4 -3
- package/dist/components/Dropdown/index.d.ts +4 -3
- package/dist/components/Dropdown/index.js +2 -1
- package/dist/components/EmailEditor/index.cjs +14 -13
- package/dist/components/EmailEditor/index.js +5 -4
- package/dist/components/RecordHeader/index.cjs +5 -4
- package/dist/components/RecordHeader/index.js +3 -2
- package/dist/components/RecordLayout/index.cjs +5 -4
- package/dist/components/RecordLayout/index.js +3 -2
- package/dist/components/RichTextEditor/index.cjs +6 -5
- package/dist/components/RichTextEditor/index.js +3 -2
- package/dist/components/SuperChat/index.cjs +10 -9
- package/dist/components/SuperChat/index.cjs.map +1 -1
- package/dist/components/SuperChat/index.js +3 -2
- package/dist/components/SuperChat/index.js.map +1 -1
- package/dist/components/SuperChat/plugins/index.cjs +5 -4
- package/dist/components/SuperChat/plugins/index.cjs.map +1 -1
- package/dist/components/SuperChat/plugins/index.js +4 -3
- package/dist/components/SuperChat/plugins/index.js.map +1 -1
- package/dist/components/Tabs/index.cjs +8 -7
- package/dist/components/Tabs/index.js +2 -1
- package/dist/components/ViewSet/index.cjs +5 -4
- package/dist/components/ViewSet/index.js +4 -3
- package/dist/components/ViewSwitcher/index.cjs +4 -3
- package/dist/components/ViewSwitcher/index.js +2 -1
- package/dist/deck.cjs +12 -3
- package/dist/deck.cjs.map +1 -1
- package/dist/deck.js +12 -3
- package/dist/deck.js.map +1 -1
- package/dist/esheet.cjs +5 -4
- package/dist/esheet.cjs.map +1 -1
- package/dist/esheet.js +2 -1
- package/dist/esheet.js.map +1 -1
- package/dist/{hey-buddy-INNUFFJJ.cjs → hey-buddy-5WOBYZRJ.cjs} +22 -11
- package/dist/hey-buddy-5WOBYZRJ.cjs.map +1 -0
- package/dist/{hey-buddy-V6FC5UKB.js → hey-buddy-AHUHCDBC.js} +22 -11
- package/dist/hey-buddy-AHUHCDBC.js.map +1 -0
- package/dist/hooks/index.cjs +36 -31
- package/dist/hooks/index.d.cts +13 -1
- package/dist/hooks/index.d.ts +13 -1
- package/dist/hooks/index.js +2 -1
- package/dist/index.cjs +215 -212
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +19 -5
- package/dist/index.d.ts +19 -5
- package/dist/index.js +30 -30
- package/dist/index.js.map +1 -1
- package/dist/org-chart.cjs +2 -1
- package/dist/org-chart.cjs.map +1 -1
- package/dist/org-chart.js +2 -1
- package/dist/org-chart.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-4IS3QCOQ.cjs.map +0 -1
- package/dist/chunk-B4CKCNWD.js.map +0 -1
- package/dist/chunk-C77FAR3F.cjs.map +0 -1
- package/dist/chunk-CVXESGXJ.js.map +0 -1
- package/dist/chunk-EEOGIGIA.js.map +0 -1
- package/dist/chunk-F3EWRDIT.js.map +0 -1
- package/dist/chunk-IT775BYG.js.map +0 -1
- package/dist/chunk-LC7PVBXH.cjs.map +0 -1
- package/dist/chunk-OSTSZDQC.js.map +0 -1
- package/dist/chunk-S4XCPDEO.cjs.map +0 -1
- package/dist/chunk-XDIMTPZU.cjs.map +0 -1
- package/dist/chunk-XVW5DPPP.js.map +0 -1
- package/dist/hey-buddy-INNUFFJJ.cjs.map +0 -1
- package/dist/hey-buddy-V6FC5UKB.js.map +0 -1
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
var chunk4NCDU5IG_cjs = require('./chunk-4NCDU5IG.cjs');
|
|
4
4
|
var chunkL7L5S2HO_cjs = require('./chunk-L7L5S2HO.cjs');
|
|
5
|
+
var chunkZ3WMDVMR_cjs = require('./chunk-Z3WMDVMR.cjs');
|
|
5
6
|
var chunkS5RVSXDV_cjs = require('./chunk-S5RVSXDV.cjs');
|
|
6
7
|
var React = require('react');
|
|
7
8
|
var classVarianceAuthority = require('class-variance-authority');
|
|
@@ -109,7 +110,7 @@ var ViewSwitcher = React__namespace.forwardRef(
|
|
|
109
110
|
};
|
|
110
111
|
const onKeyDown = (event) => {
|
|
111
112
|
const enabled = options.filter((o) => !o.disabled);
|
|
112
|
-
const forward =
|
|
113
|
+
const forward = chunkZ3WMDVMR_cjs.isRtl(event.currentTarget) ? -1 : 1;
|
|
113
114
|
switch (event.key) {
|
|
114
115
|
case "ArrowRight":
|
|
115
116
|
event.preventDefault();
|
|
@@ -198,5 +199,5 @@ var ViewSwitcher = React__namespace.forwardRef(
|
|
|
198
199
|
|
|
199
200
|
exports.ViewSwitcher = ViewSwitcher;
|
|
200
201
|
exports.viewSwitcherVariants = viewSwitcherVariants;
|
|
201
|
-
//# sourceMappingURL=chunk-
|
|
202
|
-
//# sourceMappingURL=chunk-
|
|
202
|
+
//# sourceMappingURL=chunk-XPWFIDZM.cjs.map
|
|
203
|
+
//# sourceMappingURL=chunk-XPWFIDZM.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/ViewSwitcher/ViewSwitcher.tsx"],"names":["LayoutGrid","List","Columns","Calendar","BarChart3","Table","cva","React","ViewSwitcher","defaultViewLabels","isRtl","jsx","cn","jsxs"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgDA,IAAM,SAAA,GAMF;AAAA,EACF,QAAA,EAAU,EAAE,KAAA,EAAO,UAAA,EAAY,MAAMA,4BAAA,EAAS;AAAA,EAC9C,IAAA,EAAM,EAAE,KAAA,EAAO,MAAA,EAAQ,MAAMC,sBAAA,EAAS;AAAA,EACtC,KAAA,EAAO,EAAE,KAAA,EAAO,OAAA,EAAS,MAAMC,yBAAA,EAAY;AAAA,EAC3C,QAAA,EAAU,EAAE,KAAA,EAAO,UAAA,EAAY,MAAMC,0BAAA,EAAa;AAAA,EAClD,KAAA,EAAO,EAAE,KAAA,EAAO,OAAA,EAAS,MAAMC,2BAAA,EAAU;AAAA,EACzC,OAAA,EAAS,EAAE,KAAA,EAAO,SAAA,EAAW,MAAMA,2BAAA,EAAU;AAAA,EAC7C,KAAA,EAAO,EAAE,KAAA,EAAO,OAAA,EAAS,MAAMC,uBAAA;AACjC,CAAA;AAMA,IAAM,oBAAA,GAAuBC,0BAAA;AAAA,EAC3B;AAAA,IACE,kCAAA;AAAA,IACA,gDAAA;AAAA,IACA;AAAA,GACF;AAAA,EACA;AAAA,IACE,QAAA,EAAU;AAAA,MACR,IAAA,EAAM;AAAA,QACJ,EAAA,EAAI,SAAA;AAAA,QACJ,EAAA,EAAI;AAAA;AACN,KACF;AAAA,IACA,eAAA,EAAiB,EAAE,IAAA,EAAM,IAAA;AAAK;AAElC;AAEA,IAAM,cAAA,GAAiBA,0BAAA;AAAA,EACrB;AAAA,IACE,kEAAA;AAAA,IACA,mBAAA;AAAA,IACA,0IAAA;AAAA,IACA;AAAA,GACF;AAAA,EACA;AAAA,IACE,QAAA,EAAU;AAAA,MACR,IAAA,EAAM;AAAA;AAAA,QAEJ,EAAA,EAAI,+CAAA;AAAA,QACJ,EAAA,EAAI;AAAA,OACN;AAAA,MACA,MAAA,EAAQ;AAAA;AAAA;AAAA,QAGN,IAAA,EAAM,yCAAA;AAAA,QACN,KAAA,EAAO;AAAA;AACT,KACF;AAAA,IACA,eAAA,EAAiB,EAAE,IAAA,EAAM,IAAA,EAAM,QAAQ,KAAA;AAAM;AAEjD,CAAA;AAWO,IAAM,YAAA,GAAqBC,gBAAA,CAAA,UAAA;AAAA,EAChC,SAASC,aAAAA,CACP;AAAA,IACE,KAAA;AAAA,IACA,KAAA;AAAA,IACA,aAAA;AAAA,IACA,QAAQC,mCAAA,CAAkB,YAAA;AAAA,IAC1B,UAAA,GAAa,YAAA;AAAA,IACb,IAAA;AAAA,IACA,SAAA;AAAA,IACA,UAAA;AAAA,IACA,GAAG;AAAA,KAEL,GAAA,EACA;AACA,IAAA,MAAM,OAAA,GAAgBF,gBAAA,CAAA,OAAA;AAAA,MACpB,MAAM,KAAA,CAAM,GAAA,CAAI,CAAC,CAAA,KAAO,OAAO,CAAA,KAAM,QAAA,GAAW,EAAE,EAAA,EAAI,CAAA,EAAE,GAAI,CAAE,CAAA;AAAA,MAC9D,CAAC,KAAK;AAAA,KACR;AACA,IAAA,MAAM,IAAA,GAAaA,gBAAA,CAAA,MAAA,CAAiD,EAAE,CAAA;AAEtE,IAAA,MAAM,MAAA,GAAS,CAAC,IAAA,KAAiC;AAC/C,MAAA,IAAI,CAAC,IAAA,IAAQ,IAAA,CAAK,EAAA,KAAO,KAAA,EAAO;AAChC,MAAA,aAAA,CAAc,KAAK,EAAE,CAAA;AAErB,MAAA,qBAAA;AAAA,QAAsB,MACpB,IAAA,CAAK,OAAA,CAAQ,IAAA,CAAK,EAAE,GAAG,KAAA,CAAM,EAAE,aAAA,EAAe,IAAA,EAAM;AAAA,OACtD;AAAA,IACF,CAAA;AAIA,IAAA,MAAM,IAAA,GAAO,CAAC,KAAA,KAAkB;AAC9B,MAAA,MAAM,UAAU,OAAA,CAAQ,MAAA,CAAO,CAAC,CAAA,KAAM,CAAC,EAAE,QAAQ,CAAA;AACjD,MAAA,IAAI,OAAA,CAAQ,WAAW,CAAA,EAAG;AAC1B,MAAA,MAAM,UAAU,OAAA,CAAQ,SAAA,CAAU,CAAC,CAAA,KAAM,CAAA,CAAE,OAAO,KAAK,CAAA;AACvD,MAAA,MAAM,OAAO,OAAA,KAAY,EAAA,GAAM,KAAA,GAAQ,CAAA,GAAI,KAAK,CAAA,GAAK,OAAA;AACrD,MAAA,MAAA,CAAO,SAAS,IAAA,GAAO,KAAA,GAAQ,QAAQ,MAAA,IAAU,OAAA,CAAQ,MAAM,CAAC,CAAA;AAAA,IAClE,CAAA;AAEA,IAAA,MAAM,SAAA,GAAY,CAAC,KAAA,KAAkD;AACnE,MAAA,MAAM,UAAU,OAAA,CAAQ,MAAA,CAAO,CAAC,CAAA,KAAM,CAAC,EAAE,QAAQ,CAAA;AAGjD,MAAA,MAAM,OAAA,GAAUG,uBAAA,CAAM,KAAA,CAAM,aAAa,IAAI,EAAA,GAAK,CAAA;AAClD,MAAA,QAAQ,MAAM,GAAA;AAAK,QACjB,KAAK,YAAA;AACH,UAAA,KAAA,CAAM,cAAA,EAAe;AACrB,UAAA,IAAA,CAAK,OAAO,CAAA;AACZ,UAAA;AAAA,QACF,KAAK,WAAA;AACH,UAAA,KAAA,CAAM,cAAA,EAAe;AACrB,UAAA,IAAA,CAAK,CAAC,OAAO,CAAA;AACb,UAAA;AAAA,QACF,KAAK,WAAA;AACH,UAAA,KAAA,CAAM,cAAA,EAAe;AACrB,UAAA,IAAA,CAAK,CAAC,CAAA;AACN,UAAA;AAAA,QACF,KAAK,SAAA;AACH,UAAA,KAAA,CAAM,cAAA,EAAe;AACrB,UAAA,IAAA,CAAK,EAAE,CAAA;AACP,UAAA;AAAA,QACF,KAAK,MAAA;AACH,UAAA,KAAA,CAAM,cAAA,EAAe;AACrB,UAAA,MAAA,CAAO,OAAA,CAAQ,CAAC,CAAC,CAAA;AACjB,UAAA;AAAA,QACF,KAAK,KAAA;AACH,UAAA,KAAA,CAAM,cAAA,EAAe;AACrB,UAAA,MAAA,CAAO,OAAA,CAAQ,OAAA,CAAQ,MAAA,GAAS,CAAC,CAAC,CAAA;AAClC,UAAA;AAEA;AACJ,IACF,CAAA;AAQA,IAAA,MAAM,SAAA,GACJ,QAAQ,IAAA,CAAK,CAAC,MAAM,CAAA,CAAE,EAAA,KAAO,SAAS,CAAC,CAAA,CAAE,QAAQ,CAAA,EAAG,EAAA,IACpD,QAAQ,IAAA,CAAK,CAAC,MAAM,CAAC,CAAA,CAAE,QAAQ,CAAA,EAAG,EAAA;AAEpC,IAAA,uBACEC,cAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,GAAA;AAAA,QACA,IAAA,EAAK,YAAA;AAAA,QACL,YAAA,EAAY,KAAA;AAAA,QACZ,WAAA,EAAU,eAAA;AAAA,QACV,WAAWC,oBAAA,CAAG,oBAAA,CAAqB,EAAE,IAAA,EAAM,GAAG,SAAS,CAAA;AAAA,QACtD,GAAG,IAAA;AAAA,QAEH,QAAA,EAAA,OAAA,CAAQ,GAAA,CAAI,CAAC,MAAA,KAAW;AACvB,UAAA,MAAM,IAAA,GAAO,SAAA,CAAU,MAAA,CAAO,EAAE,CAAA;AAChC,UAAA,MAAM,IAAA,GAAO,MAAA,CAAO,IAAA,IAAQ,IAAA,CAAK,IAAA;AACjC,UAAA,MAAM,IAAA,GAAO,MAAA,CAAO,KAAA,IAAS,IAAA,CAAK,KAAA;AAClC,UAAA,MAAM,MAAA,GAAS,OAAO,EAAA,KAAO,KAAA;AAC7B,UAAA,uBACEC,eAAA;AAAA,YAAC,QAAA;AAAA,YAAA;AAAA,cAEC,GAAA,EAAK,CAAC,IAAA,KAAS;AACb,gBAAA,IAAA,CAAK,OAAA,CAAQ,MAAA,CAAO,EAAE,CAAA,GAAI,IAAA;AAAA,cAC5B,CAAA;AAAA,cACA,IAAA,EAAK,QAAA;AAAA,cACL,IAAA,EAAK,OAAA;AAAA,cACL,cAAA,EAAc,MAAA;AAAA,cACd,YAAA,EAAY,UAAA,KAAe,IAAA,GAAO,MAAA,GAAY,IAAA;AAAA,cAC9C,KAAA,EAAO,IAAA;AAAA,cACP,UAAU,MAAA,CAAO,QAAA;AAAA,cACjB,QAAA,EAAU,MAAA,CAAO,EAAA,KAAO,SAAA,GAAY,CAAA,GAAI,EAAA;AAAA,cACxC,WAAA,EAAU,sBAAA;AAAA,cACV,aAAW,MAAA,CAAO,EAAA;AAAA,cAClB,SAAA,EAAWD,oBAAA;AAAA,gBACT,cAAA,CAAe,EAAE,IAAA,EAAM,MAAA,EAAQ,CAAA;AAAA,gBAC/B,UAAA,EAAY,MAAA;AAAA,gBACZ,UAAU,UAAA,EAAY;AAAA,eACxB;AAAA,cACA,OAAA,EAAS,MAAM,aAAA,CAAc,MAAA,CAAO,EAAE,CAAA;AAAA,cACtC,SAAA;AAAA,cAEA,QAAA,EAAA;AAAA,gCAAAD,cAAA,CAAC,IAAA,EAAA,EAAK,SAAA,EAAU,iBAAA,EAAkB,aAAA,EAAW,IAAA,EAAC,CAAA;AAAA,gBAC7C,eAAe,KAAA,oBACdA,cAAA;AAAA,kBAAC,MAAA;AAAA,kBAAA;AAAA,oBACC,WAAA,EAAU,qBAAA;AAAA,oBACV,SAAA,EAAWC,oBAAA;AAAA,sBACT,eAAe,YAAA,IAAgB;AAAA,qBACjC;AAAA,oBAEC,QAAA,EAAA;AAAA;AAAA;AACH;AAAA,aAAA;AAAA,YA9BG,MAAA,CAAO;AAAA,WAgCd;AAAA,QAEJ,CAAC;AAAA;AAAA,KACH;AAAA,EAEJ;AACF","file":"chunk-XPWFIDZM.cjs","sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { isRtl } from '../../hooks/useDirection';\nimport { cn } from '../../utils/cn';\nimport {\n CalendarIcon,\n ChartIcon,\n ColumnsIcon,\n GridIcon,\n ListIcon,\n TableIcon,\n} from '../Icons';\nimport { defaultViewLabels, type ViewId } from '../../views/types';\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface ViewOption {\n id: ViewId;\n /** Overrides the built-in English label. */\n label?: string;\n /** Overrides the built-in icon. */\n icon?: React.ComponentType<{ className?: string; 'aria-hidden'?: boolean }>;\n disabled?: boolean;\n}\n\nexport interface ViewSwitcherProps extends VariantProps<\n typeof viewSwitcherVariants\n> {\n /** Views to offer, in order. Accepts bare ids or `ViewOption`s. */\n views: readonly (ViewId | ViewOption)[];\n value: ViewId;\n onValueChange: (view: ViewId) => void;\n /** Accessible name of the group. */\n label?: string;\n /** Show labels beside the icons. `responsive` hides them below `sm`. */\n showLabels?: boolean | 'responsive';\n className?: string;\n classNames?: Partial<Record<'option' | 'activeOption', string>>;\n}\n\n// =============================================================================\n// Defaults\n// =============================================================================\n\nconst VIEW_META: Record<\n ViewId,\n {\n label: string;\n icon: React.ComponentType<{ className?: string; 'aria-hidden'?: boolean }>;\n }\n> = {\n overview: { label: 'Overview', icon: GridIcon },\n list: { label: 'List', icon: ListIcon },\n board: { label: 'Board', icon: ColumnsIcon },\n calendar: { label: 'Calendar', icon: CalendarIcon },\n gantt: { label: 'Gantt', icon: ChartIcon },\n roadmap: { label: 'Roadmap', icon: ChartIcon },\n table: { label: 'Table', icon: TableIcon },\n};\n\n// =============================================================================\n// Variants\n// =============================================================================\n\nconst viewSwitcherVariants = cva(\n [\n 'inline-flex items-center gap-0.5',\n 'rounded-lg border border-border bg-muted p-0.5',\n 'overflow-x-auto',\n ],\n {\n variants: {\n size: {\n sm: 'text-xs',\n md: 'text-sm',\n },\n },\n defaultVariants: { size: 'md' },\n }\n);\n\nconst optionVariants = cva(\n [\n 'inline-flex shrink-0 items-center gap-1.5 rounded-md font-medium',\n 'transition-colors',\n 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background',\n 'disabled:pointer-events-none disabled:opacity-50',\n ],\n {\n variants: {\n size: {\n // 40px tall on touch, tighter once a pointer is likely.\n sm: 'min-h-10 px-2 py-2 sm:min-h-0 sm:px-2 sm:py-1',\n md: 'min-h-10 px-2.5 py-2 sm:min-h-0 sm:px-3 sm:py-1.5',\n },\n active: {\n // A raised surface rather than tinted ink: an accent colour as text on\n // its own wash fails AA contrast. Same treatment as Tabs' pills.\n true: 'bg-background text-foreground shadow-sm',\n false: 'text-muted-foreground hover:text-foreground',\n },\n },\n defaultVariants: { size: 'md', active: false },\n }\n);\n\n// =============================================================================\n// Component\n// =============================================================================\n\n/**\n * Keyboard model is a radio group: one tab stop, arrows move *and* select.\n * Every option is visible, so moving the selection is the whole interaction —\n * there is nothing to \"commit\" afterwards.\n */\nexport const ViewSwitcher = React.forwardRef<HTMLDivElement, ViewSwitcherProps>(\n function ViewSwitcher(\n {\n views,\n value,\n onValueChange,\n label = defaultViewLabels.viewSwitcher,\n showLabels = 'responsive',\n size,\n className,\n classNames,\n ...rest\n },\n ref\n ) {\n const options = React.useMemo<ViewOption[]>(\n () => views.map((v) => (typeof v === 'string' ? { id: v } : v)),\n [views]\n );\n const refs = React.useRef<Record<string, HTMLButtonElement | null>>({});\n\n const select = (next: ViewOption | undefined) => {\n if (!next || next.id === value) return;\n onValueChange(next.id);\n // The selection owns the tab stop, so focus has to follow it.\n requestAnimationFrame(() =>\n refs.current[next.id]?.focus({ preventScroll: true })\n );\n };\n\n // Stepping is relative to the selected view, not to whichever button received\n // the key: focus moves asynchronously, and the two would drift apart.\n const step = (delta: number) => {\n const enabled = options.filter((o) => !o.disabled);\n if (enabled.length === 0) return;\n const current = enabled.findIndex((o) => o.id === value);\n const from = current === -1 ? (delta > 0 ? -1 : 0) : current;\n select(enabled[(from + delta + enabled.length) % enabled.length]);\n };\n\n const onKeyDown = (event: React.KeyboardEvent<HTMLButtonElement>) => {\n const enabled = options.filter((o) => !o.disabled);\n // Logical, not physical: ArrowRight moves to the visually next option,\n // which is the previous one under RTL.\n const forward = isRtl(event.currentTarget) ? -1 : 1;\n switch (event.key) {\n case 'ArrowRight':\n event.preventDefault();\n step(forward);\n break;\n case 'ArrowLeft':\n event.preventDefault();\n step(-forward);\n break;\n case 'ArrowDown':\n event.preventDefault();\n step(1);\n break;\n case 'ArrowUp':\n event.preventDefault();\n step(-1);\n break;\n case 'Home':\n event.preventDefault();\n select(enabled[0]);\n break;\n case 'End':\n event.preventDefault();\n select(enabled[enabled.length - 1]);\n break;\n default:\n break;\n }\n };\n\n // The roving tab stop has to land on an option the browser will actually\n // focus. A disabled `<button>` is unfocusable, so if the active option is\n // also disabled — a controlled caller, or a `defaultView` that later got\n // disabled — putting the only `tabIndex={0}` on it leaves the radiogroup\n // with no keyboard entry point at all. Fall back to the first enabled\n // option; arrows then move from there.\n const tabStopId =\n options.find((o) => o.id === value && !o.disabled)?.id ??\n options.find((o) => !o.disabled)?.id;\n\n return (\n <div\n ref={ref}\n role=\"radiogroup\"\n aria-label={label}\n data-slot=\"view-switcher\"\n className={cn(viewSwitcherVariants({ size }), className)}\n {...rest}\n >\n {options.map((option) => {\n const meta = VIEW_META[option.id];\n const Icon = option.icon ?? meta.icon;\n const text = option.label ?? meta.label;\n const active = option.id === value;\n return (\n <button\n key={option.id}\n ref={(node) => {\n refs.current[option.id] = node;\n }}\n type=\"button\"\n role=\"radio\"\n aria-checked={active}\n aria-label={showLabels === true ? undefined : text}\n title={text}\n disabled={option.disabled}\n tabIndex={option.id === tabStopId ? 0 : -1}\n data-slot=\"view-switcher-option\"\n data-view={option.id}\n className={cn(\n optionVariants({ size, active }),\n classNames?.option,\n active && classNames?.activeOption\n )}\n onClick={() => onValueChange(option.id)}\n onKeyDown={onKeyDown}\n >\n <Icon className=\"size-4 shrink-0\" aria-hidden />\n {showLabels !== false && (\n <span\n data-slot=\"view-switcher-label\"\n className={cn(\n showLabels === 'responsive' && 'hidden sm:inline'\n )}\n >\n {text}\n </span>\n )}\n </button>\n );\n })}\n </div>\n );\n }\n);\n\nexport { viewSwitcherVariants };\n"]}
|
|
@@ -1,38 +1,7 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { useState, useEffect, useCallback } from 'react';
|
|
3
3
|
|
|
4
|
-
// src/hooks/
|
|
5
|
-
var RTL_LOCALES = ["ar", "he", "fa", "ur"];
|
|
6
|
-
function isRtlLocale(locale) {
|
|
7
|
-
const lang = locale.toLowerCase().split(/[-_]/)[0];
|
|
8
|
-
return RTL_LOCALES.includes(lang);
|
|
9
|
-
}
|
|
10
|
-
function resolveDirection(element) {
|
|
11
|
-
if (typeof document === "undefined") return "ltr";
|
|
12
|
-
const target = element ?? document.documentElement;
|
|
13
|
-
return getComputedStyle(target).direction === "rtl" ? "rtl" : "ltr";
|
|
14
|
-
}
|
|
15
|
-
function useDirection(elementRef) {
|
|
16
|
-
const [direction, setDirection] = useState(
|
|
17
|
-
() => resolveDirection(elementRef?.current)
|
|
18
|
-
);
|
|
19
|
-
useEffect(() => {
|
|
20
|
-
const update = () => setDirection(resolveDirection(elementRef?.current));
|
|
21
|
-
update();
|
|
22
|
-
const observer = new MutationObserver(update);
|
|
23
|
-
observer.observe(document.documentElement, {
|
|
24
|
-
attributes: true,
|
|
25
|
-
attributeFilter: ["dir"],
|
|
26
|
-
subtree: true
|
|
27
|
-
});
|
|
28
|
-
return () => observer.disconnect();
|
|
29
|
-
}, [elementRef]);
|
|
30
|
-
const currentElement = elementRef?.current ?? null;
|
|
31
|
-
useEffect(() => {
|
|
32
|
-
setDirection(resolveDirection(currentElement));
|
|
33
|
-
}, [currentElement]);
|
|
34
|
-
return direction;
|
|
35
|
-
}
|
|
4
|
+
// src/hooks/useKeyboardInset.ts
|
|
36
5
|
var KEYBOARD_INSET_VAR = "--mieweb-keyboard-inset";
|
|
37
6
|
var VISUAL_VIEWPORT_HEIGHT_VAR = "--mieweb-visual-viewport-height";
|
|
38
7
|
var VISUAL_VIEWPORT_OFFSET_TOP_VAR = "--mieweb-visual-viewport-offset-top";
|
|
@@ -307,6 +276,6 @@ function localStorageOrderAdapter(key) {
|
|
|
307
276
|
};
|
|
308
277
|
}
|
|
309
278
|
|
|
310
|
-
export { KEYBOARD_INSET_VAR, KEYBOARD_OPEN_ATTRIBUTE, KEYBOARD_SOURCE_ATTRIBUTE,
|
|
311
|
-
//# sourceMappingURL=chunk-
|
|
312
|
-
//# sourceMappingURL=chunk-
|
|
279
|
+
export { KEYBOARD_INSET_VAR, KEYBOARD_OPEN_ATTRIBUTE, KEYBOARD_SOURCE_ATTRIBUTE, VISUAL_VIEWPORT_HEIGHT_VAR, VISUAL_VIEWPORT_OFFSET_TOP_VAR, localStorageOrderAdapter, mergeOrder, useCommandK, useIsDesktop, useIsLargeDesktop, useIsMobile, useIsMobileOrTablet, useIsSmallTablet, useIsTablet, useKeyboardInset, useKeyboardShortcut, useMediaQuery, useOrderPersistence };
|
|
280
|
+
//# sourceMappingURL=chunk-YG7BR76Y.js.map
|
|
281
|
+
//# sourceMappingURL=chunk-YG7BR76Y.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/hooks/useDirection.ts","../src/hooks/useKeyboardInset.ts","../src/hooks/useKeyboardShortcut.ts","../src/hooks/useMediaQuery.ts","../src/hooks/useOrderPersistence.ts"],"names":["useState","useEffect","useCallback","React"],"mappings":"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","file":"chunk-TVU4KB27.cjs","sourcesContent":["import { useState, useEffect } from 'react';\nimport type { RefObject } from 'react';\n\nexport type Direction = 'ltr' | 'rtl';\n\n/** Locales whose primary script reads right-to-left. */\nexport const RTL_LOCALES = ['ar', 'he', 'fa', 'ur'] as const;\n\n/**\n * Returns true when the given locale (e.g. `ar`, `ar-SA`, `he_IL`) uses a\n * right-to-left script.\n */\nexport function isRtlLocale(locale: string): boolean {\n const lang = locale.toLowerCase().split(/[-_]/)[0];\n return (RTL_LOCALES as readonly string[]).includes(lang);\n}\n\nfunction resolveDirection(element?: HTMLElement | null): Direction {\n if (typeof document === 'undefined') return 'ltr';\n const target = element ?? document.documentElement;\n return getComputedStyle(target).direction === 'rtl' ? 'rtl' : 'ltr';\n}\n\n/**\n * Hook returning the effective text direction (`ltr` | `rtl`) inherited from\n * the nearest `dir` attribute — zero setup for consumers who set\n * `<html dir=\"rtl\">`.\n *\n * Use for JS-level direction logic that CSS logical properties can't cover:\n * inverting ArrowLeft/ArrowRight keyboard handling, popover/toast placement,\n * drag math, etc.\n *\n * @param elementRef - Optional ref; when provided, direction is resolved from\n * that element (respecting any local `dir` override) instead of `<html>`.\n *\n * @example\n * ```tsx\n * const direction = useDirection();\n * const nextKey = direction === 'rtl' ? 'ArrowLeft' : 'ArrowRight';\n * ```\n */\nexport function useDirection(\n elementRef?: RefObject<HTMLElement | null>\n): Direction {\n const [direction, setDirection] = useState<Direction>(() =>\n resolveDirection(elementRef?.current)\n );\n\n useEffect(() => {\n const update = () => setDirection(resolveDirection(elementRef?.current));\n update();\n // dir changes are rare; observing the whole tree for just this attribute is cheap.\n const observer = new MutationObserver(update);\n observer.observe(document.documentElement, {\n attributes: true,\n attributeFilter: ['dir'],\n subtree: true,\n });\n return () => observer.disconnect();\n }, [elementRef]);\n\n // Re-sync when a re-render points elementRef.current at a different node:\n // the MutationObserver only fires on `dir` attribute mutations, so a swapped\n // ref target with a different inherited direction would otherwise go stale.\n const currentElement = elementRef?.current ?? null;\n useEffect(() => {\n setDirection(resolveDirection(currentElement));\n }, [currentElement]);\n\n return direction;\n}\n","import { useEffect, useState } from 'react';\n\n/** CSS custom properties written to `<html>` while the hook is mounted. */\nexport const KEYBOARD_INSET_VAR = '--mieweb-keyboard-inset';\nexport const VISUAL_VIEWPORT_HEIGHT_VAR = '--mieweb-visual-viewport-height';\nexport const VISUAL_VIEWPORT_OFFSET_TOP_VAR =\n '--mieweb-visual-viewport-offset-top';\n/** Boolean attribute set on `<html>` while an on-screen keyboard is open. */\nexport const KEYBOARD_OPEN_ATTRIBUTE = 'data-keyboard-open';\n/** Attribute on `<html>` naming the active {@link KeyboardInsetSource}. */\nexport const KEYBOARD_SOURCE_ATTRIBUTE = 'data-keyboard-source';\n\n/**\n * Where keyboard metrics come from.\n *\n * - `'visual-viewport'` — the Visual Viewport API. Works in any browser, but\n * iOS only reports the keyboard once its slide-in animation has finished.\n * - `'native'` — `keyboardWillShow` / `keyboardWillHide` window events with a\n * `keyboardHeight` property, as fired by `cordova-plugin-ionic-keyboard` and\n * Capacitor's Keyboard plugin. They arrive *before* the keyboard animates,\n * so layout can move in step with it.\n */\nexport type KeyboardInsetSource = 'visual-viewport' | 'native';\n\nexport interface KeyboardInsetState {\n /** Pixels of the layout viewport covered by the on-screen keyboard. */\n keyboardInset: number;\n /** Whether an on-screen keyboard currently covers part of the page. */\n isKeyboardOpen: boolean;\n}\n\nexport interface UseKeyboardInsetOptions {\n /** Track the keyboard; `false` stops tracking and clears the CSS variables. @default true */\n enabled?: boolean;\n /** Where keyboard metrics come from. @default 'visual-viewport' */\n source?: KeyboardInsetSource;\n}\n\ninterface NativeKeyboardEvent extends Event {\n keyboardHeight?: number;\n}\n\nconst CLOSED: KeyboardInsetState = { keyboardInset: 0, isKeyboardOpen: false };\n\n/**\n * Tracks the on-screen keyboard and publishes it on `<html>`.\n *\n * ### `source: 'native'` (Cordova / Capacitor apps)\n *\n * Listens for the native keyboard plugin's `keyboardWillShow` /\n * `keyboardWillHide` window events. Configure the plugin so the WebView is\n * neither resized nor panned (for `cordova-plugin-ionic-keyboard`:\n * `KeyboardResize=false`), keep the app shell at full height, and move only\n * the composer dock by `--mieweb-keyboard-inset` with a short transition\n * (~250ms) so it rides up with the keyboard. Only `--mieweb-keyboard-inset`,\n * `data-keyboard-open` and `data-keyboard-source` are published; the\n * visual-viewport variables stay unset.\n *\n * ### `source: 'visual-viewport'` (browsers, default)\n *\n * Uses the Visual Viewport API.\n *\n * iOS Safari and WKWebView (Cordova) ignore\n * `interactive-widget=resizes-content`, and `dvh` does not shrink for the\n * keyboard: the page keeps its full height and iOS pans the visual viewport\n * to reveal the focused input, pushing headers off-screen. This hook\n * measures what is actually visible and publishes it on `<html>` so an app\n * shell can follow the visible area instead:\n *\n * - `--mieweb-keyboard-inset` — keyboard height in px (0 when closed)\n * - `--mieweb-visual-viewport-height` — visible height in px\n * - `--mieweb-visual-viewport-offset-top` — how far iOS panned the page\n * - `data-keyboard-open` — present while the keyboard is open\n *\n * On browsers that already resize the layout for the keyboard (Chrome on\n * Android with `interactive-widget=resizes-content`) the inset stays 0 and\n * the variables simply mirror the layout viewport.\n *\n * iOS reports the visual viewport only after the keyboard has finished\n * animating, so a shell sized this way snaps into place once the keyboard\n * lands. Prefer `source: 'native'` wherever a native keyboard plugin exists.\n *\n * Mount it once, near the app root.\n *\n * @example\n * ```tsx\n * function AppShell({ children }) {\n * useKeyboardInset();\n * return (\n * <div\n * style={{\n * height: 'var(--mieweb-visual-viewport-height, 100dvh)',\n * transform: 'translateY(var(--mieweb-visual-viewport-offset-top, 0px))',\n * }}\n * >\n * {children}\n * </div>\n * );\n * }\n * ```\n *\n * @example Native app (keyboard plugin with WebView resizing disabled)\n * ```tsx\n * function ComposerDock({ children }) {\n * useKeyboardInset({ source: 'native' });\n * return (\n * <div\n * style={{\n * paddingBottom: 'calc(var(--mieweb-keyboard-inset, 0px) + 0.5rem)',\n * transition: 'padding-bottom 250ms ease',\n * }}\n * >\n * {children}\n * </div>\n * );\n * }\n * ```\n */\nexport function useKeyboardInset(\n options: UseKeyboardInsetOptions = {}\n): KeyboardInsetState {\n const { enabled = true, source = 'visual-viewport' } = options;\n const [state, setState] = useState<KeyboardInsetState>(CLOSED);\n\n useEffect(() => {\n if (!enabled || typeof window === 'undefined') return;\n const root = document.documentElement;\n\n const publish = (keyboardInset: number) => {\n const isKeyboardOpen = keyboardInset > 0;\n root.style.setProperty(KEYBOARD_INSET_VAR, `${keyboardInset}px`);\n root.toggleAttribute(KEYBOARD_OPEN_ATTRIBUTE, isKeyboardOpen);\n setState((previous) =>\n previous.keyboardInset === keyboardInset\n ? previous\n : { keyboardInset, isKeyboardOpen }\n );\n };\n\n const clear = () => {\n root.style.removeProperty(KEYBOARD_INSET_VAR);\n root.style.removeProperty(VISUAL_VIEWPORT_HEIGHT_VAR);\n root.style.removeProperty(VISUAL_VIEWPORT_OFFSET_TOP_VAR);\n root.removeAttribute(KEYBOARD_OPEN_ATTRIBUTE);\n root.removeAttribute(KEYBOARD_SOURCE_ATTRIBUTE);\n setState(CLOSED);\n };\n\n if (source === 'native') {\n const handleWillShow = (event: Event) => {\n publish(\n Math.max(\n 0,\n Math.round((event as NativeKeyboardEvent).keyboardHeight ?? 0)\n )\n );\n };\n const handleWillHide = () => publish(0);\n\n root.setAttribute(KEYBOARD_SOURCE_ATTRIBUTE, source);\n publish(0);\n window.addEventListener('keyboardWillShow', handleWillShow);\n window.addEventListener('keyboardWillHide', handleWillHide);\n\n return () => {\n window.removeEventListener('keyboardWillShow', handleWillShow);\n window.removeEventListener('keyboardWillHide', handleWillHide);\n clear();\n };\n }\n\n const viewport = window.visualViewport;\n if (!viewport) return;\n\n let frame = 0;\n\n const measure = () => {\n frame = 0;\n // Pinch-zoom shrinks the visual viewport too; that is not a keyboard.\n const zoomed = Math.abs(viewport.scale - 1) > 0.01;\n const keyboardInset = zoomed\n ? 0\n : Math.max(\n 0,\n Math.round(\n window.innerHeight - viewport.height - viewport.offsetTop\n )\n );\n\n root.style.setProperty(\n VISUAL_VIEWPORT_HEIGHT_VAR,\n `${zoomed ? window.innerHeight : Math.round(viewport.height)}px`\n );\n root.style.setProperty(\n VISUAL_VIEWPORT_OFFSET_TOP_VAR,\n `${zoomed ? 0 : Math.round(viewport.offsetTop)}px`\n );\n publish(keyboardInset);\n };\n\n // Coalesce the resize + scroll bursts iOS fires while the keyboard\n // animates into one write per frame.\n const schedule = () => {\n if (frame === 0) frame = requestAnimationFrame(measure);\n };\n\n root.setAttribute(KEYBOARD_SOURCE_ATTRIBUTE, source);\n measure();\n viewport.addEventListener('resize', schedule);\n viewport.addEventListener('scroll', schedule);\n\n return () => {\n cancelAnimationFrame(frame);\n viewport.removeEventListener('resize', schedule);\n viewport.removeEventListener('scroll', schedule);\n clear();\n };\n }, [enabled, source]);\n\n return state;\n}\n","import { useEffect, useCallback } from 'react';\n\n/**\n * Options for keyboard shortcut hook\n */\nexport interface KeyboardShortcutOptions {\n /** Whether the shortcut is currently enabled (default: true) */\n enabled?: boolean;\n /** Modifier keys required (default: none) */\n modifiers?: {\n ctrl?: boolean;\n shift?: boolean;\n alt?: boolean;\n meta?: boolean;\n };\n /** Whether to prevent default browser behavior (default: true) */\n preventDefault?: boolean;\n /** Whether to stop event propagation (default: false) */\n stopPropagation?: boolean;\n /** Element types to ignore when the shortcut is pressed (default: ['INPUT', 'TEXTAREA', 'SELECT']) */\n ignoreInputs?: boolean;\n}\n\n/**\n * Hook that triggers a callback when a specific keyboard shortcut is pressed.\n * Supports modifier keys (Ctrl, Shift, Alt, Meta) and ignores input fields by default.\n *\n * @param key - The key to listen for (e.g., 'k', 'Enter', 'Escape', '/')\n * @param callback - Function to call when the shortcut is pressed\n * @param options - Configuration options\n *\n * @example\n * ```tsx\n * // Cmd/Ctrl+K to open search\n * useKeyboardShortcut('k', openSearch, { modifiers: { meta: true, ctrl: true } });\n *\n * // Forward slash to focus search (ignoring when typing)\n * useKeyboardShortcut('/', focusSearch);\n *\n * // Escape to close modal\n * useKeyboardShortcut('Escape', closeModal);\n * ```\n */\nexport function useKeyboardShortcut(\n key: string,\n callback: (event: KeyboardEvent) => void,\n options: KeyboardShortcutOptions = {}\n): void {\n const {\n enabled = true,\n modifiers = {},\n preventDefault = true,\n stopPropagation = false,\n ignoreInputs = true,\n } = options;\n\n const handleKeyDown = useCallback(\n (event: KeyboardEvent) => {\n // Check if we should ignore input fields\n if (ignoreInputs) {\n const target = event.target as HTMLElement;\n const tagName = target.tagName.toUpperCase();\n if (\n tagName === 'INPUT' ||\n tagName === 'TEXTAREA' ||\n tagName === 'SELECT' ||\n target.isContentEditable\n ) {\n return;\n }\n }\n\n // Check modifiers\n const { ctrl, shift, alt, meta } = modifiers;\n\n // For Cmd+K style shortcuts, allow either meta (Mac) or ctrl (Windows/Linux)\n const metaOrCtrl = meta || ctrl;\n if (metaOrCtrl) {\n if (!(event.metaKey || event.ctrlKey)) return;\n } else {\n // If no meta/ctrl modifier specified, ensure neither is pressed\n if (event.metaKey || event.ctrlKey) return;\n }\n\n if (shift && !event.shiftKey) return;\n if (alt && !event.altKey) return;\n\n // Check key match (case-insensitive for letters)\n const eventKey =\n event.key.length === 1 ? event.key.toLowerCase() : event.key;\n const targetKey = key.length === 1 ? key.toLowerCase() : key;\n if (eventKey !== targetKey) return;\n\n if (preventDefault) {\n event.preventDefault();\n }\n if (stopPropagation) {\n event.stopPropagation();\n }\n\n callback(event);\n },\n [key, callback, modifiers, preventDefault, stopPropagation, ignoreInputs]\n );\n\n useEffect(() => {\n if (!enabled) return;\n\n document.addEventListener('keydown', handleKeyDown);\n return () => document.removeEventListener('keydown', handleKeyDown);\n }, [handleKeyDown, enabled]);\n}\n\n/**\n * Hook for the common Cmd/Ctrl+K shortcut pattern (command palette, search, etc.)\n *\n * @param callback - Function to call when Cmd/Ctrl+K is pressed\n * @param enabled - Whether the shortcut is active (default: true)\n *\n * @example\n * ```tsx\n * useCommandK(() => setIsOpen(true));\n * ```\n */\nexport function useCommandK(callback: () => void, enabled = true): void {\n useKeyboardShortcut('k', callback, {\n enabled,\n modifiers: { meta: true, ctrl: true },\n ignoreInputs: false, // Cmd+K should work even in inputs\n });\n}\n","import { useState, useEffect } from 'react';\n\n/**\n * Hook that tracks whether a media query matches.\n * Uses the native `matchMedia` API for efficient media query tracking.\n *\n * @param query - CSS media query string (e.g., '(min-width: 768px)')\n * @returns Boolean indicating whether the media query matches\n *\n * @example\n * ```tsx\n * function ResponsiveComponent() {\n * const isMobile = useMediaQuery('(max-width: 767px)');\n * const isTablet = useMediaQuery('(min-width: 768px) and (max-width: 1023px)');\n * const isDesktop = useMediaQuery('(min-width: 1024px)');\n *\n * return (\n * <div>\n * {isMobile && <MobileLayout />}\n * {isTablet && <TabletLayout />}\n * {isDesktop && <DesktopLayout />}\n * </div>\n * );\n * }\n * ```\n */\nexport function useMediaQuery(query: string): boolean {\n // Initialize with null to indicate SSR/initial state\n const [matches, setMatches] = useState<boolean>(() => {\n // Check if we're in a browser environment\n if (typeof window === 'undefined') return false;\n return window.matchMedia(query).matches;\n });\n\n useEffect(() => {\n if (typeof window === 'undefined') return;\n\n const mediaQuery = window.matchMedia(query);\n\n // Set initial value\n setMatches(mediaQuery.matches);\n\n // Handler for media query changes\n const handler = (event: MediaQueryListEvent) => {\n setMatches(event.matches);\n };\n\n // Modern browsers use addEventListener\n if (mediaQuery.addEventListener) {\n mediaQuery.addEventListener('change', handler);\n return () => mediaQuery.removeEventListener('change', handler);\n }\n\n // Fallback for older browsers\n mediaQuery.addListener(handler);\n return () => mediaQuery.removeListener(handler);\n }, [query]);\n\n return matches;\n}\n\n/**\n * Preset breakpoint hooks following common responsive design patterns\n */\n\n/** Returns true when viewport is smaller than 640px (mobile) */\nexport function useIsMobile(): boolean {\n return useMediaQuery('(max-width: 639px)');\n}\n\n/** Returns true when viewport is 640px-767px (large mobile / small tablet) */\nexport function useIsSmallTablet(): boolean {\n return useMediaQuery('(min-width: 640px) and (max-width: 767px)');\n}\n\n/** Returns true when viewport is 768px-1023px (tablet) */\nexport function useIsTablet(): boolean {\n return useMediaQuery('(min-width: 768px) and (max-width: 1023px)');\n}\n\n/** Returns true when viewport is 1024px or larger (desktop) */\nexport function useIsDesktop(): boolean {\n return useMediaQuery('(min-width: 1024px)');\n}\n\n/** Returns true when viewport is 1280px or larger (large desktop) */\nexport function useIsLargeDesktop(): boolean {\n return useMediaQuery('(min-width: 1280px)');\n}\n\n/** Returns true when viewport is smaller than 1024px (mobile/tablet) */\nexport function useIsMobileOrTablet(): boolean {\n return useMediaQuery('(max-width: 1023px)');\n}\n","'use client';\n\nimport * as React from 'react';\n\nexport interface OrderPersistenceAdapter {\n /** Previously saved order, or null when there is none */\n load: () => string[] | null | Promise<string[] | null>;\n save: (order: string[]) => void | Promise<void>;\n}\n\nexport interface UseOrderPersistenceOptions extends OrderPersistenceAdapter {\n /** Delay before `save` runs; rapid changes collapse into one save. */\n debounceMs?: number;\n /** Called when `load` or `save` throws or rejects */\n onError?: (error: unknown) => void;\n}\n\nexport interface UseOrderPersistenceReturn {\n /** Saved order merged with the current `ids` */\n order: string[];\n /** Update the order immediately and schedule a debounced save */\n setOrder: (order: string[]) => void;\n}\n\n/** Keep saved ids that still exist, then append new ids in their given order. */\nexport function mergeOrder(saved: string[] | null, ids: string[]): string[] {\n if (!saved) return ids;\n const known = new Set(ids);\n const kept = [...new Set(saved)].filter((id) => known.has(id));\n const keptSet = new Set(kept);\n return [...kept, ...ids.filter((id) => !keptSet.has(id))];\n}\n\n/**\n * Persist a user's item order (sections, widgets, list rows) through any\n * storage. Updates are optimistic, saves are debounced and a pending save is\n * flushed on unmount and on `pagehide` (navigation, refresh, tab close). On\n * `pagehide` only work `save` starts synchronously is guaranteed — use\n * localStorage or `navigator.sendBeacon` there; an async `fetch` may be cut\n * off. `load` runs once after mount, so server and first client render use\n * `ids` order.\n *\n * @example\n * ```tsx\n * const adapter = React.useMemo(() => localStorageOrderAdapter('clinic-sections'), []);\n * const { order, setOrder } = useOrderPersistence(sectionIds, adapter);\n * const drag = useDragReorder({ ids: order, onReorder: setOrder });\n * ```\n */\nexport function useOrderPersistence(\n ids: string[],\n { load, save, debounceMs = 500, onError }: UseOrderPersistenceOptions\n): UseOrderPersistenceReturn {\n const [saved, setSaved] = React.useState<string[] | null>(null);\n const callbacks = React.useRef({ load, save, onError });\n callbacks.current = { load, save, onError };\n const pending = React.useRef<string[] | null>(null);\n const timer = React.useRef<ReturnType<typeof setTimeout> | undefined>(\n undefined\n );\n const touched = React.useRef(false);\n // In-flight save count: `force` (pagehide) saves may overlap a normal one,\n // so queued flushes resume only after every save settles.\n const saving = React.useRef(0);\n // When saves overlap, their completion order is unknown and a stale write\n // could land last; the newest payload is saved once more after all settle.\n const lastStarted = React.useRef<string[] | null>(null);\n const overlapped = React.useRef(false);\n\n // One save at a time; changes made meanwhile coalesce into the next save.\n // `force` skips the queue for page unloads, where a queued save never runs.\n const flush = React.useCallback(function flushPending(force = false) {\n clearTimeout(timer.current);\n const next = pending.current;\n if (!next || (saving.current > 0 && !force)) return;\n pending.current = null;\n if (saving.current > 0) overlapped.current = true;\n saving.current += 1;\n lastStarted.current = next;\n const { save: run, onError: fail } = callbacks.current;\n let result: void | Promise<void>;\n try {\n result = run(next);\n } catch (e) {\n result = Promise.reject(e);\n }\n void Promise.resolve(result)\n .catch((e: unknown) => fail?.(e))\n .finally(() => {\n saving.current -= 1;\n if (saving.current > 0) return;\n if (overlapped.current) {\n overlapped.current = false;\n pending.current ??= lastStarted.current;\n }\n flushPending();\n });\n }, []);\n\n React.useEffect(() => {\n let active = true;\n const { load: read, onError: fail } = callbacks.current;\n Promise.resolve()\n .then(read)\n .then((value) => {\n // A change made while loading wins over the stored order\n if (active && value && !touched.current) setSaved(value);\n })\n .catch((e: unknown) => fail?.(e));\n return () => {\n active = false;\n flush();\n };\n }, [flush]);\n\n React.useEffect(() => {\n const onPageHide = () => flush(true);\n window.addEventListener('pagehide', onPageHide);\n return () => window.removeEventListener('pagehide', onPageHide);\n }, [flush]);\n\n const setOrder = React.useCallback(\n (next: string[]) => {\n touched.current = true;\n setSaved(next);\n pending.current = next;\n clearTimeout(timer.current);\n timer.current = setTimeout(flush, debounceMs);\n },\n [debounceMs, flush]\n );\n\n const order = React.useMemo(() => mergeOrder(saved, ids), [saved, ids]);\n return { order, setOrder };\n}\n\n/** `useOrderPersistence` adapter backed by `localStorage` (no-op on the server or when storage is blocked). */\nexport function localStorageOrderAdapter(key: string): OrderPersistenceAdapter {\n return {\n load: () => {\n try {\n if (typeof window === 'undefined') return null;\n const raw: unknown = JSON.parse(\n window.localStorage.getItem(key) ?? 'null'\n );\n return Array.isArray(raw) && raw.every((v) => typeof v === 'string')\n ? (raw as string[])\n : null;\n } catch {\n return null;\n }\n },\n save: (order) => {\n try {\n if (typeof window !== 'undefined')\n window.localStorage.setItem(key, JSON.stringify(order));\n } catch {\n // Quota exceeded or storage disabled: keep the in-memory order\n }\n },\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/hooks/useKeyboardInset.ts","../src/hooks/useKeyboardShortcut.ts","../src/hooks/useMediaQuery.ts","../src/hooks/useOrderPersistence.ts"],"names":["useEffect","useState"],"mappings":"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","file":"chunk-YG7BR76Y.js","sourcesContent":["import { useEffect, useState } from 'react';\n\n/** CSS custom properties written to `<html>` while the hook is mounted. */\nexport const KEYBOARD_INSET_VAR = '--mieweb-keyboard-inset';\nexport const VISUAL_VIEWPORT_HEIGHT_VAR = '--mieweb-visual-viewport-height';\nexport const VISUAL_VIEWPORT_OFFSET_TOP_VAR =\n '--mieweb-visual-viewport-offset-top';\n/** Boolean attribute set on `<html>` while an on-screen keyboard is open. */\nexport const KEYBOARD_OPEN_ATTRIBUTE = 'data-keyboard-open';\n/** Attribute on `<html>` naming the active {@link KeyboardInsetSource}. */\nexport const KEYBOARD_SOURCE_ATTRIBUTE = 'data-keyboard-source';\n\n/**\n * Where keyboard metrics come from.\n *\n * - `'visual-viewport'` — the Visual Viewport API. Works in any browser, but\n * iOS only reports the keyboard once its slide-in animation has finished.\n * - `'native'` — `keyboardWillShow` / `keyboardWillHide` window events with a\n * `keyboardHeight` property, as fired by `cordova-plugin-ionic-keyboard` and\n * Capacitor's Keyboard plugin. They arrive *before* the keyboard animates,\n * so layout can move in step with it.\n */\nexport type KeyboardInsetSource = 'visual-viewport' | 'native';\n\nexport interface KeyboardInsetState {\n /** Pixels of the layout viewport covered by the on-screen keyboard. */\n keyboardInset: number;\n /** Whether an on-screen keyboard currently covers part of the page. */\n isKeyboardOpen: boolean;\n}\n\nexport interface UseKeyboardInsetOptions {\n /** Track the keyboard; `false` stops tracking and clears the CSS variables. @default true */\n enabled?: boolean;\n /** Where keyboard metrics come from. @default 'visual-viewport' */\n source?: KeyboardInsetSource;\n}\n\ninterface NativeKeyboardEvent extends Event {\n keyboardHeight?: number;\n}\n\nconst CLOSED: KeyboardInsetState = { keyboardInset: 0, isKeyboardOpen: false };\n\n/**\n * Tracks the on-screen keyboard and publishes it on `<html>`.\n *\n * ### `source: 'native'` (Cordova / Capacitor apps)\n *\n * Listens for the native keyboard plugin's `keyboardWillShow` /\n * `keyboardWillHide` window events. Configure the plugin so the WebView is\n * neither resized nor panned (for `cordova-plugin-ionic-keyboard`:\n * `KeyboardResize=false`), keep the app shell at full height, and move only\n * the composer dock by `--mieweb-keyboard-inset` with a short transition\n * (~250ms) so it rides up with the keyboard. Only `--mieweb-keyboard-inset`,\n * `data-keyboard-open` and `data-keyboard-source` are published; the\n * visual-viewport variables stay unset.\n *\n * ### `source: 'visual-viewport'` (browsers, default)\n *\n * Uses the Visual Viewport API.\n *\n * iOS Safari and WKWebView (Cordova) ignore\n * `interactive-widget=resizes-content`, and `dvh` does not shrink for the\n * keyboard: the page keeps its full height and iOS pans the visual viewport\n * to reveal the focused input, pushing headers off-screen. This hook\n * measures what is actually visible and publishes it on `<html>` so an app\n * shell can follow the visible area instead:\n *\n * - `--mieweb-keyboard-inset` — keyboard height in px (0 when closed)\n * - `--mieweb-visual-viewport-height` — visible height in px\n * - `--mieweb-visual-viewport-offset-top` — how far iOS panned the page\n * - `data-keyboard-open` — present while the keyboard is open\n *\n * On browsers that already resize the layout for the keyboard (Chrome on\n * Android with `interactive-widget=resizes-content`) the inset stays 0 and\n * the variables simply mirror the layout viewport.\n *\n * iOS reports the visual viewport only after the keyboard has finished\n * animating, so a shell sized this way snaps into place once the keyboard\n * lands. Prefer `source: 'native'` wherever a native keyboard plugin exists.\n *\n * Mount it once, near the app root.\n *\n * @example\n * ```tsx\n * function AppShell({ children }) {\n * useKeyboardInset();\n * return (\n * <div\n * style={{\n * height: 'var(--mieweb-visual-viewport-height, 100dvh)',\n * transform: 'translateY(var(--mieweb-visual-viewport-offset-top, 0px))',\n * }}\n * >\n * {children}\n * </div>\n * );\n * }\n * ```\n *\n * @example Native app (keyboard plugin with WebView resizing disabled)\n * ```tsx\n * function ComposerDock({ children }) {\n * useKeyboardInset({ source: 'native' });\n * return (\n * <div\n * style={{\n * paddingBottom: 'calc(var(--mieweb-keyboard-inset, 0px) + 0.5rem)',\n * transition: 'padding-bottom 250ms ease',\n * }}\n * >\n * {children}\n * </div>\n * );\n * }\n * ```\n */\nexport function useKeyboardInset(\n options: UseKeyboardInsetOptions = {}\n): KeyboardInsetState {\n const { enabled = true, source = 'visual-viewport' } = options;\n const [state, setState] = useState<KeyboardInsetState>(CLOSED);\n\n useEffect(() => {\n if (!enabled || typeof window === 'undefined') return;\n const root = document.documentElement;\n\n const publish = (keyboardInset: number) => {\n const isKeyboardOpen = keyboardInset > 0;\n root.style.setProperty(KEYBOARD_INSET_VAR, `${keyboardInset}px`);\n root.toggleAttribute(KEYBOARD_OPEN_ATTRIBUTE, isKeyboardOpen);\n setState((previous) =>\n previous.keyboardInset === keyboardInset\n ? previous\n : { keyboardInset, isKeyboardOpen }\n );\n };\n\n const clear = () => {\n root.style.removeProperty(KEYBOARD_INSET_VAR);\n root.style.removeProperty(VISUAL_VIEWPORT_HEIGHT_VAR);\n root.style.removeProperty(VISUAL_VIEWPORT_OFFSET_TOP_VAR);\n root.removeAttribute(KEYBOARD_OPEN_ATTRIBUTE);\n root.removeAttribute(KEYBOARD_SOURCE_ATTRIBUTE);\n setState(CLOSED);\n };\n\n if (source === 'native') {\n const handleWillShow = (event: Event) => {\n publish(\n Math.max(\n 0,\n Math.round((event as NativeKeyboardEvent).keyboardHeight ?? 0)\n )\n );\n };\n const handleWillHide = () => publish(0);\n\n root.setAttribute(KEYBOARD_SOURCE_ATTRIBUTE, source);\n publish(0);\n window.addEventListener('keyboardWillShow', handleWillShow);\n window.addEventListener('keyboardWillHide', handleWillHide);\n\n return () => {\n window.removeEventListener('keyboardWillShow', handleWillShow);\n window.removeEventListener('keyboardWillHide', handleWillHide);\n clear();\n };\n }\n\n const viewport = window.visualViewport;\n if (!viewport) return;\n\n let frame = 0;\n\n const measure = () => {\n frame = 0;\n // Pinch-zoom shrinks the visual viewport too; that is not a keyboard.\n const zoomed = Math.abs(viewport.scale - 1) > 0.01;\n const keyboardInset = zoomed\n ? 0\n : Math.max(\n 0,\n Math.round(\n window.innerHeight - viewport.height - viewport.offsetTop\n )\n );\n\n root.style.setProperty(\n VISUAL_VIEWPORT_HEIGHT_VAR,\n `${zoomed ? window.innerHeight : Math.round(viewport.height)}px`\n );\n root.style.setProperty(\n VISUAL_VIEWPORT_OFFSET_TOP_VAR,\n `${zoomed ? 0 : Math.round(viewport.offsetTop)}px`\n );\n publish(keyboardInset);\n };\n\n // Coalesce the resize + scroll bursts iOS fires while the keyboard\n // animates into one write per frame.\n const schedule = () => {\n if (frame === 0) frame = requestAnimationFrame(measure);\n };\n\n root.setAttribute(KEYBOARD_SOURCE_ATTRIBUTE, source);\n measure();\n viewport.addEventListener('resize', schedule);\n viewport.addEventListener('scroll', schedule);\n\n return () => {\n cancelAnimationFrame(frame);\n viewport.removeEventListener('resize', schedule);\n viewport.removeEventListener('scroll', schedule);\n clear();\n };\n }, [enabled, source]);\n\n return state;\n}\n","import { useEffect, useCallback } from 'react';\n\n/**\n * Options for keyboard shortcut hook\n */\nexport interface KeyboardShortcutOptions {\n /** Whether the shortcut is currently enabled (default: true) */\n enabled?: boolean;\n /** Modifier keys required (default: none) */\n modifiers?: {\n ctrl?: boolean;\n shift?: boolean;\n alt?: boolean;\n meta?: boolean;\n };\n /** Whether to prevent default browser behavior (default: true) */\n preventDefault?: boolean;\n /** Whether to stop event propagation (default: false) */\n stopPropagation?: boolean;\n /** Element types to ignore when the shortcut is pressed (default: ['INPUT', 'TEXTAREA', 'SELECT']) */\n ignoreInputs?: boolean;\n}\n\n/**\n * Hook that triggers a callback when a specific keyboard shortcut is pressed.\n * Supports modifier keys (Ctrl, Shift, Alt, Meta) and ignores input fields by default.\n *\n * @param key - The key to listen for (e.g., 'k', 'Enter', 'Escape', '/')\n * @param callback - Function to call when the shortcut is pressed\n * @param options - Configuration options\n *\n * @example\n * ```tsx\n * // Cmd/Ctrl+K to open search\n * useKeyboardShortcut('k', openSearch, { modifiers: { meta: true, ctrl: true } });\n *\n * // Forward slash to focus search (ignoring when typing)\n * useKeyboardShortcut('/', focusSearch);\n *\n * // Escape to close modal\n * useKeyboardShortcut('Escape', closeModal);\n * ```\n */\nexport function useKeyboardShortcut(\n key: string,\n callback: (event: KeyboardEvent) => void,\n options: KeyboardShortcutOptions = {}\n): void {\n const {\n enabled = true,\n modifiers = {},\n preventDefault = true,\n stopPropagation = false,\n ignoreInputs = true,\n } = options;\n\n const handleKeyDown = useCallback(\n (event: KeyboardEvent) => {\n // Check if we should ignore input fields\n if (ignoreInputs) {\n const target = event.target as HTMLElement;\n const tagName = target.tagName.toUpperCase();\n if (\n tagName === 'INPUT' ||\n tagName === 'TEXTAREA' ||\n tagName === 'SELECT' ||\n target.isContentEditable\n ) {\n return;\n }\n }\n\n // Check modifiers\n const { ctrl, shift, alt, meta } = modifiers;\n\n // For Cmd+K style shortcuts, allow either meta (Mac) or ctrl (Windows/Linux)\n const metaOrCtrl = meta || ctrl;\n if (metaOrCtrl) {\n if (!(event.metaKey || event.ctrlKey)) return;\n } else {\n // If no meta/ctrl modifier specified, ensure neither is pressed\n if (event.metaKey || event.ctrlKey) return;\n }\n\n if (shift && !event.shiftKey) return;\n if (alt && !event.altKey) return;\n\n // Check key match (case-insensitive for letters)\n const eventKey =\n event.key.length === 1 ? event.key.toLowerCase() : event.key;\n const targetKey = key.length === 1 ? key.toLowerCase() : key;\n if (eventKey !== targetKey) return;\n\n if (preventDefault) {\n event.preventDefault();\n }\n if (stopPropagation) {\n event.stopPropagation();\n }\n\n callback(event);\n },\n [key, callback, modifiers, preventDefault, stopPropagation, ignoreInputs]\n );\n\n useEffect(() => {\n if (!enabled) return;\n\n document.addEventListener('keydown', handleKeyDown);\n return () => document.removeEventListener('keydown', handleKeyDown);\n }, [handleKeyDown, enabled]);\n}\n\n/**\n * Hook for the common Cmd/Ctrl+K shortcut pattern (command palette, search, etc.)\n *\n * @param callback - Function to call when Cmd/Ctrl+K is pressed\n * @param enabled - Whether the shortcut is active (default: true)\n *\n * @example\n * ```tsx\n * useCommandK(() => setIsOpen(true));\n * ```\n */\nexport function useCommandK(callback: () => void, enabled = true): void {\n useKeyboardShortcut('k', callback, {\n enabled,\n modifiers: { meta: true, ctrl: true },\n ignoreInputs: false, // Cmd+K should work even in inputs\n });\n}\n","import { useState, useEffect } from 'react';\n\n/**\n * Hook that tracks whether a media query matches.\n * Uses the native `matchMedia` API for efficient media query tracking.\n *\n * @param query - CSS media query string (e.g., '(min-width: 768px)')\n * @returns Boolean indicating whether the media query matches\n *\n * @example\n * ```tsx\n * function ResponsiveComponent() {\n * const isMobile = useMediaQuery('(max-width: 767px)');\n * const isTablet = useMediaQuery('(min-width: 768px) and (max-width: 1023px)');\n * const isDesktop = useMediaQuery('(min-width: 1024px)');\n *\n * return (\n * <div>\n * {isMobile && <MobileLayout />}\n * {isTablet && <TabletLayout />}\n * {isDesktop && <DesktopLayout />}\n * </div>\n * );\n * }\n * ```\n */\nexport function useMediaQuery(query: string): boolean {\n // Initialize with null to indicate SSR/initial state\n const [matches, setMatches] = useState<boolean>(() => {\n // Check if we're in a browser environment\n if (typeof window === 'undefined') return false;\n return window.matchMedia(query).matches;\n });\n\n useEffect(() => {\n if (typeof window === 'undefined') return;\n\n const mediaQuery = window.matchMedia(query);\n\n // Set initial value\n setMatches(mediaQuery.matches);\n\n // Handler for media query changes\n const handler = (event: MediaQueryListEvent) => {\n setMatches(event.matches);\n };\n\n // Modern browsers use addEventListener\n if (mediaQuery.addEventListener) {\n mediaQuery.addEventListener('change', handler);\n return () => mediaQuery.removeEventListener('change', handler);\n }\n\n // Fallback for older browsers\n mediaQuery.addListener(handler);\n return () => mediaQuery.removeListener(handler);\n }, [query]);\n\n return matches;\n}\n\n/**\n * Preset breakpoint hooks following common responsive design patterns\n */\n\n/** Returns true when viewport is smaller than 640px (mobile) */\nexport function useIsMobile(): boolean {\n return useMediaQuery('(max-width: 639px)');\n}\n\n/** Returns true when viewport is 640px-767px (large mobile / small tablet) */\nexport function useIsSmallTablet(): boolean {\n return useMediaQuery('(min-width: 640px) and (max-width: 767px)');\n}\n\n/** Returns true when viewport is 768px-1023px (tablet) */\nexport function useIsTablet(): boolean {\n return useMediaQuery('(min-width: 768px) and (max-width: 1023px)');\n}\n\n/** Returns true when viewport is 1024px or larger (desktop) */\nexport function useIsDesktop(): boolean {\n return useMediaQuery('(min-width: 1024px)');\n}\n\n/** Returns true when viewport is 1280px or larger (large desktop) */\nexport function useIsLargeDesktop(): boolean {\n return useMediaQuery('(min-width: 1280px)');\n}\n\n/** Returns true when viewport is smaller than 1024px (mobile/tablet) */\nexport function useIsMobileOrTablet(): boolean {\n return useMediaQuery('(max-width: 1023px)');\n}\n","'use client';\n\nimport * as React from 'react';\n\nexport interface OrderPersistenceAdapter {\n /** Previously saved order, or null when there is none */\n load: () => string[] | null | Promise<string[] | null>;\n save: (order: string[]) => void | Promise<void>;\n}\n\nexport interface UseOrderPersistenceOptions extends OrderPersistenceAdapter {\n /** Delay before `save` runs; rapid changes collapse into one save. */\n debounceMs?: number;\n /** Called when `load` or `save` throws or rejects */\n onError?: (error: unknown) => void;\n}\n\nexport interface UseOrderPersistenceReturn {\n /** Saved order merged with the current `ids` */\n order: string[];\n /** Update the order immediately and schedule a debounced save */\n setOrder: (order: string[]) => void;\n}\n\n/** Keep saved ids that still exist, then append new ids in their given order. */\nexport function mergeOrder(saved: string[] | null, ids: string[]): string[] {\n if (!saved) return ids;\n const known = new Set(ids);\n const kept = [...new Set(saved)].filter((id) => known.has(id));\n const keptSet = new Set(kept);\n return [...kept, ...ids.filter((id) => !keptSet.has(id))];\n}\n\n/**\n * Persist a user's item order (sections, widgets, list rows) through any\n * storage. Updates are optimistic, saves are debounced and a pending save is\n * flushed on unmount and on `pagehide` (navigation, refresh, tab close). On\n * `pagehide` only work `save` starts synchronously is guaranteed — use\n * localStorage or `navigator.sendBeacon` there; an async `fetch` may be cut\n * off. `load` runs once after mount, so server and first client render use\n * `ids` order.\n *\n * @example\n * ```tsx\n * const adapter = React.useMemo(() => localStorageOrderAdapter('clinic-sections'), []);\n * const { order, setOrder } = useOrderPersistence(sectionIds, adapter);\n * const drag = useDragReorder({ ids: order, onReorder: setOrder });\n * ```\n */\nexport function useOrderPersistence(\n ids: string[],\n { load, save, debounceMs = 500, onError }: UseOrderPersistenceOptions\n): UseOrderPersistenceReturn {\n const [saved, setSaved] = React.useState<string[] | null>(null);\n const callbacks = React.useRef({ load, save, onError });\n callbacks.current = { load, save, onError };\n const pending = React.useRef<string[] | null>(null);\n const timer = React.useRef<ReturnType<typeof setTimeout> | undefined>(\n undefined\n );\n const touched = React.useRef(false);\n // In-flight save count: `force` (pagehide) saves may overlap a normal one,\n // so queued flushes resume only after every save settles.\n const saving = React.useRef(0);\n // When saves overlap, their completion order is unknown and a stale write\n // could land last; the newest payload is saved once more after all settle.\n const lastStarted = React.useRef<string[] | null>(null);\n const overlapped = React.useRef(false);\n\n // One save at a time; changes made meanwhile coalesce into the next save.\n // `force` skips the queue for page unloads, where a queued save never runs.\n const flush = React.useCallback(function flushPending(force = false) {\n clearTimeout(timer.current);\n const next = pending.current;\n if (!next || (saving.current > 0 && !force)) return;\n pending.current = null;\n if (saving.current > 0) overlapped.current = true;\n saving.current += 1;\n lastStarted.current = next;\n const { save: run, onError: fail } = callbacks.current;\n let result: void | Promise<void>;\n try {\n result = run(next);\n } catch (e) {\n result = Promise.reject(e);\n }\n void Promise.resolve(result)\n .catch((e: unknown) => fail?.(e))\n .finally(() => {\n saving.current -= 1;\n if (saving.current > 0) return;\n if (overlapped.current) {\n overlapped.current = false;\n pending.current ??= lastStarted.current;\n }\n flushPending();\n });\n }, []);\n\n React.useEffect(() => {\n let active = true;\n const { load: read, onError: fail } = callbacks.current;\n Promise.resolve()\n .then(read)\n .then((value) => {\n // A change made while loading wins over the stored order\n if (active && value && !touched.current) setSaved(value);\n })\n .catch((e: unknown) => fail?.(e));\n return () => {\n active = false;\n flush();\n };\n }, [flush]);\n\n React.useEffect(() => {\n const onPageHide = () => flush(true);\n window.addEventListener('pagehide', onPageHide);\n return () => window.removeEventListener('pagehide', onPageHide);\n }, [flush]);\n\n const setOrder = React.useCallback(\n (next: string[]) => {\n touched.current = true;\n setSaved(next);\n pending.current = next;\n clearTimeout(timer.current);\n timer.current = setTimeout(flush, debounceMs);\n },\n [debounceMs, flush]\n );\n\n const order = React.useMemo(() => mergeOrder(saved, ids), [saved, ids]);\n return { order, setOrder };\n}\n\n/** `useOrderPersistence` adapter backed by `localStorage` (no-op on the server or when storage is blocked). */\nexport function localStorageOrderAdapter(key: string): OrderPersistenceAdapter {\n return {\n load: () => {\n try {\n if (typeof window === 'undefined') return null;\n const raw: unknown = JSON.parse(\n window.localStorage.getItem(key) ?? 'null'\n );\n return Array.isArray(raw) && raw.every((v) => typeof v === 'string')\n ? (raw as string[])\n : null;\n } catch {\n return null;\n }\n },\n save: (order) => {\n try {\n if (typeof window !== 'undefined')\n window.localStorage.setItem(key, JSON.stringify(order));\n } catch {\n // Quota exceeded or storage disabled: keep the in-memory order\n }\n },\n };\n}\n"]}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { useState, useEffect } from 'react';
|
|
2
|
+
|
|
3
|
+
// src/hooks/useDirection.ts
|
|
4
|
+
var RTL_LOCALES = ["ar", "he", "fa", "ur"];
|
|
5
|
+
function isRtlLocale(locale) {
|
|
6
|
+
const lang = locale.toLowerCase().split(/[-_]/)[0];
|
|
7
|
+
return RTL_LOCALES.includes(lang);
|
|
8
|
+
}
|
|
9
|
+
function resolveDirection(element) {
|
|
10
|
+
if (typeof document === "undefined") return "ltr";
|
|
11
|
+
const target = element ?? document.documentElement;
|
|
12
|
+
return getComputedStyle(target).direction === "rtl" ? "rtl" : "ltr";
|
|
13
|
+
}
|
|
14
|
+
function isRtl(element) {
|
|
15
|
+
return resolveDirection(element) === "rtl";
|
|
16
|
+
}
|
|
17
|
+
function useDirection(elementRef) {
|
|
18
|
+
const [direction, setDirection] = useState(
|
|
19
|
+
() => resolveDirection(elementRef?.current)
|
|
20
|
+
);
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
const update = () => setDirection(resolveDirection(elementRef?.current));
|
|
23
|
+
update();
|
|
24
|
+
const observer = new MutationObserver(update);
|
|
25
|
+
observer.observe(document.documentElement, {
|
|
26
|
+
attributes: true,
|
|
27
|
+
attributeFilter: ["dir"],
|
|
28
|
+
subtree: true
|
|
29
|
+
});
|
|
30
|
+
return () => observer.disconnect();
|
|
31
|
+
}, [elementRef]);
|
|
32
|
+
const currentElement = elementRef?.current ?? null;
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
setDirection(resolveDirection(currentElement));
|
|
35
|
+
}, [currentElement]);
|
|
36
|
+
return direction;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export { RTL_LOCALES, isRtl, isRtlLocale, useDirection };
|
|
40
|
+
//# sourceMappingURL=chunk-YVKBXNPK.js.map
|
|
41
|
+
//# sourceMappingURL=chunk-YVKBXNPK.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/hooks/useDirection.ts"],"names":[],"mappings":";;;AAMO,IAAM,WAAA,GAAc,CAAC,IAAA,EAAM,IAAA,EAAM,MAAM,IAAI;AAM3C,SAAS,YAAY,MAAA,EAAyB;AACnD,EAAA,MAAM,OAAO,MAAA,CAAO,WAAA,GAAc,KAAA,CAAM,MAAM,EAAE,CAAC,CAAA;AACjD,EAAA,OAAQ,WAAA,CAAkC,SAAS,IAAI,CAAA;AACzD;AAEA,SAAS,iBAAiB,OAAA,EAAqC;AAC7D,EAAA,IAAI,OAAO,QAAA,KAAa,WAAA,EAAa,OAAO,KAAA;AAC5C,EAAA,MAAM,MAAA,GAAS,WAAW,QAAA,CAAS,eAAA;AACnC,EAAA,OAAO,gBAAA,CAAiB,MAAM,CAAA,CAAE,SAAA,KAAc,QAAQ,KAAA,GAAQ,KAAA;AAChE;AAaO,SAAS,MAAM,OAAA,EAAmC;AACvD,EAAA,OAAO,gBAAA,CAAiB,OAAO,CAAA,KAAM,KAAA;AACvC;AAoBO,SAAS,aACd,UAAA,EACW;AACX,EAAA,MAAM,CAAC,SAAA,EAAW,YAAY,CAAA,GAAI,QAAA;AAAA,IAAoB,MACpD,gBAAA,CAAiB,UAAA,EAAY,OAAO;AAAA,GACtC;AAEA,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,MAAM,SAAS,MAAM,YAAA,CAAa,gBAAA,CAAiB,UAAA,EAAY,OAAO,CAAC,CAAA;AACvE,IAAA,MAAA,EAAO;AAEP,IAAA,MAAM,QAAA,GAAW,IAAI,gBAAA,CAAiB,MAAM,CAAA;AAC5C,IAAA,QAAA,CAAS,OAAA,CAAQ,SAAS,eAAA,EAAiB;AAAA,MACzC,UAAA,EAAY,IAAA;AAAA,MACZ,eAAA,EAAiB,CAAC,KAAK,CAAA;AAAA,MACvB,OAAA,EAAS;AAAA,KACV,CAAA;AACD,IAAA,OAAO,MAAM,SAAS,UAAA,EAAW;AAAA,EACnC,CAAA,EAAG,CAAC,UAAU,CAAC,CAAA;AAKf,EAAA,MAAM,cAAA,GAAiB,YAAY,OAAA,IAAW,IAAA;AAC9C,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,YAAA,CAAa,gBAAA,CAAiB,cAAc,CAAC,CAAA;AAAA,EAC/C,CAAA,EAAG,CAAC,cAAc,CAAC,CAAA;AAEnB,EAAA,OAAO,SAAA;AACT","file":"chunk-YVKBXNPK.js","sourcesContent":["import { useState, useEffect } from 'react';\nimport type { RefObject } from 'react';\n\nexport type Direction = 'ltr' | 'rtl';\n\n/** Locales whose primary script reads right-to-left. */\nexport const RTL_LOCALES = ['ar', 'he', 'fa', 'ur'] as const;\n\n/**\n * Returns true when the given locale (e.g. `ar`, `ar-SA`, `he_IL`) uses a\n * right-to-left script.\n */\nexport function isRtlLocale(locale: string): boolean {\n const lang = locale.toLowerCase().split(/[-_]/)[0];\n return (RTL_LOCALES as readonly string[]).includes(lang);\n}\n\nfunction resolveDirection(element?: Element | null): Direction {\n if (typeof document === 'undefined') return 'ltr';\n const target = element ?? document.documentElement;\n return getComputedStyle(target).direction === 'rtl' ? 'rtl' : 'ltr';\n}\n\n/**\n * Non-hook check for event handlers: true when the element currently renders\n * right-to-left. Use inside `onKeyDown` to invert ArrowLeft/ArrowRight so\n * horizontal arrows follow the *visual* direction:\n *\n * ```ts\n * const rtl = isRtl(event.currentTarget);\n * ```\n *\n * Prefer `useDirection()` when the direction drives rendering.\n */\nexport function isRtl(element?: Element | null): boolean {\n return resolveDirection(element) === 'rtl';\n}\n\n/**\n * Hook returning the effective text direction (`ltr` | `rtl`) inherited from\n * the nearest `dir` attribute — zero setup for consumers who set\n * `<html dir=\"rtl\">`.\n *\n * Use for JS-level direction logic that CSS logical properties can't cover:\n * inverting ArrowLeft/ArrowRight keyboard handling, popover/toast placement,\n * drag math, etc.\n *\n * @param elementRef - Optional ref; when provided, direction is resolved from\n * that element (respecting any local `dir` override) instead of `<html>`.\n *\n * @example\n * ```tsx\n * const direction = useDirection();\n * const nextKey = direction === 'rtl' ? 'ArrowLeft' : 'ArrowRight';\n * ```\n */\nexport function useDirection(\n elementRef?: RefObject<HTMLElement | null>\n): Direction {\n const [direction, setDirection] = useState<Direction>(() =>\n resolveDirection(elementRef?.current)\n );\n\n useEffect(() => {\n const update = () => setDirection(resolveDirection(elementRef?.current));\n update();\n // dir changes are rare; observing the whole tree for just this attribute is cheap.\n const observer = new MutationObserver(update);\n observer.observe(document.documentElement, {\n attributes: true,\n attributeFilter: ['dir'],\n subtree: true,\n });\n return () => observer.disconnect();\n }, [elementRef]);\n\n // Re-sync when a re-render points elementRef.current at a different node:\n // the MutationObserver only fires on `dir` attribute mutations, so a swapped\n // ref target with a different inherited direction would otherwise go stale.\n const currentElement = elementRef?.current ?? null;\n useEffect(() => {\n setDirection(resolveDirection(currentElement));\n }, [currentElement]);\n\n return direction;\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { notifyComposerMigrationOnce, DragDropZone, ChatComposer, DEFAULT_MAX_FILE_SIZE, DEFAULT_ACCEPTED_FILE_TYPES, CameraButton } from './chunk-
|
|
1
|
+
import { notifyComposerMigrationOnce, DragDropZone, ChatComposer, DEFAULT_MAX_FILE_SIZE, DEFAULT_ACCEPTED_FILE_TYPES, CameraButton } from './chunk-TJ4GWQPM.js';
|
|
2
2
|
import { cn } from './chunk-TQONQSJO.js';
|
|
3
3
|
import * as React from 'react';
|
|
4
4
|
import { cva } from 'class-variance-authority';
|
|
@@ -2043,5 +2043,5 @@ function MessagingSplitView({
|
|
|
2043
2043
|
MessagingSplitView.displayName = "MessagingSplitView";
|
|
2044
2044
|
|
|
2045
2045
|
export { AttachmentPreview, ConversationHeader, ConversationListItem, ConversationListSkeleton, DateSeparator, EmptyState, LightboxModal, LoadMoreButton, MessageBubble, MessageList, MessageStatusIcon, MessageThread, MessagingSplitView, ReadReceiptIndicator, SkeletonMessage, TypingIndicator, bubbleVariants, formatDateLabel, formatFileSize, formatLastSeen, getConversationSubtitle, getConversationTitle, groupMessagesByDate, headerVariants, isSameSenderGroup, useMessageScroll, useMessages, useReadReceipts, useTypingEmulation, useTypingIndicator };
|
|
2046
|
-
//# sourceMappingURL=chunk-
|
|
2047
|
-
//# sourceMappingURL=chunk-
|
|
2046
|
+
//# sourceMappingURL=chunk-YZCM7CXK.js.map
|
|
2047
|
+
//# sourceMappingURL=chunk-YZCM7CXK.js.map
|