@mhmo91/schmancy 0.4.92 → 0.4.94
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/ai/area.md +181 -968
- package/dist/ai/area.md +181 -968
- package/dist/{animated-text-CUFCt7Zg.js → animated-text-BLDqQ33G.js} +3 -3
- package/dist/{animated-text-CUFCt7Zg.js.map → animated-text-BLDqQ33G.js.map} +1 -1
- package/dist/{animated-text-CM1zqBki.cjs → animated-text-CIgDmUF0.cjs} +2 -2
- package/dist/{animated-text-CM1zqBki.cjs.map → animated-text-CIgDmUF0.cjs.map} +1 -1
- package/dist/animated-text.cjs +1 -1
- package/dist/animated-text.js +1 -1
- package/dist/area.cjs +1 -1
- package/dist/area.js +1 -1
- package/dist/{autocomplete-CFREt7Vm.cjs → autocomplete-35wx4zfo.cjs} +6 -6
- package/dist/autocomplete-35wx4zfo.cjs.map +1 -0
- package/dist/{autocomplete-09_SfjsP.js → autocomplete-DMbJLodc.js} +35 -35
- package/dist/autocomplete-DMbJLodc.js.map +1 -0
- package/dist/autocomplete.cjs +1 -1
- package/dist/autocomplete.js +1 -1
- package/dist/{avatar-CKp3Ukk4.js → avatar-BqGZ5lKc.js} +152 -200
- package/dist/avatar-BqGZ5lKc.js.map +1 -0
- package/dist/{avatar-C8PC_n9i.cjs → avatar-UnP-o7T9.cjs} +21 -69
- package/dist/avatar-UnP-o7T9.cjs.map +1 -0
- package/dist/badge.cjs +1 -1
- package/dist/badge.js +1 -1
- package/dist/{boat-DDJK4CS4.js → boat-PVy1fXfm.js} +2 -2
- package/dist/{boat-DDJK4CS4.js.map → boat-PVy1fXfm.js.map} +1 -1
- package/dist/{boat-Ddi4PegB.cjs → boat-zVJF22wK.cjs} +2 -2
- package/dist/{boat-Ddi4PegB.cjs.map → boat-zVJF22wK.cjs.map} +1 -1
- package/dist/boat.cjs +1 -1
- package/dist/boat.js +1 -1
- package/dist/busy.cjs +1 -1
- package/dist/busy.js +1 -1
- package/dist/button.cjs +1 -1
- package/dist/button.js +1 -1
- package/dist/card.cjs +1 -1
- package/dist/card.js +1 -1
- package/dist/{checkbox-TclkD2h5.cjs → checkbox-BtZqKgPT.cjs} +2 -2
- package/dist/{checkbox-TclkD2h5.cjs.map → checkbox-BtZqKgPT.cjs.map} +1 -1
- package/dist/{checkbox-BU1x0W8U.js → checkbox-DAFDBz7J.js} +2 -2
- package/dist/{checkbox-BU1x0W8U.js.map → checkbox-DAFDBz7J.js.map} +1 -1
- package/dist/checkbox.cjs +1 -1
- package/dist/checkbox.js +1 -1
- package/dist/{chips-DQBHJNSu.cjs → chips-BRigd0oB.cjs} +10 -21
- package/dist/{chips-DQBHJNSu.cjs.map → chips-BRigd0oB.cjs.map} +1 -1
- package/dist/{chips-Bxsoq1vm.js → chips-BnA6mVWq.js} +56 -67
- package/dist/{chips-Bxsoq1vm.js.map → chips-BnA6mVWq.js.map} +1 -1
- package/dist/chips.cjs +1 -1
- package/dist/chips.js +1 -1
- package/dist/code-highlight.cjs +1 -1
- package/dist/code-highlight.js +1 -1
- package/dist/{code-preview-C9Imx-RX.cjs → code-preview-BYWzA52m.cjs} +2 -2
- package/dist/{code-preview-C9Imx-RX.cjs.map → code-preview-BYWzA52m.cjs.map} +1 -1
- package/dist/{code-preview-DnUM87ZB.js → code-preview-CUc2NHQu.js} +2 -2
- package/dist/{code-preview-DnUM87ZB.js.map → code-preview-CUc2NHQu.js.map} +1 -1
- package/dist/components.cjs +1 -1
- package/dist/components.js +1 -1
- package/dist/content-drawer.cjs +1 -1
- package/dist/content-drawer.js +1 -1
- package/dist/{date-range-DF7MnRi3.js → date-range-DFNdMN6p.js} +4 -4
- package/dist/{date-range-DF7MnRi3.js.map → date-range-DFNdMN6p.js.map} +1 -1
- package/dist/{date-range-D1A1Xrmw.cjs → date-range-DhWbY9Ph.cjs} +2 -2
- package/dist/{date-range-D1A1Xrmw.cjs.map → date-range-DhWbY9Ph.cjs.map} +1 -1
- package/dist/{date-range-inline-CwLJLOe1.cjs → date-range-inline-FiCLIta4.cjs} +2 -2
- package/dist/{date-range-inline-CwLJLOe1.cjs.map → date-range-inline-FiCLIta4.cjs.map} +1 -1
- package/dist/{date-range-inline-Ca4DPfeR.js → date-range-inline-xh0mrWH9.js} +3 -3
- package/dist/{date-range-inline-Ca4DPfeR.js.map → date-range-inline-xh0mrWH9.js.map} +1 -1
- package/dist/date-range-inline.cjs +1 -1
- package/dist/date-range-inline.js +1 -1
- package/dist/date-range.cjs +1 -1
- package/dist/date-range.js +1 -1
- package/dist/{delay-hXu_bFxW.js → delay-BTRy2v5H.js} +2 -2
- package/dist/{delay-hXu_bFxW.js.map → delay-BTRy2v5H.js.map} +1 -1
- package/dist/{delay-DQKt6VVn.cjs → delay-dCLgp1B7.cjs} +2 -2
- package/dist/{delay-DQKt6VVn.cjs.map → delay-dCLgp1B7.cjs.map} +1 -1
- package/dist/delay.cjs +1 -1
- package/dist/delay.js +1 -1
- package/dist/{details-Ft0ivj0F.cjs → details-Bf0qMDKW.cjs} +2 -2
- package/dist/{details-Ft0ivj0F.cjs.map → details-Bf0qMDKW.cjs.map} +1 -1
- package/dist/{details-Ci0XXz0g.js → details-BsmaD0yr.js} +2 -2
- package/dist/{details-Ci0XXz0g.js.map → details-BsmaD0yr.js.map} +1 -1
- package/dist/details.cjs +1 -1
- package/dist/details.js +1 -1
- package/dist/{dialog-content-7tFlrTFn.cjs → dialog-content-0w0WQYF3.cjs} +2 -2
- package/dist/{dialog-content-7tFlrTFn.cjs.map → dialog-content-0w0WQYF3.cjs.map} +1 -1
- package/dist/{dialog-content-DXnLwUtv.js → dialog-content-r1Ai3BZj.js} +4 -4
- package/dist/{dialog-content-DXnLwUtv.js.map → dialog-content-r1Ai3BZj.js.map} +1 -1
- package/dist/dialog-service-CgJH8clD.cjs +2 -0
- package/dist/dialog-service-CgJH8clD.cjs.map +1 -0
- package/dist/{dialog-service-DwhuICgc.js → dialog-service-PQ9ssbsY.js} +53 -47
- package/dist/dialog-service-PQ9ssbsY.js.map +1 -0
- package/dist/dialog.cjs +1 -1
- package/dist/dialog.js +2 -2
- package/dist/directives.cjs +1 -1
- package/dist/directives.js +1 -1
- package/dist/divider-BPsLFNzK.js +22 -0
- package/dist/divider-BPsLFNzK.js.map +1 -0
- package/dist/divider-CYPKRC_-.cjs +2 -0
- package/dist/divider-CYPKRC_-.cjs.map +1 -0
- package/dist/divider.cjs +1 -1
- package/dist/divider.js +1 -1
- package/dist/{dropdown-content-BCk2WbXc.js → dropdown-content-B51-eGbI.js} +40 -41
- package/dist/dropdown-content-B51-eGbI.js.map +1 -0
- package/dist/{dropdown-content-CXxTjA0j.cjs → dropdown-content-DelDUcfW.cjs} +5 -10
- package/dist/dropdown-content-DelDUcfW.cjs.map +1 -0
- package/dist/dropdown.cjs +1 -1
- package/dist/dropdown.js +1 -1
- package/dist/{email-recipients-BWIYYwA8.cjs → email-recipients-BlSATi3L.cjs} +24 -24
- package/dist/email-recipients-BlSATi3L.cjs.map +1 -0
- package/dist/{email-recipients-BT3xPoBN.js → email-recipients-hkvv_vgI.js} +141 -144
- package/dist/email-recipients-hkvv_vgI.js.map +1 -0
- package/dist/extra.cjs +1 -1
- package/dist/extra.js +1 -1
- package/dist/{flex-DJEZ1DdQ.js → flex-BDtCUg0s.js} +2 -2
- package/dist/{flex-DJEZ1DdQ.js.map → flex-BDtCUg0s.js.map} +1 -1
- package/dist/{flex-DflXFgVs.cjs → flex-_K-5RVZM.cjs} +2 -2
- package/dist/{flex-DflXFgVs.cjs.map → flex-_K-5RVZM.cjs.map} +1 -1
- package/dist/{form-7OiDtJT4.js → form-BTmhZIB8.js} +11 -23
- package/dist/form-BTmhZIB8.js.map +1 -0
- package/dist/form-jw1NVkyp.cjs +2 -0
- package/dist/form-jw1NVkyp.cjs.map +1 -0
- package/dist/form.cjs +1 -1
- package/dist/form.js +1 -1
- package/dist/{formField.mixin-3i7VOHMH.js → formField.mixin-D_1WwNWJ.js} +2 -2
- package/dist/{formField.mixin-3i7VOHMH.js.map → formField.mixin-D_1WwNWJ.js.map} +1 -1
- package/dist/{formField.mixin-B1vJlhmI.cjs → formField.mixin-HN79lSIE.cjs} +2 -2
- package/dist/{formField.mixin-B1vJlhmI.cjs.map → formField.mixin-HN79lSIE.cjs.map} +1 -1
- package/dist/{icon-BMhWh8Ib.js → icon-Clqcexag.js} +2 -2
- package/dist/{icon-BMhWh8Ib.js.map → icon-Clqcexag.js.map} +1 -1
- package/dist/{icon-button-DOZuY9x8.js → icon-button-Bu_eEqfk.js} +5 -5
- package/dist/icon-button-Bu_eEqfk.js.map +1 -0
- package/dist/{icon-button-CLDy5tUS.cjs → icon-button-M5CqyRR4.cjs} +4 -4
- package/dist/icon-button-M5CqyRR4.cjs.map +1 -0
- package/dist/{icon-TPLdDnCq.cjs → icon-pFAWVSGq.cjs} +2 -2
- package/dist/{icon-TPLdDnCq.cjs.map → icon-pFAWVSGq.cjs.map} +1 -1
- package/dist/icons.cjs +1 -1
- package/dist/icons.js +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.js +49 -49
- package/dist/{input-Dre3bMuF.js → input-CFlQ_9Cq.js} +40 -44
- package/dist/input-CFlQ_9Cq.js.map +1 -0
- package/dist/input-CWOVMdSb.cjs +51 -0
- package/dist/input-CWOVMdSb.cjs.map +1 -0
- package/dist/input.cjs +1 -1
- package/dist/input.js +1 -1
- package/dist/layout.cjs +1 -1
- package/dist/layout.js +1 -1
- package/dist/lazy-DObpkuL6.cjs.map +1 -1
- package/dist/lazy-E2LCDm7n.js.map +1 -1
- package/dist/{list-DZ43cTnR.cjs → list-CDXL73JQ.cjs} +2 -2
- package/dist/{list-DZ43cTnR.cjs.map → list-CDXL73JQ.cjs.map} +1 -1
- package/dist/{list-2mBZmMCP.js → list-rmfIuWZK.js} +2 -2
- package/dist/{list-2mBZmMCP.js.map → list-rmfIuWZK.js.map} +1 -1
- package/dist/list.cjs +1 -1
- package/dist/list.js +1 -1
- package/dist/{litElement.mixin-Bj0zv3lK.js → litElement.mixin-Dm9SOPDp.js} +2 -2
- package/dist/{litElement.mixin-Bj0zv3lK.js.map → litElement.mixin-Dm9SOPDp.js.map} +1 -1
- package/dist/{litElement.mixin-CjRG_9tZ.cjs → litElement.mixin-YJ8D9a8Z.cjs} +2 -2
- package/dist/{litElement.mixin-CjRG_9tZ.cjs.map → litElement.mixin-YJ8D9a8Z.cjs.map} +1 -1
- package/dist/mailbox.cjs +1 -1
- package/dist/mailbox.js +1 -1
- package/dist/{map-sSS6ACVH.js → map-BfP3ZVm_.js} +2 -2
- package/dist/{map-sSS6ACVH.js.map → map-BfP3ZVm_.js.map} +1 -1
- package/dist/{map-D69SEPpf.cjs → map-DQwGv46m.cjs} +2 -2
- package/dist/{map-D69SEPpf.cjs.map → map-DQwGv46m.cjs.map} +1 -1
- package/dist/map.cjs +1 -1
- package/dist/map.js +1 -1
- package/dist/media-B28J9yez.js +261 -0
- package/dist/media-B28J9yez.js.map +1 -0
- package/dist/media-DtYChqKs.cjs +177 -0
- package/dist/media-DtYChqKs.cjs.map +1 -0
- package/dist/{menu-CATzB9tA.js → menu-C0iTDVTM.js} +3 -3
- package/dist/{menu-CATzB9tA.js.map → menu-C0iTDVTM.js.map} +1 -1
- package/dist/{menu-BOeTdvk_.cjs → menu-jcodrTEA.cjs} +2 -2
- package/dist/{menu-BOeTdvk_.cjs.map → menu-jcodrTEA.cjs.map} +1 -1
- package/dist/menu.cjs +1 -1
- package/dist/menu.js +1 -1
- package/dist/nav-drawer.cjs +1 -1
- package/dist/nav-drawer.js +1 -1
- package/dist/{notification-service-DrwmIgoR.js → notification-service-BIH173dS.js} +62 -58
- package/dist/notification-service-BIH173dS.js.map +1 -0
- package/dist/{notification-service-LnNx5EDh.cjs → notification-service-CD-XzDeP.cjs} +7 -7
- package/dist/notification-service-CD-XzDeP.cjs.map +1 -0
- package/dist/notification.cjs +1 -1
- package/dist/notification.js +2 -2
- package/dist/{notify-C41nYdUf.js → notify-4u_P6A9W.js} +2 -2
- package/dist/{notify-C41nYdUf.js.map → notify-4u_P6A9W.js.map} +1 -1
- package/dist/{notify-DQMcgjgZ.cjs → notify-C95rxXiW.cjs} +2 -2
- package/dist/{notify-DQMcgjgZ.cjs.map → notify-C95rxXiW.cjs.map} +1 -1
- package/dist/{option-DbBr0StZ.js → option-CTkTl7Pz.js} +12 -12
- package/dist/option-CTkTl7Pz.js.map +1 -0
- package/dist/{option-BNKGfKgL.cjs → option-D-Itfwmu.cjs} +5 -5
- package/dist/option-D-Itfwmu.cjs.map +1 -0
- package/dist/option.cjs +1 -1
- package/dist/option.js +1 -1
- package/dist/{payment-card-form-_jHodv7G.js → payment-card-form-DcipYS1-.js} +3 -3
- package/dist/{payment-card-form-_jHodv7G.js.map → payment-card-form-DcipYS1-.js.map} +1 -1
- package/dist/{payment-card-form-ByWbYHhd.cjs → payment-card-form-HQsOp4z5.cjs} +2 -2
- package/dist/{payment-card-form-ByWbYHhd.cjs.map → payment-card-form-HQsOp4z5.cjs.map} +1 -1
- package/dist/{progress-BY7K7Etz.cjs → progress-B3Q1qs5e.cjs} +2 -2
- package/dist/{progress-BY7K7Etz.cjs.map → progress-B3Q1qs5e.cjs.map} +1 -1
- package/dist/{progress-Cix7Mu1p.js → progress-zOZKeIA7.js} +2 -2
- package/dist/{progress-Cix7Mu1p.js.map → progress-zOZKeIA7.js.map} +1 -1
- package/dist/progress.cjs +1 -1
- package/dist/progress.js +1 -1
- package/dist/{radio-button-BNmjg-i9.js → radio-button-C4TI0v9W.js} +27 -27
- package/dist/radio-button-C4TI0v9W.js.map +1 -0
- package/dist/radio-button-COzEX5Ar.cjs +41 -0
- package/dist/radio-button-COzEX5Ar.cjs.map +1 -0
- package/dist/radio-group.cjs +1 -1
- package/dist/radio-group.js +1 -1
- package/dist/{ripple-BumgqsDT.js → ripple-Cy-nvO8W.js} +24 -22
- package/dist/ripple-Cy-nvO8W.js.map +1 -0
- package/dist/ripple-DqQrvaTe.cjs +16 -0
- package/dist/ripple-DqQrvaTe.cjs.map +1 -0
- package/dist/route.component-Cr-M16SZ.cjs +12 -0
- package/dist/route.component-Cr-M16SZ.cjs.map +1 -0
- package/dist/route.component-Df4CQy4C.js +320 -0
- package/dist/route.component-Df4CQy4C.js.map +1 -0
- package/dist/{schmancy-steps-container-BUE4fG8_.js → schmancy-steps-container-DC6Ca6-A.js} +2 -2
- package/dist/{schmancy-steps-container-BUE4fG8_.js.map → schmancy-steps-container-DC6Ca6-A.js.map} +1 -1
- package/dist/{schmancy-steps-container-D69TfC2I.cjs → schmancy-steps-container-DfzodwEn.cjs} +2 -2
- package/dist/{schmancy-steps-container-D69TfC2I.cjs.map → schmancy-steps-container-DfzodwEn.cjs.map} +1 -1
- package/dist/{select-Bspcrc-h.js → select-Br2sY6MK.js} +16 -16
- package/dist/select-Br2sY6MK.js.map +1 -0
- package/dist/select-wUk1qE31.cjs +57 -0
- package/dist/select-wUk1qE31.cjs.map +1 -0
- package/dist/select.cjs +1 -1
- package/dist/select.js +1 -1
- package/dist/{sheet-BxOGSGJ0.js → sheet-BjJdVvvp.js} +3 -3
- package/dist/{sheet-BxOGSGJ0.js.map → sheet-BjJdVvvp.js.map} +1 -1
- package/dist/{sheet-CuE9tUeo.cjs → sheet-D3RmdvbL.cjs} +2 -2
- package/dist/{sheet-CuE9tUeo.cjs.map → sheet-D3RmdvbL.cjs.map} +1 -1
- package/dist/sheet.cjs +1 -1
- package/dist/sheet.js +1 -1
- package/dist/{slider-ChQVbnYO.js → slider-Dgwlzvr-.js} +3 -3
- package/dist/{slider-ChQVbnYO.js.map → slider-Dgwlzvr-.js.map} +1 -1
- package/dist/{slider-DeKwZONn.cjs → slider-XoN7Z9iO.cjs} +2 -2
- package/dist/{slider-DeKwZONn.cjs.map → slider-XoN7Z9iO.cjs.map} +1 -1
- package/dist/slider.cjs +1 -1
- package/dist/slider.js +1 -1
- package/dist/{spinner-C5tMQys5.cjs → spinner-D9l0j12i.cjs} +2 -2
- package/dist/{spinner-C5tMQys5.cjs.map → spinner-D9l0j12i.cjs.map} +1 -1
- package/dist/{spinner-y1dPMTQp.js → spinner-e06PNB2u.js} +2 -2
- package/dist/{spinner-y1dPMTQp.js.map → spinner-e06PNB2u.js.map} +1 -1
- package/dist/steps.cjs +1 -1
- package/dist/steps.js +1 -1
- package/dist/{surface-4MzhEaru.cjs → surface-BgLZnRcp.cjs} +2 -2
- package/dist/{surface-4MzhEaru.cjs.map → surface-BgLZnRcp.cjs.map} +1 -1
- package/dist/{surface-BYmdH754.js → surface-trk3Zpii.js} +2 -2
- package/dist/{surface-BYmdH754.js.map → surface-trk3Zpii.js.map} +1 -1
- package/dist/surface.cjs +1 -1
- package/dist/surface.js +1 -1
- package/dist/{table-YBHaxQzQ.js → table-BfFAQg7K.js} +2 -2
- package/dist/{table-YBHaxQzQ.js.map → table-BfFAQg7K.js.map} +1 -1
- package/dist/{table-J5EVvpXy.cjs → table-g0Wsm45r.cjs} +2 -2
- package/dist/{table-J5EVvpXy.cjs.map → table-g0Wsm45r.cjs.map} +1 -1
- package/dist/table.cjs +1 -1
- package/dist/table.js +1 -1
- package/dist/{tabs-compatibility-ezB19WI8.js → tabs-compatibility-D1RfuRqZ.js} +2 -2
- package/dist/{tabs-compatibility-ezB19WI8.js.map → tabs-compatibility-D1RfuRqZ.js.map} +1 -1
- package/dist/{tabs-compatibility-JMTsaND_.cjs → tabs-compatibility-FkpvGEG3.cjs} +2 -2
- package/dist/{tabs-compatibility-JMTsaND_.cjs.map → tabs-compatibility-FkpvGEG3.cjs.map} +1 -1
- package/dist/tabs.cjs +1 -1
- package/dist/tabs.js +1 -1
- package/dist/tailwind.mixin-CPuO9c5y.js +43 -0
- package/dist/{tailwind.mixin-Cwbr8x57.js.map → tailwind.mixin-CPuO9c5y.js.map} +1 -1
- package/dist/tailwind.mixin-Cb-fyJ_y.cjs +2 -0
- package/dist/{tailwind.mixin-B-VcpMHn.cjs.map → tailwind.mixin-Cb-fyJ_y.cjs.map} +1 -1
- package/dist/teleport.cjs +1 -1
- package/dist/teleport.js +1 -1
- package/dist/{textarea-B3nWhCra.cjs → textarea-DBKIOjBp.cjs} +2 -2
- package/dist/{textarea-B3nWhCra.cjs.map → textarea-DBKIOjBp.cjs.map} +1 -1
- package/dist/{textarea-yn7IFbnu.js → textarea-U-fvfv2M.js} +3 -3
- package/dist/{textarea-yn7IFbnu.js.map → textarea-U-fvfv2M.js.map} +1 -1
- package/dist/textarea.cjs +1 -1
- package/dist/textarea.js +1 -1
- package/dist/{theme-button-I_c1ZWWV.cjs → theme-button-C-6Y_Mib.cjs} +2 -2
- package/dist/{theme-button-I_c1ZWWV.cjs.map → theme-button-C-6Y_Mib.cjs.map} +1 -1
- package/dist/{theme-button-CGArg1pu.js → theme-button-CnWM9ZCv.js} +2 -2
- package/dist/{theme-button-CGArg1pu.js.map → theme-button-CnWM9ZCv.js.map} +1 -1
- package/dist/theme-button.cjs +1 -1
- package/dist/theme-button.js +1 -1
- package/dist/theme.cjs +1 -1
- package/dist/{theme.component-B5zp-4OG.js → theme.component-CfLAvCYi.js} +168 -168
- package/dist/{theme.component-B5zp-4OG.js.map → theme.component-CfLAvCYi.js.map} +1 -1
- package/dist/{theme.component-CdSDaeU0.cjs → theme.component-Dko2yi2n.cjs} +3 -3
- package/dist/{theme.component-CdSDaeU0.cjs.map → theme.component-Dko2yi2n.cjs.map} +1 -1
- package/dist/theme.js +1 -1
- package/dist/{timezone-CepyC8kw.js → timezone-CXGJk88N.js} +3 -3
- package/dist/{timezone-CepyC8kw.js.map → timezone-CXGJk88N.js.map} +1 -1
- package/dist/{timezone-DRBf5biI.cjs → timezone-Wy7jf5gf.cjs} +2 -2
- package/dist/{timezone-DRBf5biI.cjs.map → timezone-Wy7jf5gf.cjs.map} +1 -1
- package/dist/tooltip-BXCWSU_6.cjs +7 -0
- package/dist/tooltip-BXCWSU_6.cjs.map +1 -0
- package/dist/{tooltip-C6HlAqs2.js → tooltip-CuDCLzkr.js} +55 -55
- package/dist/tooltip-CuDCLzkr.js.map +1 -0
- package/dist/tooltip.cjs +1 -1
- package/dist/tooltip.js +1 -1
- package/dist/{tree-CpqWMdfb.cjs → tree-Bux6udiH.cjs} +2 -2
- package/dist/{tree-CpqWMdfb.cjs.map → tree-Bux6udiH.cjs.map} +1 -1
- package/dist/{tree-CxJAr7G3.js → tree-CfcLl-dV.js} +2 -2
- package/dist/{tree-CxJAr7G3.js.map → tree-CfcLl-dV.js.map} +1 -1
- package/dist/tree.cjs +1 -1
- package/dist/tree.js +1 -1
- package/dist/{typewriter-D3QLAVdz.js → typewriter-C63WkKf1.js} +64 -76
- package/dist/typewriter-C63WkKf1.js.map +1 -0
- package/dist/{typewriter-BWg7Otk8.cjs → typewriter-HZThI3m1.cjs} +3 -15
- package/dist/typewriter-HZThI3m1.cjs.map +1 -0
- package/dist/typewriter.cjs +1 -1
- package/dist/typewriter.js +1 -1
- package/dist/{typography-DGOOlbC1.cjs → typography-DNj4eHeR.cjs} +2 -2
- package/dist/{typography-DGOOlbC1.cjs.map → typography-DNj4eHeR.cjs.map} +1 -1
- package/dist/{typography-C8NCP_rr.js → typography-DRkYb9R0.js} +2 -2
- package/dist/{typography-C8NCP_rr.js.map → typography-DRkYb9R0.js.map} +1 -1
- package/dist/typography.cjs +1 -1
- package/dist/typography.js +1 -1
- package/package.json +1 -1
- package/types/src/area/area.component.d.ts +8 -3
- package/types/src/area/area.service.d.ts +1 -0
- package/types/src/area/route.component.d.ts +4 -5
- package/types/src/area/utils.d.ts +2 -1
- package/types/src/card/card.d.ts +56 -3
- package/types/src/directives/ripple.d.ts +2 -1
- package/types/src/dropdown/dropdown-component.d.ts +1 -0
- package/types/src/form/form-v2.d.ts +0 -1
- package/types/src/form/form.d.ts +0 -1
- package/types/src/mailbox/email-editor.d.ts +1 -2
- package/types/src/notification/notification-audio.d.ts +9 -0
- package/types/src/notification/notification-container.d.ts +1 -0
- package/dist/autocomplete-09_SfjsP.js.map +0 -1
- package/dist/autocomplete-CFREt7Vm.cjs.map +0 -1
- package/dist/avatar-C8PC_n9i.cjs.map +0 -1
- package/dist/avatar-CKp3Ukk4.js.map +0 -1
- package/dist/dialog-service-DwhuICgc.js.map +0 -1
- package/dist/dialog-service-juvsgc5X.cjs +0 -2
- package/dist/dialog-service-juvsgc5X.cjs.map +0 -1
- package/dist/divider-BG9uDpUp.js +0 -103
- package/dist/divider-BG9uDpUp.js.map +0 -1
- package/dist/divider-CWnAg2p-.cjs +0 -83
- package/dist/divider-CWnAg2p-.cjs.map +0 -1
- package/dist/dropdown-content-BCk2WbXc.js.map +0 -1
- package/dist/dropdown-content-CXxTjA0j.cjs.map +0 -1
- package/dist/email-recipients-BT3xPoBN.js.map +0 -1
- package/dist/email-recipients-BWIYYwA8.cjs.map +0 -1
- package/dist/form-7OiDtJT4.js.map +0 -1
- package/dist/form-nHwPpyhJ.cjs +0 -14
- package/dist/form-nHwPpyhJ.cjs.map +0 -1
- package/dist/icon-button-CLDy5tUS.cjs.map +0 -1
- package/dist/icon-button-DOZuY9x8.js.map +0 -1
- package/dist/input-CqLneJhz.cjs +0 -51
- package/dist/input-CqLneJhz.cjs.map +0 -1
- package/dist/input-Dre3bMuF.js.map +0 -1
- package/dist/media-B-VIbSH_.js +0 -151
- package/dist/media-B-VIbSH_.js.map +0 -1
- package/dist/media-Cew1Mxkj.cjs +0 -100
- package/dist/media-Cew1Mxkj.cjs.map +0 -1
- package/dist/notification-service-DrwmIgoR.js.map +0 -1
- package/dist/notification-service-LnNx5EDh.cjs.map +0 -1
- package/dist/option-BNKGfKgL.cjs.map +0 -1
- package/dist/option-DbBr0StZ.js.map +0 -1
- package/dist/radio-button-BNmjg-i9.js.map +0 -1
- package/dist/radio-button-CYq6qTSb.cjs +0 -41
- package/dist/radio-button-CYq6qTSb.cjs.map +0 -1
- package/dist/ripple-BumgqsDT.js.map +0 -1
- package/dist/ripple-C2BHbhcS.cjs +0 -16
- package/dist/ripple-C2BHbhcS.cjs.map +0 -1
- package/dist/route.component-CzCd2fMa.cjs +0 -12
- package/dist/route.component-CzCd2fMa.cjs.map +0 -1
- package/dist/route.component-Q3rCkryr.js +0 -321
- package/dist/route.component-Q3rCkryr.js.map +0 -1
- package/dist/select-B-yc3JB7.cjs +0 -57
- package/dist/select-B-yc3JB7.cjs.map +0 -1
- package/dist/select-Bspcrc-h.js.map +0 -1
- package/dist/tailwind.mixin-B-VcpMHn.cjs +0 -2
- package/dist/tailwind.mixin-Cwbr8x57.js +0 -43
- package/dist/tooltip-C6HlAqs2.js.map +0 -1
- package/dist/tooltip-CCX8PidC.cjs +0 -7
- package/dist/tooltip-CCX8PidC.cjs.map +0 -1
- package/dist/typewriter-BWg7Otk8.cjs.map +0 -1
- package/dist/typewriter-D3QLAVdz.js.map +0 -1
package/dist/ai/area.md
CHANGED
|
@@ -1,1069 +1,282 @@
|
|
|
1
|
-
# Schmancy Area -
|
|
1
|
+
# Schmancy Area - Routing System
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Reactive routing system for web components using RxJS. Supports multiple outlets, guards, lazy loading, and nested routing.
|
|
4
4
|
|
|
5
|
-
##
|
|
6
|
-
|
|
7
|
-
### 1. `<schmancy-area>` - Router Outlet
|
|
8
|
-
The main container that displays routed components.
|
|
5
|
+
## Quick Start
|
|
9
6
|
|
|
10
7
|
```html
|
|
11
|
-
<!--
|
|
12
|
-
<schmancy-area
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
8
|
+
<!-- Define routes declaratively -->
|
|
9
|
+
<schmancy-area name="main" default="home-page">
|
|
10
|
+
<schmancy-route when="products" component="product-list"></schmancy-route>
|
|
11
|
+
<schmancy-route when="about" component="about-page"></schmancy-route>
|
|
12
|
+
<schmancy-route
|
|
13
|
+
when="admin"
|
|
14
|
+
component="admin-panel"
|
|
15
|
+
.guard=${authState$.asObservable()}
|
|
16
|
+
@redirect=${() => area.push({ component: 'login', area: 'main' })}>
|
|
17
|
+
</schmancy-route>
|
|
21
18
|
</schmancy-area>
|
|
22
19
|
```
|
|
23
20
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
```html
|
|
28
|
-
<!-- Simple route -->
|
|
29
|
-
<schmancy-route
|
|
30
|
-
when="products" <!-- Matches 'products' segment in URL -->
|
|
31
|
-
component="product-list"
|
|
32
|
-
exact>
|
|
33
|
-
</schmancy-route>
|
|
34
|
-
|
|
35
|
-
<!-- Component tag name matching -->
|
|
36
|
-
<schmancy-route
|
|
37
|
-
when="product-detail" <!-- Matches when component is 'product-detail' -->
|
|
38
|
-
component="product-detail">
|
|
39
|
-
</schmancy-route>
|
|
21
|
+
```typescript
|
|
22
|
+
// Navigate programmatically
|
|
23
|
+
import { area } from '@schmancy/area';
|
|
40
24
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
25
|
+
area.push({
|
|
26
|
+
component: 'product-detail',
|
|
27
|
+
area: 'main',
|
|
28
|
+
params: { productId: '123' },
|
|
29
|
+
state: { fromCart: true }
|
|
30
|
+
});
|
|
47
31
|
```
|
|
48
32
|
|
|
49
|
-
##
|
|
50
|
-
|
|
51
|
-
### `<schmancy-area>` Properties
|
|
52
|
-
|
|
53
|
-
| Property | Type | Description |
|
|
54
|
-
|----------|------|-------------|
|
|
55
|
-
| `name` | `string` | **Required**. Unique identifier for this router outlet. |
|
|
56
|
-
| `default` | `string \| Promise<NodeModule> \| CustomElementConstructor \| HTMLElement \| LazyComponent` | Default component to display when no route matches or area is empty. |
|
|
57
|
-
|
|
58
|
-
### `<schmancy-route>` Properties
|
|
59
|
-
|
|
60
|
-
| Property | Type | Description |
|
|
61
|
-
|----------|------|-------------|
|
|
62
|
-
| `when` | `string` | **Required**. URL segment to match OR component tag name for programmatic navigation. |
|
|
63
|
-
| `component` | `RouteComponent` | Component to render when route matches (string, constructor, element, lazy component). |
|
|
64
|
-
| `exact` | `boolean` | Whether route should match exactly (default: false). |
|
|
65
|
-
| `guard` | `() => GuardResult \| Promise<GuardResult>` | Navigation guard function. |
|
|
33
|
+
## Core Components
|
|
66
34
|
|
|
67
|
-
|
|
35
|
+
### `<schmancy-area>` - Router Outlet
|
|
36
|
+
| Property | Type | Required | Description |
|
|
37
|
+
|----------|------|----------|-------------|
|
|
38
|
+
| `name` | `string` | ✓ | Unique outlet identifier |
|
|
39
|
+
| `default` | `ComponentType` | | Fallback when no route matches |
|
|
68
40
|
|
|
69
|
-
|
|
41
|
+
### `<schmancy-route>` - Route Definition
|
|
42
|
+
| Property | Type | Required | Description |
|
|
43
|
+
|----------|------|----------|-------------|
|
|
44
|
+
| `when` | `string` | ✓ | URL segment or component tag to match |
|
|
45
|
+
| `component` | `RouteComponent` | ✓ | Component to render |
|
|
46
|
+
| `guard` | `Observable<boolean>` | | Navigation guard |
|
|
47
|
+
| `exact` | `boolean` | | Currently unused |
|
|
70
48
|
|
|
71
|
-
|
|
49
|
+
## URL Matching
|
|
72
50
|
|
|
73
|
-
|
|
74
|
-
The URL path is split by '/' and the `when` attribute is checked against each segment in the array:
|
|
51
|
+
Routes match in DOM order. First match wins.
|
|
75
52
|
|
|
76
53
|
```html
|
|
77
|
-
<!--
|
|
54
|
+
<!-- Matches URL segments -->
|
|
78
55
|
<schmancy-route when="products" component="product-list"></schmancy-route>
|
|
79
|
-
<!-- URL: /store/products
|
|
80
|
-
<!-- URL: /products → segments: ['products'] → MATCHES -->
|
|
81
|
-
<!-- URL: /home → segments: ['home'] → DOES NOT MATCH -->
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
### 2. **Component Tag Name Matching**
|
|
85
|
-
For programmatic navigation, the `when` attribute can match the component tag name:
|
|
56
|
+
<!-- URL: /store/products → segments: ['store', 'products'] → MATCHES -->
|
|
86
57
|
|
|
87
|
-
|
|
58
|
+
<!-- Matches component tag for programmatic nav -->
|
|
88
59
|
<schmancy-route when="user-profile" component="user-profile"></schmancy-route>
|
|
89
|
-
<!--
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
### 3. **JSON-Encoded State in URL**
|
|
93
|
-
When pretty URLs are disabled (default), the router encodes state as JSON in the URL:
|
|
94
|
-
|
|
95
|
-
```javascript
|
|
96
|
-
// URL: /%7B%22main%22%3A%7B%22component%22%3A%22user-profile%22%7D%7D
|
|
97
|
-
// Decoded: {"main":{"component":"user-profile"}}
|
|
98
|
-
// The router extracts this and matches against routes
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
### Matching Priority
|
|
102
|
-
|
|
103
|
-
Routes are evaluated in the order they appear in the DOM. The first matching route wins:
|
|
104
|
-
|
|
105
|
-
```html
|
|
106
|
-
<schmancy-area name="main">
|
|
107
|
-
<!-- Evaluated first -->
|
|
108
|
-
<schmancy-route when="user" component="user-detail"></schmancy-route>
|
|
109
|
-
<!-- Evaluated second -->
|
|
110
|
-
<schmancy-route when="users" component="user-list"></schmancy-route>
|
|
111
|
-
<!-- Catch-all (empty when) evaluated last -->
|
|
112
|
-
<schmancy-route when="" component="not-found"></schmancy-route>
|
|
113
|
-
</schmancy-area>
|
|
60
|
+
<!-- area.push({ component: 'user-profile', area: 'main' }) → MATCHES -->
|
|
114
61
|
```
|
|
115
62
|
|
|
116
63
|
## Navigation Guards
|
|
117
64
|
|
|
118
|
-
Guards
|
|
65
|
+
Guards are `Observable<boolean>` that emit true to allow, false to block navigation.
|
|
119
66
|
|
|
120
67
|
```typescript
|
|
121
|
-
//
|
|
122
|
-
|
|
123
|
-
when="admin"
|
|
124
|
-
component="admin-panel"
|
|
125
|
-
.guard=${() => user.isAdmin}>
|
|
126
|
-
</schmancy-route>
|
|
127
|
-
|
|
128
|
-
// String guard - redirect to path
|
|
129
|
-
<schmancy-route
|
|
130
|
-
when="profile"
|
|
131
|
-
component="user-profile"
|
|
132
|
-
.guard=${() => isAuthenticated() || '/login'}>
|
|
133
|
-
</schmancy-route>
|
|
68
|
+
// Simple guard
|
|
69
|
+
const authState$ = new BehaviorSubject(false);
|
|
134
70
|
|
|
135
|
-
// Object guard - redirect with explicit syntax
|
|
136
71
|
<schmancy-route
|
|
137
|
-
when="
|
|
138
|
-
component="
|
|
139
|
-
.guard=${()
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
return { redirect: '/unauthorized' };
|
|
145
|
-
}
|
|
146
|
-
return true;
|
|
147
|
-
}}>
|
|
148
|
-
</schmancy-route>
|
|
149
|
-
|
|
150
|
-
// Async guard - check with backend
|
|
151
|
-
<schmancy-route
|
|
152
|
-
when="premium"
|
|
153
|
-
component="premium-content"
|
|
154
|
-
.guard=${async () => {
|
|
155
|
-
const subscription = await checkSubscription();
|
|
156
|
-
return subscription.isActive || { redirect: '/upgrade' };
|
|
72
|
+
when="protected"
|
|
73
|
+
component="protected-page"
|
|
74
|
+
.guard=${authState$.asObservable()}
|
|
75
|
+
@redirect=${(e) => {
|
|
76
|
+
// e.detail contains: blockedRoute, area, params, state
|
|
77
|
+
$notify.error('Access denied');
|
|
78
|
+
area.push({ component: 'login', area: 'main' });
|
|
157
79
|
}}>
|
|
158
80
|
</schmancy-route>
|
|
159
|
-
```
|
|
160
|
-
|
|
161
|
-
### Guard Return Types
|
|
162
81
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
| `"/path"` (string) | Redirect to the specified path segment |
|
|
168
|
-
| `{redirect: "/path"}` | Explicit redirect object |
|
|
169
|
-
| `Promise<...>` | Async guard, resolved before navigation |
|
|
170
|
-
|
|
171
|
-
**Note**: Guard failures (`false` return) will fall back to the area's `default` component if specified. Redirects trigger a new route lookup based on the redirect path segment.
|
|
172
|
-
|
|
173
|
-
## Nested Routing
|
|
174
|
-
|
|
175
|
-
Create complex nested routing structures:
|
|
176
|
-
|
|
177
|
-
```html
|
|
178
|
-
<!-- Parent component with nested routes -->
|
|
179
|
-
<schmancy-route
|
|
180
|
-
when="app" <!-- Matches 'app' segment in URL -->
|
|
181
|
-
component="app-layout"
|
|
182
|
-
default="app-dashboard">
|
|
183
|
-
|
|
184
|
-
<!-- app-layout.ts -->
|
|
185
|
-
<div class="app-container">
|
|
186
|
-
<nav>...</nav>
|
|
187
|
-
<schmancy-area name="app-content">
|
|
188
|
-
<!-- Nested routes also use segment matching -->
|
|
189
|
-
<schmancy-route when="dashboard" component="app-dashboard"></schmancy-route>
|
|
190
|
-
<schmancy-route when="users" component="user-list"></schmancy-route>
|
|
191
|
-
<schmancy-route when="user-detail" component="user-detail"></schmancy-route>
|
|
192
|
-
<schmancy-route when="settings" component="settings-layout">
|
|
193
|
-
<!-- Even deeper nesting in settings-layout -->
|
|
194
|
-
</schmancy-route>
|
|
195
|
-
</schmancy-area>
|
|
196
|
-
</div>
|
|
197
|
-
</schmancy-route>
|
|
82
|
+
// Complex guard with multiple conditions
|
|
83
|
+
const canAccess$ = combineLatest([authState$, roleState$]).pipe(
|
|
84
|
+
map(([auth, role]) => auth && role === 'admin')
|
|
85
|
+
);
|
|
198
86
|
```
|
|
199
87
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
```html
|
|
203
|
-
<!-- Root level -->
|
|
204
|
-
<schmancy-area name="root">
|
|
205
|
-
<schmancy-route when="" component="app-shell" default="home-page"> <!-- Empty when = catch-all -->
|
|
206
|
-
|
|
207
|
-
<!-- Inside app-shell -->
|
|
208
|
-
<schmancy-area name="main">
|
|
209
|
-
<schmancy-route when="products" component="product-layout">
|
|
210
|
-
|
|
211
|
-
<!-- Inside product-layout -->
|
|
212
|
-
<schmancy-area name="product-content">
|
|
213
|
-
<schmancy-route when="product-list" component="product-list"></schmancy-route>
|
|
214
|
-
<schmancy-route when="product-detail" component="product-detail"></schmancy-route>
|
|
215
|
-
<schmancy-route when="reviews" component="product-reviews"></schmancy-route>
|
|
216
|
-
</schmancy-area>
|
|
217
|
-
|
|
218
|
-
</schmancy-route>
|
|
219
|
-
</schmancy-area>
|
|
220
|
-
|
|
221
|
-
</schmancy-route>
|
|
222
|
-
</schmancy-area>
|
|
223
|
-
```
|
|
88
|
+
**Important**: Guards return `Observable<boolean>` only. No strings, objects, or redirect paths.
|
|
89
|
+
Handle redirects via `@redirect` event.
|
|
224
90
|
|
|
225
91
|
## Service API
|
|
226
92
|
|
|
227
93
|
```typescript
|
|
228
|
-
|
|
229
|
-
import { area } from '@schmancy/index';
|
|
230
|
-
// Or specific import: import { area } from '@schmancy/area';
|
|
94
|
+
import { area } from '@schmancy/area';
|
|
231
95
|
|
|
232
|
-
// Navigation
|
|
96
|
+
// Navigation
|
|
233
97
|
area.push({
|
|
234
|
-
component:
|
|
235
|
-
area:
|
|
236
|
-
state?:
|
|
237
|
-
params?:
|
|
238
|
-
props?:
|
|
239
|
-
historyStrategy?: 'push'
|
|
240
|
-
clearQueryParams?: [
|
|
98
|
+
component: string | Constructor | HTMLElement | LazyComponent,
|
|
99
|
+
area: string, // Required
|
|
100
|
+
state?: any, // Stored in history
|
|
101
|
+
params?: any, // Applied to component
|
|
102
|
+
props?: any, // Alias for params
|
|
103
|
+
historyStrategy?: 'push' | 'replace' | 'silent',
|
|
104
|
+
clearQueryParams?: string[] | boolean
|
|
241
105
|
});
|
|
242
106
|
|
|
243
|
-
//
|
|
244
|
-
area.pop('sidebar');
|
|
107
|
+
// Clear area
|
|
108
|
+
area.pop('sidebar');
|
|
245
109
|
|
|
246
|
-
//
|
|
247
|
-
area.on(areaName
|
|
248
|
-
area.
|
|
249
|
-
area.
|
|
250
|
-
area.params<T>(areaName) // Get typed params from an area
|
|
251
|
-
area.param<T>(areaName, key) // Get a specific param value
|
|
252
|
-
area.props<T>(areaName) // Get typed props from an area
|
|
253
|
-
area.prop<T>(areaName, key) // Get a specific prop value
|
|
254
|
-
|
|
255
|
-
// Utility methods
|
|
256
|
-
area.hasArea(areaName) // Check if an area exists
|
|
257
|
-
area.getActiveAreas() // Get array of active area names
|
|
258
|
-
area.getRoute(areaName) // Get route synchronously (returns ActiveRoute | undefined)
|
|
110
|
+
// Subscriptions (return Observables)
|
|
111
|
+
area.on(areaName) // Subscribe to route changes
|
|
112
|
+
area.getState<T>(areaName) // Get typed state
|
|
113
|
+
area.params<T>(areaName) // Get typed params
|
|
259
114
|
|
|
260
115
|
// Configuration
|
|
261
|
-
area.prettyURL = false
|
|
262
|
-
area.
|
|
263
|
-
area.enableHistoryMode = true // Enable browser history management (default: true)
|
|
116
|
+
area.prettyURL = false // JSON-encoded URLs (default)
|
|
117
|
+
area.enableHistoryMode = true // Browser history (default)
|
|
264
118
|
```
|
|
265
119
|
|
|
266
|
-
|
|
267
|
-
- **`params`**: Component-level parameters that are passed as properties to the component instance
|
|
268
|
-
- **`props`**: Alias for params - both are applied directly to the component element
|
|
269
|
-
- **`state`**: Arbitrary data stored in browser history, accessible via subscriptions
|
|
270
|
-
- **URL Query Parameters**: Managed separately via `clearQueryParams` option
|
|
271
|
-
|
|
272
|
-
## Common Patterns
|
|
273
|
-
|
|
274
|
-
### Basic Navigation
|
|
120
|
+
## Lazy Loading
|
|
275
121
|
|
|
276
122
|
```typescript
|
|
277
|
-
|
|
278
|
-
area.push({
|
|
279
|
-
component: 'home-page',
|
|
280
|
-
area: 'main'
|
|
281
|
-
});
|
|
282
|
-
|
|
283
|
-
// Navigate with parameters and state
|
|
284
|
-
area.push({
|
|
285
|
-
component: ProductDetailComponent,
|
|
286
|
-
area: 'main',
|
|
287
|
-
params: { productId: '12345' }, // Query parameters
|
|
288
|
-
state: { showReviews: true },
|
|
289
|
-
historyStrategy: 'push'
|
|
290
|
-
});
|
|
291
|
-
|
|
292
|
-
// Navigate with component properties
|
|
293
|
-
area.push({
|
|
294
|
-
component: 'my-component',
|
|
295
|
-
area: 'main',
|
|
296
|
-
props: {
|
|
297
|
-
title: 'Hello World',
|
|
298
|
-
data: { id: 123, name: 'Test' },
|
|
299
|
-
onClick: () => console.log('Clicked!')
|
|
300
|
-
}
|
|
301
|
-
});
|
|
302
|
-
```
|
|
303
|
-
|
|
304
|
-
### Declarative Routing with Guards
|
|
305
|
-
|
|
306
|
-
```html
|
|
307
|
-
<schmancy-area name="main">
|
|
308
|
-
<!-- Public routes -->
|
|
309
|
-
<schmancy-route when="home" component="home-page"></schmancy-route>
|
|
310
|
-
<schmancy-route when="about" component="about-page"></schmancy-route>
|
|
311
|
-
|
|
312
|
-
<!-- Protected routes -->
|
|
313
|
-
<schmancy-route
|
|
314
|
-
when="dashboard"
|
|
315
|
-
component="dashboard-layout"
|
|
316
|
-
.guard=${() => isAuthenticated() || '/login'}>
|
|
317
|
-
</schmancy-route>
|
|
318
|
-
|
|
319
|
-
<!-- Admin only -->
|
|
320
|
-
<schmancy-route
|
|
321
|
-
when="admin"
|
|
322
|
-
component="admin-panel"
|
|
323
|
-
.guard=${() => hasRole('admin') || { redirect: '/unauthorized' }}>
|
|
324
|
-
</schmancy-route>
|
|
325
|
-
|
|
326
|
-
<!-- Catch-all route (should be last) -->
|
|
327
|
-
<!-- Note: Empty 'when' will match any route not matched above -->
|
|
328
|
-
<schmancy-route when="" component="not-found-page"></schmancy-route>
|
|
329
|
-
</schmancy-area>
|
|
330
|
-
```
|
|
331
|
-
|
|
332
|
-
### Clearing Areas with area.pop()
|
|
333
|
-
|
|
334
|
-
```typescript
|
|
335
|
-
// Open a modal or sidebar
|
|
336
|
-
area.push({
|
|
337
|
-
component: 'user-settings',
|
|
338
|
-
area: 'modal',
|
|
339
|
-
props: { userId: '123' }
|
|
340
|
-
});
|
|
341
|
-
|
|
342
|
-
// Clear the modal - sends clearing signal to area component
|
|
343
|
-
area.pop('modal'); // Component receives null signal and clears content
|
|
344
|
-
|
|
345
|
-
// Clear multiple areas
|
|
346
|
-
['modal', 'sidebar', 'overlay'].forEach(name => area.pop(name));
|
|
347
|
-
|
|
348
|
-
// Note: area.pop() now properly sends a clearing signal through the RxJS pipeline
|
|
349
|
-
// The area component will receive a route with null component and remove its content
|
|
350
|
-
```
|
|
351
|
-
|
|
352
|
-
### Reactive Subscriptions
|
|
353
|
-
|
|
354
|
-
```typescript
|
|
355
|
-
// Subscribe to area changes
|
|
356
|
-
area.on('main').subscribe(route => {
|
|
357
|
-
console.log(`Component: ${route.component}`);
|
|
358
|
-
console.log(`Params:`, route.params); // Query parameters
|
|
359
|
-
console.log(`Props:`, route.props); // Component properties
|
|
360
|
-
});
|
|
123
|
+
import { lazy } from '@schmancy/area';
|
|
361
124
|
|
|
362
|
-
//
|
|
363
|
-
|
|
364
|
-
interface UserParams { id: string; tab?: string; }
|
|
125
|
+
// Define lazy components
|
|
126
|
+
const LazyDashboard = lazy(() => import('./dashboard'));
|
|
365
127
|
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
128
|
+
// Use in routes
|
|
129
|
+
<schmancy-route
|
|
130
|
+
when="dashboard"
|
|
131
|
+
.component=${LazyDashboard}>
|
|
132
|
+
</schmancy-route>
|
|
369
133
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
134
|
+
// Preload on hover
|
|
135
|
+
button.addEventListener('mouseenter', () => {
|
|
136
|
+
LazyDashboard.preload();
|
|
373
137
|
});
|
|
374
|
-
```
|
|
375
138
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
Default components provide fallback UI when no route matches:
|
|
379
|
-
|
|
380
|
-
```html
|
|
381
|
-
<!-- Area-level default -->
|
|
382
|
-
<schmancy-area name="main" default="welcome-page">
|
|
383
|
-
<schmancy-route when="products" component="product-list"></schmancy-route>
|
|
384
|
-
<!-- Shows welcome-page when no route matches -->
|
|
385
|
-
</schmancy-area>
|
|
386
|
-
|
|
387
|
-
<!-- Route-level default for nested routing -->
|
|
388
|
-
<schmancy-route
|
|
389
|
-
when="app"
|
|
390
|
-
component="app-layout"
|
|
391
|
-
default="app-dashboard">
|
|
392
|
-
<!-- Shows app-dashboard as default content inside app-layout -->
|
|
393
|
-
</schmancy-route>
|
|
139
|
+
// Navigate
|
|
140
|
+
area.push({ component: LazyDashboard, area: 'main' });
|
|
394
141
|
```
|
|
395
142
|
|
|
396
|
-
##
|
|
397
|
-
|
|
398
|
-
### 1. Route Organization
|
|
143
|
+
## Nested Routing
|
|
399
144
|
|
|
400
145
|
```html
|
|
401
|
-
<!-- Order routes from most specific to least specific -->
|
|
402
146
|
<schmancy-area name="main">
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
<!-- Catch-all (empty when) last -->
|
|
412
|
-
<schmancy-route when="" component="not-found"></schmancy-route>
|
|
147
|
+
<schmancy-route when="app" component="app-layout">
|
|
148
|
+
<!-- Inside app-layout component -->
|
|
149
|
+
<schmancy-area name="app-content">
|
|
150
|
+
<schmancy-route when="users" component="user-list"></schmancy-route>
|
|
151
|
+
<schmancy-route when="settings" component="settings"></schmancy-route>
|
|
152
|
+
</schmancy-area>
|
|
153
|
+
</schmancy-route>
|
|
413
154
|
</schmancy-area>
|
|
414
155
|
```
|
|
415
156
|
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
```typescript
|
|
419
|
-
// Compose multiple guard conditions
|
|
420
|
-
const requireAuth = () => isAuthenticated() || '/login';
|
|
421
|
-
const requireAdmin = () => hasRole('admin') || '/unauthorized';
|
|
422
|
-
const requireSubscription = () => hasActiveSubscription() || '/upgrade';
|
|
423
|
-
|
|
424
|
-
// Combine guards
|
|
425
|
-
const premiumAdminGuard = () => {
|
|
426
|
-
if (!isAuthenticated()) return '/login';
|
|
427
|
-
if (!hasRole('admin')) return '/unauthorized';
|
|
428
|
-
if (!hasActiveSubscription()) return '/upgrade';
|
|
429
|
-
return true;
|
|
430
|
-
};
|
|
431
|
-
|
|
432
|
-
<schmancy-route
|
|
433
|
-
when="premium-admin"
|
|
434
|
-
component="premium-admin-panel"
|
|
435
|
-
.guard=${premiumAdminGuard}>
|
|
436
|
-
</schmancy-route>
|
|
437
|
-
```
|
|
438
|
-
|
|
439
|
-
### 3. Nested Area Management
|
|
440
|
-
|
|
441
|
-
```typescript
|
|
442
|
-
// Parent component manages child areas
|
|
443
|
-
class AppLayout extends LitElement {
|
|
444
|
-
render() {
|
|
445
|
-
return html`
|
|
446
|
-
<div class="layout">
|
|
447
|
-
<nav>
|
|
448
|
-
<a @click=${() => this.navigate('/app/dashboard')}>Dashboard</a>
|
|
449
|
-
<a @click=${() => this.navigate('/app/users')}>Users</a>
|
|
450
|
-
</nav>
|
|
451
|
-
|
|
452
|
-
<schmancy-area name="app-content">
|
|
453
|
-
<schmancy-route when="dashboard" component="dashboard"></schmancy-route>
|
|
454
|
-
<schmancy-route when="users" component="users-module"></schmancy-route>
|
|
455
|
-
</schmancy-area>
|
|
456
|
-
</div>
|
|
457
|
-
`;
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
navigate(path: string) {
|
|
461
|
-
window.history.pushState(null, '', path);
|
|
462
|
-
window.dispatchEvent(new PopStateEvent('popstate'));
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
```
|
|
466
|
-
|
|
467
|
-
### 4. Dynamic Route Loading & Lazy Components
|
|
468
|
-
|
|
469
|
-
#### Using the `lazy()` helper function
|
|
470
|
-
|
|
471
|
-
Schmancy Area provides a powerful `lazy()` function similar to React.lazy() for optimal code splitting. Lazy components are resolved to constructors before element creation for better performance:
|
|
472
|
-
|
|
473
|
-
```typescript
|
|
474
|
-
import { lazy } from '@schmancy/area';
|
|
475
|
-
|
|
476
|
-
// Create lazy-loaded components with default exports
|
|
477
|
-
const LazyDashboard = lazy(() => import('./components/dashboard'));
|
|
478
|
-
const LazyAnalytics = lazy(() => import('./components/analytics'));
|
|
479
|
-
const LazyReports = lazy(() => import('./components/reports'));
|
|
480
|
-
|
|
481
|
-
// Use with area.push()
|
|
482
|
-
area.push({
|
|
483
|
-
component: LazyDashboard,
|
|
484
|
-
area: 'main'
|
|
485
|
-
});
|
|
486
|
-
|
|
487
|
-
// Use with declarative routes
|
|
488
|
-
<schmancy-route
|
|
489
|
-
when="analytics"
|
|
490
|
-
.component=${LazyAnalytics}
|
|
491
|
-
.guard=${() => hasFeature('analytics') || '/upgrade'}>
|
|
492
|
-
</schmancy-route>
|
|
493
|
-
|
|
494
|
-
// Set as default for an area
|
|
495
|
-
<schmancy-area name="main" .default=${LazyDashboard}></schmancy-area>
|
|
496
|
-
```
|
|
497
|
-
|
|
498
|
-
#### Preloading Components
|
|
499
|
-
|
|
500
|
-
Lazy components support preloading for better perceived performance. The lazy() function caches both the loading promise and the loaded module:
|
|
501
|
-
|
|
502
|
-
```typescript
|
|
503
|
-
const LazyProfile = lazy(() => import('./profile'));
|
|
504
|
-
|
|
505
|
-
// Preload on hover for instant navigation
|
|
506
|
-
button.addEventListener('mouseenter', () => {
|
|
507
|
-
LazyProfile.preload(); // Starts loading and caches the promise
|
|
508
|
-
});
|
|
509
|
-
|
|
510
|
-
// The component will be instantly available when navigated to
|
|
511
|
-
area.push({ component: LazyProfile, area: 'main' }); // Uses cached module
|
|
512
|
-
|
|
513
|
-
// Preload critical routes after initial page load
|
|
514
|
-
window.addEventListener('load', () => {
|
|
515
|
-
setTimeout(() => {
|
|
516
|
-
LazyProfile.preload(); // Caches for future use
|
|
517
|
-
LazySettings.preload();
|
|
518
|
-
}, 2000);
|
|
519
|
-
});
|
|
520
|
-
```
|
|
157
|
+
## Common Patterns
|
|
521
158
|
|
|
522
|
-
|
|
159
|
+
### Protected Routes with Authentication
|
|
523
160
|
|
|
524
161
|
```typescript
|
|
525
|
-
import { $LitElement } from '@mixins/index';
|
|
526
|
-
import { area, lazy } from '@schmancy/area';
|
|
527
|
-
import { html } from 'lit';
|
|
528
|
-
import { customElement, state } from 'lit/decorators.js';
|
|
529
|
-
|
|
530
|
-
// Define lazy components with default exports
|
|
531
|
-
const routes = {
|
|
532
|
-
dashboard: lazy(() => import('./lazy-components/dashboard')),
|
|
533
|
-
users: lazy(() => import('./lazy-components/users')),
|
|
534
|
-
products: lazy(() => import('./lazy-components/products')),
|
|
535
|
-
reports: lazy(() => import('./lazy-components/reports')),
|
|
536
|
-
settings: lazy(() => import('./lazy-components/settings'))
|
|
537
|
-
};
|
|
538
|
-
|
|
539
162
|
@customElement('app-shell')
|
|
540
163
|
export class AppShell extends $LitElement() {
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
private navigate(route: string, component: any) {
|
|
544
|
-
this.currentRoute = route;
|
|
545
|
-
area.push({
|
|
546
|
-
area: 'main',
|
|
547
|
-
component: component
|
|
548
|
-
});
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
render() {
|
|
552
|
-
return html`
|
|
553
|
-
<div class="grid grid-cols-[auto_1fr]">
|
|
554
|
-
<!-- Sidebar Navigation -->
|
|
555
|
-
<schmancy-list>
|
|
556
|
-
<schmancy-list-item
|
|
557
|
-
?selected=${this.currentRoute === 'dashboard'}
|
|
558
|
-
@click=${() => this.navigate('dashboard', routes.dashboard)}
|
|
559
|
-
@mouseenter=${() => routes.dashboard.preload()}>
|
|
560
|
-
<schmancy-icon slot="start">dashboard</schmancy-icon>
|
|
561
|
-
Dashboard
|
|
562
|
-
</schmancy-list-item>
|
|
563
|
-
|
|
564
|
-
<schmancy-list-item
|
|
565
|
-
?selected=${this.currentRoute === 'users'}
|
|
566
|
-
@click=${() => this.navigate('users', routes.users)}
|
|
567
|
-
@mouseenter=${() => routes.users.preload()}>
|
|
568
|
-
<schmancy-icon slot="start">group</schmancy-icon>
|
|
569
|
-
Users
|
|
570
|
-
</schmancy-list-item>
|
|
571
|
-
|
|
572
|
-
<schmancy-list-item
|
|
573
|
-
?selected=${this.currentRoute === 'products'}
|
|
574
|
-
@click=${() => this.navigate('products', routes.products)}
|
|
575
|
-
@mouseenter=${() => routes.products.preload()}>
|
|
576
|
-
<schmancy-icon slot="start">inventory_2</schmancy-icon>
|
|
577
|
-
Products
|
|
578
|
-
</schmancy-list-item>
|
|
579
|
-
</schmancy-list>
|
|
580
|
-
|
|
581
|
-
<!-- Main Content Area -->
|
|
582
|
-
<schmancy-area
|
|
583
|
-
name="main"
|
|
584
|
-
.default=${routes.dashboard}>
|
|
585
|
-
</schmancy-area>
|
|
586
|
-
</div>
|
|
587
|
-
`;
|
|
588
|
-
}
|
|
589
|
-
}
|
|
590
|
-
```
|
|
591
|
-
|
|
592
|
-
#### Lazy Component Structure
|
|
593
|
-
|
|
594
|
-
Each lazy-loaded component should use default export:
|
|
164
|
+
private authState$ = new BehaviorSubject(false);
|
|
595
165
|
|
|
596
|
-
```typescript
|
|
597
|
-
// lazy-components/dashboard.ts
|
|
598
|
-
import { html, css } from 'lit';
|
|
599
|
-
import { customElement } from 'lit/decorators.js';
|
|
600
|
-
import { $LitElement } from '@mixins/index';
|
|
601
|
-
|
|
602
|
-
@customElement('lazy-dashboard')
|
|
603
|
-
export default class LazyDashboard extends $LitElement(css`
|
|
604
|
-
:host {
|
|
605
|
-
display: block;
|
|
606
|
-
padding: 24px;
|
|
607
|
-
}
|
|
608
|
-
`) {
|
|
609
166
|
render() {
|
|
610
167
|
return html`
|
|
611
|
-
<schmancy-
|
|
612
|
-
<schmancy-
|
|
613
|
-
|
|
614
|
-
|
|
168
|
+
<schmancy-area name="main">
|
|
169
|
+
<schmancy-route when="home" component="home-page"></schmancy-route>
|
|
170
|
+
|
|
171
|
+
<schmancy-route
|
|
172
|
+
when="dashboard"
|
|
173
|
+
component="dashboard"
|
|
174
|
+
.guard=${this.authState$.asObservable()}
|
|
175
|
+
@redirect=${() => {
|
|
176
|
+
area.push({ component: 'login', area: 'main' });
|
|
177
|
+
}}>
|
|
178
|
+
</schmancy-route>
|
|
179
|
+
</schmancy-area>
|
|
615
180
|
`;
|
|
616
181
|
}
|
|
617
182
|
}
|
|
618
183
|
```
|
|
619
184
|
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
1. **Reduced Initial Bundle Size**: Components are loaded only when needed
|
|
623
|
-
2. **Faster Initial Page Load**: Critical path includes only essential code
|
|
624
|
-
3. **Automatic Code Splitting**: Each lazy import creates a separate chunk
|
|
625
|
-
4. **Memory Efficiency**: Components not in use aren't loaded in memory
|
|
626
|
-
5. **Better Perceived Performance**: Preloading on hover makes navigation feel instant
|
|
627
|
-
|
|
628
|
-
#### Performance Best Practices
|
|
629
|
-
|
|
630
|
-
```typescript
|
|
631
|
-
// 1. Group related components in the same chunk
|
|
632
|
-
const LazyAdminComponents = lazy(() => import('./admin/index'));
|
|
633
|
-
|
|
634
|
-
// 2. Preload critical routes after initial render
|
|
635
|
-
connectedCallback() {
|
|
636
|
-
super.connectedCallback();
|
|
637
|
-
|
|
638
|
-
// Preload common routes after a delay
|
|
639
|
-
setTimeout(() => {
|
|
640
|
-
routes.dashboard.preload();
|
|
641
|
-
routes.users.preload();
|
|
642
|
-
}, 3000);
|
|
643
|
-
}
|
|
644
|
-
|
|
645
|
-
// 3. Use intersection observer for preloading visible links
|
|
646
|
-
const observer = new IntersectionObserver((entries) => {
|
|
647
|
-
entries.forEach(entry => {
|
|
648
|
-
if (entry.isIntersecting) {
|
|
649
|
-
const route = entry.target.dataset.route;
|
|
650
|
-
routes[route]?.preload();
|
|
651
|
-
}
|
|
652
|
-
});
|
|
653
|
-
});
|
|
654
|
-
|
|
655
|
-
// 4. Handle loading errors gracefully
|
|
656
|
-
const LazyFeature = lazy(() =>
|
|
657
|
-
import('./feature').catch(() =>
|
|
658
|
-
// Fallback to simpler version on error
|
|
659
|
-
import('./feature-lite')
|
|
660
|
-
)
|
|
661
|
-
);
|
|
662
|
-
```
|
|
663
|
-
|
|
664
|
-
#### Legacy Method (Still Supported)
|
|
665
|
-
|
|
666
|
-
The traditional dynamic import method is still supported:
|
|
667
|
-
|
|
668
|
-
```typescript
|
|
669
|
-
// Direct dynamic import (without lazy helper)
|
|
670
|
-
<schmancy-route
|
|
671
|
-
when="analytics"
|
|
672
|
-
.component=${() => import('./analytics-dashboard.js').then(m => m.AnalyticsDashboard)}
|
|
673
|
-
.guard=${() => hasFeature('analytics') || '/upgrade'}>
|
|
674
|
-
</schmancy-route>
|
|
675
|
-
```
|
|
676
|
-
|
|
677
|
-
### 5. Error Boundaries
|
|
678
|
-
|
|
679
|
-
```typescript
|
|
680
|
-
// Wrap routes in error boundaries
|
|
681
|
-
class ErrorBoundary extends LitElement {
|
|
682
|
-
@state() hasError = false;
|
|
683
|
-
|
|
684
|
-
constructor() {
|
|
685
|
-
super();
|
|
686
|
-
window.addEventListener('error', () => {
|
|
687
|
-
this.hasError = true;
|
|
688
|
-
});
|
|
689
|
-
}
|
|
690
|
-
|
|
691
|
-
render() {
|
|
692
|
-
if (this.hasError) {
|
|
693
|
-
return html`<error-page></error-page>`;
|
|
694
|
-
}
|
|
695
|
-
return html`<slot></slot>`;
|
|
696
|
-
}
|
|
697
|
-
}
|
|
698
|
-
|
|
699
|
-
// Use in template
|
|
700
|
-
html`
|
|
701
|
-
<error-boundary>
|
|
702
|
-
<schmancy-area name="main">
|
|
703
|
-
<!-- routes -->
|
|
704
|
-
</schmancy-area>
|
|
705
|
-
</error-boundary>
|
|
706
|
-
`;
|
|
707
|
-
```
|
|
708
|
-
|
|
709
|
-
## History Management
|
|
710
|
-
|
|
711
|
-
The router manages browser history automatically, storing state in `history.state.schmancyAreas`:
|
|
712
|
-
|
|
713
|
-
### Browser State Structure
|
|
714
|
-
```javascript
|
|
715
|
-
// Browser history.state structure:
|
|
716
|
-
{
|
|
717
|
-
schmancyAreas: {
|
|
718
|
-
main: {
|
|
719
|
-
component: 'user-profile',
|
|
720
|
-
state: { view: 'edit' },
|
|
721
|
-
params: { userId: '123' },
|
|
722
|
-
area: 'main'
|
|
723
|
-
},
|
|
724
|
-
sidebar: {
|
|
725
|
-
component: 'nav-menu',
|
|
726
|
-
area: 'sidebar'
|
|
727
|
-
}
|
|
728
|
-
}
|
|
729
|
-
}
|
|
730
|
-
```
|
|
731
|
-
|
|
732
|
-
### URL Encoding Modes
|
|
185
|
+
### Multi-Area Application
|
|
733
186
|
|
|
734
|
-
#### 1. **JSON Encoding (Default)**
|
|
735
|
-
When `area.prettyURL = false` (default), state is encoded as JSON in the URL:
|
|
736
|
-
```javascript
|
|
737
|
-
// URL: /%7B%22main%22%3A%7B%22component%22%3A%22user-profile%22%7D%7D
|
|
738
|
-
// Decoded: {"main":{"component":"user-profile"}}
|
|
739
|
-
```
|
|
740
|
-
|
|
741
|
-
#### 2. **Pretty URLs**
|
|
742
|
-
When `area.prettyURL = true`, creates cleaner URLs:
|
|
743
|
-
```javascript
|
|
744
|
-
area.prettyURL = true;
|
|
745
|
-
// URL: /user-profile?userId=123
|
|
746
|
-
// Component and simple params are included in the URL
|
|
747
|
-
```
|
|
748
|
-
|
|
749
|
-
### History Strategies
|
|
750
|
-
|
|
751
|
-
```typescript
|
|
752
|
-
area.push({
|
|
753
|
-
component: 'page',
|
|
754
|
-
area: 'main',
|
|
755
|
-
historyStrategy: 'push' // Options: 'push' | 'replace' | 'pop' | 'silent'
|
|
756
|
-
});
|
|
757
|
-
|
|
758
|
-
// 'push': Adds new entry to history (default)
|
|
759
|
-
// 'replace': Replaces current history entry
|
|
760
|
-
// 'pop': Updates during back/forward navigation
|
|
761
|
-
// 'silent': No history update
|
|
762
|
-
```
|
|
763
|
-
|
|
764
|
-
## Internal Behaviors
|
|
765
|
-
|
|
766
|
-
### Component Lifecycle
|
|
767
|
-
|
|
768
|
-
1. **Route Resolution Pipeline**:
|
|
769
|
-
- Route request received (programmatic, URL, or browser navigation)
|
|
770
|
-
- Lazy components resolved to constructors (if applicable)
|
|
771
|
-
- Component identifier extracted for deduplication
|
|
772
|
-
- Route deduplicated using `distinctUntilChanged`
|
|
773
|
-
- HTMLElement created and properties applied
|
|
774
|
-
- Component swapped with animation
|
|
775
|
-
|
|
776
|
-
2. **Component Swapping Animation**:
|
|
777
|
-
- Old component fades out (150ms)
|
|
778
|
-
- Old component removed from DOM
|
|
779
|
-
- New component added to shadow DOM
|
|
780
|
-
- New component fades in (150ms)
|
|
781
|
-
|
|
782
|
-
3. **Deduplication Strategy**:
|
|
783
|
-
Components are deduplicated based on:
|
|
784
|
-
- Component identifier (tag name or constructor name)
|
|
785
|
-
- JSON stringified params
|
|
786
|
-
- JSON stringified state
|
|
787
|
-
|
|
788
|
-
This prevents unnecessary re-renders when navigating to the same route.
|
|
789
|
-
|
|
790
|
-
### RxJS Pipeline Architecture
|
|
791
|
-
|
|
792
|
-
The area component uses a sophisticated RxJS pipeline:
|
|
793
|
-
|
|
794
|
-
```typescript
|
|
795
|
-
// Three navigation sources merged into one stream:
|
|
796
|
-
merge(
|
|
797
|
-
area.request, // Programmatic navigation
|
|
798
|
-
of(location.pathname), // Initial load
|
|
799
|
-
fromEvent('popstate') // Browser back/forward
|
|
800
|
-
)
|
|
801
|
-
.pipe(
|
|
802
|
-
// Resolve lazy components
|
|
803
|
-
switchMap(/* ... */),
|
|
804
|
-
|
|
805
|
-
// Deduplicate identical routes
|
|
806
|
-
distinctUntilChanged(/* ... */),
|
|
807
|
-
|
|
808
|
-
// Share single subscription
|
|
809
|
-
shareReplay(1),
|
|
810
|
-
|
|
811
|
-
// Handle errors gracefully
|
|
812
|
-
catchError(/* ... */),
|
|
813
|
-
|
|
814
|
-
// Cleanup on disconnect
|
|
815
|
-
takeUntil(this.disconnecting)
|
|
816
|
-
)
|
|
817
|
-
```
|
|
818
|
-
|
|
819
|
-
### Error Handling
|
|
820
|
-
|
|
821
|
-
- **Lazy Load Failures**: Logged to console, component set to null
|
|
822
|
-
- **Component Creation Failures**: Logged to console, gracefully skipped
|
|
823
|
-
- **Guard Errors**: Caught and fall back to default component
|
|
824
|
-
- **Navigation Errors**: Logged and return EMPTY observable
|
|
825
|
-
|
|
826
|
-
## Migration Guide
|
|
827
|
-
|
|
828
|
-
### From Direct area.push() to Declarative Routes
|
|
829
|
-
|
|
830
|
-
**Before:**
|
|
831
|
-
```typescript
|
|
832
|
-
// Old imperative approach
|
|
833
|
-
if (path === '/products') {
|
|
834
|
-
area.push({ component: 'product-list', area: 'main' });
|
|
835
|
-
} else if (path.startsWith('/products/')) {
|
|
836
|
-
const id = path.split('/')[2];
|
|
837
|
-
area.push({
|
|
838
|
-
component: 'product-detail',
|
|
839
|
-
area: 'main',
|
|
840
|
-
params: { id }
|
|
841
|
-
});
|
|
842
|
-
}
|
|
843
|
-
```
|
|
844
|
-
|
|
845
|
-
**After:**
|
|
846
187
|
```html
|
|
847
|
-
<!--
|
|
848
|
-
<
|
|
849
|
-
<schmancy-
|
|
850
|
-
<schmancy-
|
|
851
|
-
|
|
188
|
+
<!-- Multiple independent outlets -->
|
|
189
|
+
<div class="layout">
|
|
190
|
+
<schmancy-area name="sidebar" default="nav-menu"></schmancy-area>
|
|
191
|
+
<schmancy-area name="main" default="home"></schmancy-area>
|
|
192
|
+
<schmancy-area name="modal"></schmancy-area>
|
|
193
|
+
</div>
|
|
852
194
|
```
|
|
853
195
|
|
|
854
|
-
### Updating Guard Logic
|
|
855
|
-
|
|
856
|
-
**Before:**
|
|
857
196
|
```typescript
|
|
858
|
-
//
|
|
859
|
-
area.
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
area.push({ component: 'login-page', area: 'main' });
|
|
863
|
-
return EMPTY;
|
|
864
|
-
}
|
|
865
|
-
return of(route);
|
|
866
|
-
})
|
|
867
|
-
).subscribe();
|
|
868
|
-
```
|
|
869
|
-
|
|
870
|
-
**After:**
|
|
871
|
-
```html
|
|
872
|
-
<!-- Built-in guard support -->
|
|
873
|
-
<schmancy-route
|
|
874
|
-
when="protected"
|
|
875
|
-
component="protected-content"
|
|
876
|
-
.guard=${() => isAuthenticated() || '/login'}>
|
|
877
|
-
</schmancy-route>
|
|
878
|
-
```
|
|
879
|
-
|
|
880
|
-
## Advanced Examples
|
|
197
|
+
// Navigate different areas independently
|
|
198
|
+
area.push({ component: 'user-list', area: 'main' });
|
|
199
|
+
area.push({ component: 'quick-stats', area: 'sidebar' });
|
|
200
|
+
area.push({ component: 'edit-dialog', area: 'modal' });
|
|
881
201
|
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
```html
|
|
885
|
-
<schmancy-area name="root">
|
|
886
|
-
<!-- Tenant detection route -->
|
|
887
|
-
<schmancy-route
|
|
888
|
-
when="tenant-app" <!-- Matches based on component tag name -->
|
|
889
|
-
component="tenant-app"
|
|
890
|
-
.guard=${async (params) => {
|
|
891
|
-
const tenant = params.tenant;
|
|
892
|
-
const isValid = await validateTenant(tenant);
|
|
893
|
-
return isValid || { redirect: '/invalid-tenant' };
|
|
894
|
-
}}>
|
|
895
|
-
|
|
896
|
-
<!-- Inside tenant-app -->
|
|
897
|
-
<schmancy-area name="tenant-content">
|
|
898
|
-
<schmancy-route when="dashboard" component="tenant-dashboard"></schmancy-route>
|
|
899
|
-
<schmancy-route when="users" component="tenant-users"></schmancy-route>
|
|
900
|
-
<schmancy-route when="settings" component="tenant-settings"></schmancy-route>
|
|
901
|
-
</schmancy-area>
|
|
902
|
-
|
|
903
|
-
</schmancy-route>
|
|
904
|
-
</schmancy-area>
|
|
905
|
-
```
|
|
906
|
-
|
|
907
|
-
### Wizard/Step Navigation
|
|
908
|
-
|
|
909
|
-
```html
|
|
910
|
-
<schmancy-route when="onboarding" component="onboarding-wizard">
|
|
911
|
-
<!-- Inside onboarding-wizard -->
|
|
912
|
-
<schmancy-area name="wizard-step">
|
|
913
|
-
<schmancy-route
|
|
914
|
-
when="profile"
|
|
915
|
-
component="step-profile"
|
|
916
|
-
.guard=${() => hasCompletedStep(0) || '/onboarding/welcome'}>
|
|
917
|
-
</schmancy-route>
|
|
918
|
-
|
|
919
|
-
<schmancy-route
|
|
920
|
-
when="preferences"
|
|
921
|
-
component="step-preferences"
|
|
922
|
-
.guard=${() => hasCompletedStep(1) || '/onboarding/profile'}>
|
|
923
|
-
</schmancy-route>
|
|
924
|
-
|
|
925
|
-
<schmancy-route
|
|
926
|
-
when="complete"
|
|
927
|
-
component="step-complete"
|
|
928
|
-
.guard=${() => hasCompletedStep(2) || '/onboarding/preferences'}>
|
|
929
|
-
</schmancy-route>
|
|
930
|
-
</schmancy-area>
|
|
931
|
-
</schmancy-route>
|
|
202
|
+
// Clear modal
|
|
203
|
+
area.pop('modal');
|
|
932
204
|
```
|
|
933
205
|
|
|
934
|
-
## Troubleshooting
|
|
935
|
-
|
|
936
|
-
### Common Issues and Solutions
|
|
937
|
-
|
|
938
|
-
1. **Routes not matching**
|
|
939
|
-
- Check route order (specific before general)
|
|
940
|
-
- Verify URL segments match exactly
|
|
941
|
-
- Ensure wildcards are used correctly
|
|
942
|
-
|
|
943
|
-
2. **Guards not redirecting**
|
|
944
|
-
- Return string path or `{redirect: path}` object
|
|
945
|
-
- Check async guards are returning promises
|
|
946
|
-
- Verify guard function is bound correctly with `.guard=${}`
|
|
947
|
-
|
|
948
|
-
3. **Nested routes not working**
|
|
949
|
-
- Parent route must use wildcard `/*` to allow nested paths
|
|
950
|
-
- Child area must have unique name
|
|
951
|
-
- Check component hierarchy is rendering child areas
|
|
952
|
-
|
|
953
|
-
4. **area.pop() not clearing content**
|
|
954
|
-
- Update to latest version (fixed in recent update)
|
|
955
|
-
- Ensure area name is correct
|
|
956
|
-
- Check no other navigation is immediately refilling area
|
|
957
|
-
|
|
958
|
-
5. **Default components not showing**
|
|
959
|
-
- Verify default is set on correct element (area or route)
|
|
960
|
-
- Check no matching routes are preventing default
|
|
961
|
-
- Ensure component name/reference is valid
|
|
962
|
-
|
|
963
206
|
## Type Definitions
|
|
964
207
|
|
|
965
208
|
```typescript
|
|
966
|
-
//
|
|
209
|
+
// Navigation request
|
|
967
210
|
interface RouteAction {
|
|
968
|
-
component:
|
|
969
|
-
area: string; // Required
|
|
970
|
-
state?: Record<string, unknown>; // State stored in history
|
|
971
|
-
params?: Record<string, unknown>; // Component properties
|
|
972
|
-
props?: Record<string, unknown>; // Alias for params
|
|
973
|
-
historyStrategy?: 'push' | 'replace' | 'pop' | 'silent';
|
|
974
|
-
clearQueryParams?: string[] | boolean | null; // Clear URL query params
|
|
975
|
-
_source?: 'programmatic' | 'browser' | 'initial'; // Internal use only
|
|
976
|
-
}
|
|
977
|
-
|
|
978
|
-
// Active Route - current state of an area
|
|
979
|
-
interface ActiveRoute {
|
|
980
|
-
component: string; // Always resolved to tag name
|
|
211
|
+
component: ComponentType;
|
|
981
212
|
area: string;
|
|
982
|
-
state?: Record<string,
|
|
983
|
-
params?: Record<string,
|
|
984
|
-
props?: Record<string,
|
|
213
|
+
state?: Record<string, any>;
|
|
214
|
+
params?: Record<string, any>;
|
|
215
|
+
props?: Record<string, any>; // Alias for params
|
|
216
|
+
historyStrategy?: 'push' | 'replace' | 'silent';
|
|
217
|
+
clearQueryParams?: string[] | boolean;
|
|
985
218
|
}
|
|
986
219
|
|
|
987
|
-
// Guard
|
|
988
|
-
|
|
989
|
-
type GuardFunction = () => GuardResult | Promise<GuardResult>;
|
|
220
|
+
// Guard type
|
|
221
|
+
type ObservableGuardResult = Observable<boolean>;
|
|
990
222
|
|
|
991
|
-
//
|
|
992
|
-
|
|
993
|
-
| string
|
|
994
|
-
| CustomElementConstructor
|
|
995
|
-
| HTMLElement
|
|
996
|
-
|
|
|
997
|
-
| (() => Promise<{ default: CustomElementConstructor }>) // Lazy loader
|
|
998
|
-
| Promise<{ default: CustomElementConstructor }>; // Dynamic import
|
|
223
|
+
// Component types
|
|
224
|
+
type RouteComponent =
|
|
225
|
+
| string // Tag name
|
|
226
|
+
| CustomElementConstructor // Class
|
|
227
|
+
| HTMLElement // Instance
|
|
228
|
+
| (() => Promise<{ default: Constructor }>) // Lazy
|
|
999
229
|
|
|
1000
|
-
//
|
|
1001
|
-
interface
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
230
|
+
// Redirect event detail
|
|
231
|
+
interface RedirectEventDetail {
|
|
232
|
+
blockedRoute: string;
|
|
233
|
+
area: string;
|
|
234
|
+
params: Record<string, any>;
|
|
235
|
+
state: Record<string, any>;
|
|
1006
236
|
}
|
|
237
|
+
```
|
|
1007
238
|
|
|
1008
|
-
|
|
1009
|
-
interface LazyComponent<T extends CustomElementConstructor = CustomElementConstructor> {
|
|
1010
|
-
(): Promise<{ default: T }>;
|
|
1011
|
-
preload(): Promise<void>;
|
|
1012
|
-
_promise?: Promise<{ default: T }>; // Cached loading promise
|
|
1013
|
-
_module?: { default: T }; // Cached loaded module
|
|
1014
|
-
}
|
|
239
|
+
## Internal Behavior
|
|
1015
240
|
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
241
|
+
### Route Resolution Pipeline
|
|
242
|
+
1. Receives navigation request (programmatic, URL, or browser)
|
|
243
|
+
2. Resolves lazy components to constructors
|
|
244
|
+
3. Deduplicates identical routes
|
|
245
|
+
4. Evaluates guard Observable
|
|
246
|
+
5. Creates element and applies properties
|
|
247
|
+
6. Swaps components with 150ms fade animation
|
|
1021
248
|
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
pop = 'pop',
|
|
1027
|
-
silent = 'silent'
|
|
1028
|
-
}
|
|
1029
|
-
```
|
|
249
|
+
### Route Evaluation Order
|
|
250
|
+
- Routes evaluated in DOM order
|
|
251
|
+
- First matching route wins
|
|
252
|
+
- Empty `when=""` acts as catch-all
|
|
1030
253
|
|
|
1031
|
-
|
|
254
|
+
### History Management
|
|
255
|
+
- JSON-encoded URLs by default: `/%7B%22main%22%3A%7B%22component%22%3A%22home%22%7D%7D`
|
|
256
|
+
- Pretty URLs optional: `/products?id=123`
|
|
257
|
+
- State stored in `history.state.schmancyAreas`
|
|
1032
258
|
|
|
1033
|
-
|
|
1034
|
-
- **[Layout](./layout.md)** - For responsive layouts
|
|
1035
|
-
- **[Teleport](./teleport.md)** - For advanced component transportation
|
|
1036
|
-
- **[Sheet](./sheet.md)** - For modal overlays
|
|
259
|
+
## Troubleshooting
|
|
1037
260
|
|
|
1038
|
-
|
|
261
|
+
**Routes not matching**
|
|
262
|
+
- Check route order (specific before general)
|
|
263
|
+
- Verify `when` matches URL segment or component tag
|
|
1039
264
|
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
5. **Cache guard results** when checking expensive operations
|
|
1045
|
-
6. **Use `historyStrategy: 'silent'`** for non-navigational updates
|
|
1046
|
-
7. **Debounce rapid navigation** in user-triggered events
|
|
1047
|
-
8. **Preload critical routes** after initial render using `component.preload()`
|
|
1048
|
-
9. **Leverage default exports** in lazy-loaded components for cleaner imports
|
|
1049
|
-
10. **Monitor bundle sizes** to ensure effective code splitting
|
|
265
|
+
**Guards not working**
|
|
266
|
+
- Must be `Observable<boolean>`, not functions
|
|
267
|
+
- Use `.asObservable()` on BehaviorSubjects
|
|
268
|
+
- Handle redirects in `@redirect` event
|
|
1050
269
|
|
|
1051
|
-
|
|
270
|
+
**Nested routes failing**
|
|
271
|
+
- Each area needs unique name
|
|
272
|
+
- Parent must render child `<schmancy-area>`
|
|
1052
273
|
|
|
1053
|
-
|
|
1054
|
-
-
|
|
1055
|
-
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
- ✅ **Default components** for fallback UI (area-level only)
|
|
1059
|
-
- ✅ **Reactive subscriptions** with RxJS observables
|
|
1060
|
-
- ✅ **Type-safe** API with TypeScript support
|
|
1061
|
-
- ✅ **Multiple router outlets** for complex layouts
|
|
1062
|
-
- ✅ **Proper area.pop()** sends clearing signals through RxJS pipeline
|
|
1063
|
-
- ✅ **Lazy loading** with caching and preload support
|
|
1064
|
-
- ✅ **History management** with JSON encoding or pretty URLs
|
|
1065
|
-
- ✅ **Animation transitions** during component swapping (150ms fade)
|
|
1066
|
-
- ✅ **Deduplication** prevents unnecessary re-renders
|
|
1067
|
-
- ✅ **Error handling** with graceful fallbacks
|
|
274
|
+
**area.pop() not clearing**
|
|
275
|
+
- Verify area name is correct
|
|
276
|
+
- Check no immediate re-navigation
|
|
277
|
+
|
|
278
|
+
## Related Components
|
|
1068
279
|
|
|
1069
|
-
|
|
280
|
+
- **[Store](./store.md)** - State management
|
|
281
|
+
- **[Sheet](./sheet.md)** - Modal overlays
|
|
282
|
+
- **[Layout](./layout.md)** - Responsive layouts
|