diff --git a/README.md b/README.md index 66ee9d8..8b71405 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ Add `corex` to your `mix.exs` dependencies: ```elixir def deps do [ - {:corex, "~> 0.1.0-alpha.26"} + {:corex, "~> 0.1.0-alpha.27"} ] end ``` diff --git a/design/components/clipboard.css b/design/components/clipboard.css index eba8c2f..0407fb4 100644 --- a/design/components/clipboard.css +++ b/design/components/clipboard.css @@ -61,3 +61,29 @@ display: inline-block !important; } } + +@utility clipboard--* { + [data-scope="clipboard"][data-part="root"] { + max-width: --value(--container-ui-*, [length]); + gap: --value(--spacing-ui-gap-*, [length]); + } + + [data-scope="clipboard"][data-part="control"] { + gap: --value(--spacing-ui-gap-*, [length]); + } + + [data-scope="clipboard"][data-part="trigger"] { + font-size: --value(--text-ui-*, [length]); + line-height: --value(--text-ui-* --line-height, [length]); + min-height: --value(--spacing-ui-*, [length]); + } + + [data-scope="clipboard"][data-part="label"] { + font-size: --value(--text-ui-*, [length]); + line-height: --value(--text-ui-* --line-height, [length]); + } + + [data-scope="clipboard"][data-part="input"] { + max-width: --value(--container-ui-*, [length]); + } +} diff --git a/design/components/code.css b/design/components/code.css new file mode 100644 index 0000000..f6dec12 --- /dev/null +++ b/design/components/code.css @@ -0,0 +1,126 @@ +@import "../main.css"; + +@layer components { + .code[data-scope="code"][data-part="root"] { + @apply ui-root; + } + + .code [data-scope="code"][data-part="content"] .highlight { + @apply ui-content scrollbar scrollbar--sm; + font-family: var(--font-code); + font-size: var(--text-ui-sm); + line-height: var(--text-ui-sm--line-height); + padding: var(--spacing-ui-padding); + background-color: var(--color-ui); + color: var(--color-ui--text); + border: 1px solid var(--color-ui--border); + overflow: auto; + background-image: linear-gradient( + var(--color-root) 50%, + var(--color-layer) 50% + ); + background-size: 100% calc(var(--text-ui) * var(--text-ui--line-height)); + background-origin: content-box; + background-attachment: local; + background-position: 0 calc(var(--spacing-ui-padding) / 2);; + + + & code { + width: fit-content; + padding-inline-end: var(--spacing-ui); + } + } + + + .code .highlight .unselectable { + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + } + + + [data-mode="light"] .code .highlight .hll { background-color: #fff9c4; } + [data-mode="light"] .code .highlight .bp { color: #0070c1; } + [data-mode="light"] .code .highlight .c, [data-mode="light"] .code .highlight .c1, [data-mode="light"] .code .highlight .ch, [data-mode="light"] .code .highlight .cm, [data-mode="light"] .code .highlight .cs { color: #008000; font-style: italic; } + [data-mode="light"] .code .highlight .cp, [data-mode="light"] .code .highlight .cpf { color: #0000ff; font-style: normal; } + [data-mode="light"] .code .highlight .dl { color: #a31515; } + [data-mode="light"] .code .highlight .err { border-color: #cd3131; color: #cd3131; } + [data-mode="light"] .code .highlight .fm { color: #795e26; } + [data-mode="light"] .code .highlight .gd { color: #a31515; } + [data-mode="light"] .code .highlight .ge { font-style: italic; } + [data-mode="light"] .code .highlight .gh { color: #000080; font-weight: bold; } + [data-mode="light"] .code .highlight .gi { color: #098658; } + [data-mode="light"] .code .highlight .go { color: #6e7681; } + [data-mode="light"] .code .highlight .gp { color: #000080; font-weight: bold; } + [data-mode="light"] .code .highlight .gr { color: #cd3131; } + [data-mode="light"] .code .highlight .gs { font-weight: bold; } + [data-mode="light"] .code .highlight .gt { color: #0451a5; } + [data-mode="light"] .code .highlight .gu { color: #800080; font-weight: bold; } + [data-mode="light"] .code .highlight .il { color: #098658; } + [data-mode="light"] .code .highlight .k, [data-mode="light"] .code .highlight .kd, [data-mode="light"] .code .highlight .kr, [data-mode="light"] .code .highlight .kc, [data-mode="light"] .code .highlight .kn, [data-mode="light"] .code .highlight .kp { color: #0000ff; font-weight: bold; } + [data-mode="light"] .code .highlight .kt { color: #267f99; } + [data-mode="light"] .code .highlight .m, [data-mode="light"] .code .highlight .mb, [data-mode="light"] .code .highlight .mf, [data-mode="light"] .code .highlight .mh, [data-mode="light"] .code .highlight .mi, [data-mode="light"] .code .highlight .mo { color: #098658; } + [data-mode="light"] .code .highlight .na { color: #e50000; } + [data-mode="light"] .code .highlight .nb { color: #267f99; } + [data-mode="light"] .code .highlight .nc, [data-mode="light"] .code .highlight .nn { color: #267f99; font-weight: bold; } + [data-mode="light"] .code .highlight .nd { color: #af00db; } + [data-mode="light"] .code .highlight .ne { color: #cd3131; font-weight: bold; } + [data-mode="light"] .code .highlight .nf { color: #795e26; } + [data-mode="light"] .code .highlight .ni { color: #6e7681; font-weight: bold; } + [data-mode="light"] .code .highlight .nl { color: #000000; } + [data-mode="light"] .code .highlight .no { color: #0070c1; } + [data-mode="light"] .code .highlight .nt { color: #800000; font-weight: bold; } + [data-mode="light"] .code .highlight .nv, [data-mode="light"] .code .highlight .vc, [data-mode="light"] .code .highlight .vg, [data-mode="light"] .code .highlight .vi, [data-mode="light"] .code .highlight .vm { color: #001080; } + [data-mode="light"] .code .highlight .o { color: #000000; } + [data-mode="light"] .code .highlight .ow { color: #0000ff; font-weight: bold; } + [data-mode="light"] .code .highlight .s, [data-mode="light"] .code .highlight .s1, [data-mode="light"] .code .highlight .s2, [data-mode="light"] .code .highlight .sa, [data-mode="light"] .code .highlight .sb, [data-mode="light"] .code .highlight .sc, [data-mode="light"] .code .highlight .sh, [data-mode="light"] .code .highlight .sx { color: #a31515; } + [data-mode="light"] .code .highlight .sd { color: #a31515; font-style: italic; } + [data-mode="light"] .code .highlight .se { color: #ee0000; font-weight: bold; } + [data-mode="light"] .code .highlight .si { color: #811f3f; font-weight: bold; } + [data-mode="light"] .code .highlight .sr { color: #811f3f; } + [data-mode="light"] .code .highlight .ss { color: #001080; } + + [data-mode="dark"] .code .highlight .hll { background-color: #264f78; } + [data-mode="dark"] .code .highlight .bp { color: #4ec9b0; } + [data-mode="dark"] .code .highlight .c, [data-mode="dark"] .code .highlight .c1, [data-mode="dark"] .code .highlight .ch, [data-mode="dark"] .code .highlight .cm, [data-mode="dark"] .code .highlight .cs { color: #6a9955; font-style: italic; } + [data-mode="dark"] .code .highlight .cp, [data-mode="dark"] .code .highlight .cpf { color: #569cd6; font-style: normal; } + [data-mode="dark"] .code .highlight .dl { color: #ce9178; } + [data-mode="dark"] .code .highlight .err { border-color: #f44747; color: #f44747; } + [data-mode="dark"] .code .highlight .fm { color: #dcdcaa; } + [data-mode="dark"] .code .highlight .gd { color: #ce9178; } + [data-mode="dark"] .code .highlight .ge { font-style: italic; } + [data-mode="dark"] .code .highlight .gh { color: #569cd6; font-weight: bold; } + [data-mode="dark"] .code .highlight .gi { color: #b5cea8; } + [data-mode="dark"] .code .highlight .go { color: #808080; } + [data-mode="dark"] .code .highlight .gp { color: #569cd6; font-weight: bold; } + [data-mode="dark"] .code .highlight .gr { color: #f44747; } + [data-mode="dark"] .code .highlight .gs { font-weight: bold; } + [data-mode="dark"] .code .highlight .gt { color: #4ec9b0; } + [data-mode="dark"] .code .highlight .gu { color: #c586c0; font-weight: bold; } + [data-mode="dark"] .code .highlight .il { color: #b5cea8; } + [data-mode="dark"] .code .highlight .k, [data-mode="dark"] .code .highlight .kd, [data-mode="dark"] .code .highlight .kr, [data-mode="dark"] .code .highlight .kc, [data-mode="dark"] .code .highlight .kn, [data-mode="dark"] .code .highlight .kp { color: #569cd6; font-weight: bold; } + [data-mode="dark"] .code .highlight .kt { color: #4ec9b0; } + [data-mode="dark"] .code .highlight .m, [data-mode="dark"] .code .highlight .mb, [data-mode="dark"] .code .highlight .mf, [data-mode="dark"] .code .highlight .mh, [data-mode="dark"] .code .highlight .mi, [data-mode="dark"] .code .highlight .mo { color: #b5cea8; } + [data-mode="dark"] .code .highlight .na { color: #9cdcfe; } + [data-mode="dark"] .code .highlight .nb { color: #4ec9b0; } + [data-mode="dark"] .code .highlight .nc, [data-mode="dark"] .code .highlight .nn { color: #4ec9b0; font-weight: bold; } + [data-mode="dark"] .code .highlight .nd { color: #c586c0; } + [data-mode="dark"] .code .highlight .ne { color: #f44747; font-weight: bold; } + [data-mode="dark"] .code .highlight .nf { color: #dcdcaa; } + [data-mode="dark"] .code .highlight .ni { color: #808080; font-weight: bold; } + [data-mode="dark"] .code .highlight .nl { color: #c8c8c8; } + [data-mode="dark"] .code .highlight .no { color: #4fc1ff; } + [data-mode="dark"] .code .highlight .nt { color: #569cd6; font-weight: bold; } + [data-mode="dark"] .code .highlight .nv, [data-mode="dark"] .code .highlight .vc, [data-mode="dark"] .code .highlight .vg, [data-mode="dark"] .code .highlight .vi, [data-mode="dark"] .code .highlight .vm { color: #9cdcfe; } + [data-mode="dark"] .code .highlight .o { color: #d4d4d4; } + [data-mode="dark"] .code .highlight .ow { color: #569cd6; font-weight: bold; } + [data-mode="dark"] .code .highlight .s, [data-mode="dark"] .code .highlight .s1, [data-mode="dark"] .code .highlight .s2, [data-mode="dark"] .code .highlight .sa, [data-mode="dark"] .code .highlight .sb, [data-mode="dark"] .code .highlight .sc, [data-mode="dark"] .code .highlight .sh, [data-mode="dark"] .code .highlight .sx { color: #ce9178; } + [data-mode="dark"] .code .highlight .sd { color: #ce9178; font-style: italic; } + [data-mode="dark"] .code .highlight .se { color: #d7ba7d; font-weight: bold; } + [data-mode="dark"] .code .highlight .si { color: #d7ba7d; font-weight: bold; } + [data-mode="dark"] .code .highlight .sr { color: #d16969; } + [data-mode="dark"] .code .highlight .ss { color: #9cdcfe; } +} diff --git a/design/components/typo.css b/design/components/typo.css index 2729c4b..6825f93 100644 --- a/design/components/typo.css +++ b/design/components/typo.css @@ -286,16 +286,4 @@ gap: var(--spacing-ui-gap-lg); } - .typo pre { - @apply scrollbar scrollbar--sm; - - font-family: var(--font-code); - font-size: var(--text-ui-sm); - line-height: var(--text-ui-sm--line-height); - padding: var(--spacing-ui-padding-sm); - background-color: var(--color-ui); - color: var(--color-ui--text); - border: 1px solid var(--color-ui--border); - overflow: auto; - } } diff --git a/e2e/assets/corex/components/clipboard.css b/e2e/assets/corex/components/clipboard.css index eba8c2f..0407fb4 100644 --- a/e2e/assets/corex/components/clipboard.css +++ b/e2e/assets/corex/components/clipboard.css @@ -61,3 +61,29 @@ display: inline-block !important; } } + +@utility clipboard--* { + [data-scope="clipboard"][data-part="root"] { + max-width: --value(--container-ui-*, [length]); + gap: --value(--spacing-ui-gap-*, [length]); + } + + [data-scope="clipboard"][data-part="control"] { + gap: --value(--spacing-ui-gap-*, [length]); + } + + [data-scope="clipboard"][data-part="trigger"] { + font-size: --value(--text-ui-*, [length]); + line-height: --value(--text-ui-* --line-height, [length]); + min-height: --value(--spacing-ui-*, [length]); + } + + [data-scope="clipboard"][data-part="label"] { + font-size: --value(--text-ui-*, [length]); + line-height: --value(--text-ui-* --line-height, [length]); + } + + [data-scope="clipboard"][data-part="input"] { + max-width: --value(--container-ui-*, [length]); + } +} diff --git a/e2e/assets/corex/components/code.css b/e2e/assets/corex/components/code.css new file mode 100644 index 0000000..f6dec12 --- /dev/null +++ b/e2e/assets/corex/components/code.css @@ -0,0 +1,126 @@ +@import "../main.css"; + +@layer components { + .code[data-scope="code"][data-part="root"] { + @apply ui-root; + } + + .code [data-scope="code"][data-part="content"] .highlight { + @apply ui-content scrollbar scrollbar--sm; + font-family: var(--font-code); + font-size: var(--text-ui-sm); + line-height: var(--text-ui-sm--line-height); + padding: var(--spacing-ui-padding); + background-color: var(--color-ui); + color: var(--color-ui--text); + border: 1px solid var(--color-ui--border); + overflow: auto; + background-image: linear-gradient( + var(--color-root) 50%, + var(--color-layer) 50% + ); + background-size: 100% calc(var(--text-ui) * var(--text-ui--line-height)); + background-origin: content-box; + background-attachment: local; + background-position: 0 calc(var(--spacing-ui-padding) / 2);; + + + & code { + width: fit-content; + padding-inline-end: var(--spacing-ui); + } + } + + + .code .highlight .unselectable { + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + } + + + [data-mode="light"] .code .highlight .hll { background-color: #fff9c4; } + [data-mode="light"] .code .highlight .bp { color: #0070c1; } + [data-mode="light"] .code .highlight .c, [data-mode="light"] .code .highlight .c1, [data-mode="light"] .code .highlight .ch, [data-mode="light"] .code .highlight .cm, [data-mode="light"] .code .highlight .cs { color: #008000; font-style: italic; } + [data-mode="light"] .code .highlight .cp, [data-mode="light"] .code .highlight .cpf { color: #0000ff; font-style: normal; } + [data-mode="light"] .code .highlight .dl { color: #a31515; } + [data-mode="light"] .code .highlight .err { border-color: #cd3131; color: #cd3131; } + [data-mode="light"] .code .highlight .fm { color: #795e26; } + [data-mode="light"] .code .highlight .gd { color: #a31515; } + [data-mode="light"] .code .highlight .ge { font-style: italic; } + [data-mode="light"] .code .highlight .gh { color: #000080; font-weight: bold; } + [data-mode="light"] .code .highlight .gi { color: #098658; } + [data-mode="light"] .code .highlight .go { color: #6e7681; } + [data-mode="light"] .code .highlight .gp { color: #000080; font-weight: bold; } + [data-mode="light"] .code .highlight .gr { color: #cd3131; } + [data-mode="light"] .code .highlight .gs { font-weight: bold; } + [data-mode="light"] .code .highlight .gt { color: #0451a5; } + [data-mode="light"] .code .highlight .gu { color: #800080; font-weight: bold; } + [data-mode="light"] .code .highlight .il { color: #098658; } + [data-mode="light"] .code .highlight .k, [data-mode="light"] .code .highlight .kd, [data-mode="light"] .code .highlight .kr, [data-mode="light"] .code .highlight .kc, [data-mode="light"] .code .highlight .kn, [data-mode="light"] .code .highlight .kp { color: #0000ff; font-weight: bold; } + [data-mode="light"] .code .highlight .kt { color: #267f99; } + [data-mode="light"] .code .highlight .m, [data-mode="light"] .code .highlight .mb, [data-mode="light"] .code .highlight .mf, [data-mode="light"] .code .highlight .mh, [data-mode="light"] .code .highlight .mi, [data-mode="light"] .code .highlight .mo { color: #098658; } + [data-mode="light"] .code .highlight .na { color: #e50000; } + [data-mode="light"] .code .highlight .nb { color: #267f99; } + [data-mode="light"] .code .highlight .nc, [data-mode="light"] .code .highlight .nn { color: #267f99; font-weight: bold; } + [data-mode="light"] .code .highlight .nd { color: #af00db; } + [data-mode="light"] .code .highlight .ne { color: #cd3131; font-weight: bold; } + [data-mode="light"] .code .highlight .nf { color: #795e26; } + [data-mode="light"] .code .highlight .ni { color: #6e7681; font-weight: bold; } + [data-mode="light"] .code .highlight .nl { color: #000000; } + [data-mode="light"] .code .highlight .no { color: #0070c1; } + [data-mode="light"] .code .highlight .nt { color: #800000; font-weight: bold; } + [data-mode="light"] .code .highlight .nv, [data-mode="light"] .code .highlight .vc, [data-mode="light"] .code .highlight .vg, [data-mode="light"] .code .highlight .vi, [data-mode="light"] .code .highlight .vm { color: #001080; } + [data-mode="light"] .code .highlight .o { color: #000000; } + [data-mode="light"] .code .highlight .ow { color: #0000ff; font-weight: bold; } + [data-mode="light"] .code .highlight .s, [data-mode="light"] .code .highlight .s1, [data-mode="light"] .code .highlight .s2, [data-mode="light"] .code .highlight .sa, [data-mode="light"] .code .highlight .sb, [data-mode="light"] .code .highlight .sc, [data-mode="light"] .code .highlight .sh, [data-mode="light"] .code .highlight .sx { color: #a31515; } + [data-mode="light"] .code .highlight .sd { color: #a31515; font-style: italic; } + [data-mode="light"] .code .highlight .se { color: #ee0000; font-weight: bold; } + [data-mode="light"] .code .highlight .si { color: #811f3f; font-weight: bold; } + [data-mode="light"] .code .highlight .sr { color: #811f3f; } + [data-mode="light"] .code .highlight .ss { color: #001080; } + + [data-mode="dark"] .code .highlight .hll { background-color: #264f78; } + [data-mode="dark"] .code .highlight .bp { color: #4ec9b0; } + [data-mode="dark"] .code .highlight .c, [data-mode="dark"] .code .highlight .c1, [data-mode="dark"] .code .highlight .ch, [data-mode="dark"] .code .highlight .cm, [data-mode="dark"] .code .highlight .cs { color: #6a9955; font-style: italic; } + [data-mode="dark"] .code .highlight .cp, [data-mode="dark"] .code .highlight .cpf { color: #569cd6; font-style: normal; } + [data-mode="dark"] .code .highlight .dl { color: #ce9178; } + [data-mode="dark"] .code .highlight .err { border-color: #f44747; color: #f44747; } + [data-mode="dark"] .code .highlight .fm { color: #dcdcaa; } + [data-mode="dark"] .code .highlight .gd { color: #ce9178; } + [data-mode="dark"] .code .highlight .ge { font-style: italic; } + [data-mode="dark"] .code .highlight .gh { color: #569cd6; font-weight: bold; } + [data-mode="dark"] .code .highlight .gi { color: #b5cea8; } + [data-mode="dark"] .code .highlight .go { color: #808080; } + [data-mode="dark"] .code .highlight .gp { color: #569cd6; font-weight: bold; } + [data-mode="dark"] .code .highlight .gr { color: #f44747; } + [data-mode="dark"] .code .highlight .gs { font-weight: bold; } + [data-mode="dark"] .code .highlight .gt { color: #4ec9b0; } + [data-mode="dark"] .code .highlight .gu { color: #c586c0; font-weight: bold; } + [data-mode="dark"] .code .highlight .il { color: #b5cea8; } + [data-mode="dark"] .code .highlight .k, [data-mode="dark"] .code .highlight .kd, [data-mode="dark"] .code .highlight .kr, [data-mode="dark"] .code .highlight .kc, [data-mode="dark"] .code .highlight .kn, [data-mode="dark"] .code .highlight .kp { color: #569cd6; font-weight: bold; } + [data-mode="dark"] .code .highlight .kt { color: #4ec9b0; } + [data-mode="dark"] .code .highlight .m, [data-mode="dark"] .code .highlight .mb, [data-mode="dark"] .code .highlight .mf, [data-mode="dark"] .code .highlight .mh, [data-mode="dark"] .code .highlight .mi, [data-mode="dark"] .code .highlight .mo { color: #b5cea8; } + [data-mode="dark"] .code .highlight .na { color: #9cdcfe; } + [data-mode="dark"] .code .highlight .nb { color: #4ec9b0; } + [data-mode="dark"] .code .highlight .nc, [data-mode="dark"] .code .highlight .nn { color: #4ec9b0; font-weight: bold; } + [data-mode="dark"] .code .highlight .nd { color: #c586c0; } + [data-mode="dark"] .code .highlight .ne { color: #f44747; font-weight: bold; } + [data-mode="dark"] .code .highlight .nf { color: #dcdcaa; } + [data-mode="dark"] .code .highlight .ni { color: #808080; font-weight: bold; } + [data-mode="dark"] .code .highlight .nl { color: #c8c8c8; } + [data-mode="dark"] .code .highlight .no { color: #4fc1ff; } + [data-mode="dark"] .code .highlight .nt { color: #569cd6; font-weight: bold; } + [data-mode="dark"] .code .highlight .nv, [data-mode="dark"] .code .highlight .vc, [data-mode="dark"] .code .highlight .vg, [data-mode="dark"] .code .highlight .vi, [data-mode="dark"] .code .highlight .vm { color: #9cdcfe; } + [data-mode="dark"] .code .highlight .o { color: #d4d4d4; } + [data-mode="dark"] .code .highlight .ow { color: #569cd6; font-weight: bold; } + [data-mode="dark"] .code .highlight .s, [data-mode="dark"] .code .highlight .s1, [data-mode="dark"] .code .highlight .s2, [data-mode="dark"] .code .highlight .sa, [data-mode="dark"] .code .highlight .sb, [data-mode="dark"] .code .highlight .sc, [data-mode="dark"] .code .highlight .sh, [data-mode="dark"] .code .highlight .sx { color: #ce9178; } + [data-mode="dark"] .code .highlight .sd { color: #ce9178; font-style: italic; } + [data-mode="dark"] .code .highlight .se { color: #d7ba7d; font-weight: bold; } + [data-mode="dark"] .code .highlight .si { color: #d7ba7d; font-weight: bold; } + [data-mode="dark"] .code .highlight .sr { color: #d16969; } + [data-mode="dark"] .code .highlight .ss { color: #9cdcfe; } +} diff --git a/e2e/assets/corex/components/typo.css b/e2e/assets/corex/components/typo.css index 2729c4b..947ee9d 100644 --- a/e2e/assets/corex/components/typo.css +++ b/e2e/assets/corex/components/typo.css @@ -285,17 +285,4 @@ flex-direction: column; gap: var(--spacing-ui-gap-lg); } - - .typo pre { - @apply scrollbar scrollbar--sm; - - font-family: var(--font-code); - font-size: var(--text-ui-sm); - line-height: var(--text-ui-sm--line-height); - padding: var(--spacing-ui-padding-sm); - background-color: var(--color-ui); - color: var(--color-ui--text); - border: 1px solid var(--color-ui--border); - overflow: auto; - } } diff --git a/e2e/assets/css/app.css b/e2e/assets/css/app.css index 518c01b..b96ffff 100644 --- a/e2e/assets/css/app.css +++ b/e2e/assets/css/app.css @@ -10,6 +10,12 @@ @import "../corex/main.css"; @import "../corex/tokens/themes/neo/light.css"; @import "../corex/tokens/themes/neo/dark.css"; +@import "../corex/tokens/themes/uno/light.css"; +@import "../corex/tokens/themes/uno/dark.css"; +@import "../corex/tokens/themes/duo/light.css"; +@import "../corex/tokens/themes/duo/dark.css"; +@import "../corex/tokens/themes/leo/light.css"; +@import "../corex/tokens/themes/leo/dark.css"; @import "../corex/components/typo.css"; @import "../corex/components/accordion.css"; @import "../corex/components/layout.css"; @@ -43,6 +49,7 @@ @import "../corex/components/pin-input.css"; @import "../corex/components/radio-group.css"; @import "../corex/components/timer.css"; +@import "../corex/components/code.css"; @import "./navigation.css"; @@ -50,7 +57,10 @@ #locale-select.select, #locale-select.select [data-scope="select"][data-part="root"], -#locale-select.select [data-scope="select"][data-part="trigger"] { +#locale-select.select [data-scope="select"][data-part="trigger"], +#theme-select.select, +#theme-select.select [data-scope="select"][data-part="root"], +#theme-select.select [data-scope="select"][data-part="trigger"] { width: auto; } diff --git a/e2e/assets/css/code_highlight.css b/e2e/assets/css/code_highlight.css new file mode 100644 index 0000000..24b6479 --- /dev/null +++ b/e2e/assets/css/code_highlight.css @@ -0,0 +1,77 @@ +.highlight .hll {background-color: #ffffcc} +.highlight {background-color: #f8f8f8}.highlight .unselectable { + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.highlight .bp {color: #008000; } /* :name_builtin_pseudo */ +.highlight .c {color: #408080; font-style: italic; } /* :comment */ +.highlight .c1 {color: #408080; font-style: italic; } /* :comment_single */ +.highlight .ch {color: #408080; font-style: italic; } /* :comment_hashbang */ +.highlight .cm {color: #408080; font-style: italic; } /* :comment_multiline */ +.highlight .cp {color: #BC7A00; font-style: normal; } /* :comment_preproc */ +.highlight .cpf {color: #BC7A00; font-style: normal; } /* :comment_preproc_file */ +.highlight .cs {color: #408080; font-style: italic; } /* :comment_special */ +.highlight .dl {color: #BA2121; } /* :string_delimiter */ +.highlight .err {border: #FF0000; } /* :error */ +.highlight .fm {color: #0000FF; } /* :name_function_magic */ +.highlight .gd {color: #A00000; } /* :generic_deleted */ +.highlight .ge {font-style: italic; } /* :generic_emph */ +.highlight .gh {color: #000080; font-weight: bold; } /* :generic_heading */ +.highlight .gi {color: #00A000; } /* :generic_inserted */ +.highlight .go {color: #888; } /* :generic_output */ +.highlight .gp {color: #000080; font-weight: bold; } /* :generic_prompt */ +.highlight .gr {color: #FF0000; } /* :generic_error */ +.highlight .gs {font-weight: bold; } /* :generic_strong */ +.highlight .gt {color: #04D; } /* :generic_traceback */ +.highlight .gu {color: #800080; font-weight: bold; } /* :generic_subheading */ +.highlight .il {color: #666666; } /* :number_integer_long */ +.highlight .k {color: #008000; font-weight: bold; } /* :keyword */ +.highlight .kc {color: #008000; font-weight: bold; } /* :keyword_constant */ +.highlight .kd {color: #008000; font-weight: bold; } /* :keyword_declaration */ +.highlight .kn {color: #008000; font-weight: bold; } /* :keyword_namespace */ +.highlight .kp {color: #008000; font-weight: normal; } /* :keyword_pseudo */ +.highlight .kr {color: #008000; font-weight: bold; } /* :keyword_reserved */ +.highlight .kt {color: #B00040; font-weight: normal; } /* :keyword_type */ +.highlight .m {color: #666666; } /* :number */ +.highlight .mb {color: #666666; } /* :number_bin */ +.highlight .mf {color: #666666; } /* :number_float */ +.highlight .mh {color: #666666; } /* :number_hex */ +.highlight .mi {color: #666666; } /* :number_integer */ +.highlight .mo {color: #666666; } /* :number_oct */ +.highlight .na {color: #7D9029; } /* :name_attribute */ +.highlight .nb {color: #008000; } /* :name_builtin */ +.highlight .nc {color: #0000FF; font-weight: bold; } /* :name_class */ +.highlight .nd {color: #AA22FF; } /* :name_decorator */ +.highlight .ne {color: #D2413A; font-weight: bold; } /* :name_exception */ +.highlight .nf {color: #0000FF; } /* :name_function */ +.highlight .ni {color: #999999; font-weight: bold; } /* :name_entity */ +.highlight .nl {color: #A0A000; } /* :name_label */ +.highlight .nn {color: #0000FF; font-weight: bold; } /* :name_namespace */ +.highlight .no {color: #880000; } /* :name_constant */ +.highlight .nt {color: #008000; font-weight: bold; } /* :name_tag */ +.highlight .nv {color: #19177C; } /* :name_variable */ +.highlight .o {color: #666666; } /* :operator */ +.highlight .ow {color: #AA22FF; font-weight: bold; } /* :operator_word */ +.highlight .s {color: #BA2121; } /* :string */ +.highlight .s1 {color: #BA2121; } /* :string_single */ +.highlight .s2 {color: #BA2121; } /* :string_double */ +.highlight .sa {color: #BA2121; } /* :string_affix */ +.highlight .sb {color: #BA2121; } /* :string_backtick */ +.highlight .sc {color: #BA2121; } /* :string_char */ +.highlight .sd {color: #BA2121; font-style: italic; } /* :string_doc */ +.highlight .se {color: #BB6622; font-weight: bold; } /* :string_escape */ +.highlight .sh {color: #BA2121; } /* :string_heredoc */ +.highlight .si {color: #BB6688; font-weight: bold; } /* :string_interpol */ +.highlight .sr {color: #BB6688; } /* :string_regex */ +.highlight .ss {color: #19177C; } /* :string_symbol */ +.highlight .sx {color: #008000; } /* :string_other */ +.highlight .sx {color: #BA2121; } /* :string_sigil */ +.highlight .vc {color: #19177C; } /* :name_variable_class */ +.highlight .vg {color: #19177C; } /* :name_variable_global */ +.highlight .vi {color: #19177C; } /* :name_variable_instance */ +.highlight .vm {color: #19177C; } /* :name_variable_magic */ diff --git a/e2e/assets/styles/custom.css b/e2e/assets/styles/custom.css new file mode 100644 index 0000000..24b6479 --- /dev/null +++ b/e2e/assets/styles/custom.css @@ -0,0 +1,77 @@ +.highlight .hll {background-color: #ffffcc} +.highlight {background-color: #f8f8f8}.highlight .unselectable { + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.highlight .bp {color: #008000; } /* :name_builtin_pseudo */ +.highlight .c {color: #408080; font-style: italic; } /* :comment */ +.highlight .c1 {color: #408080; font-style: italic; } /* :comment_single */ +.highlight .ch {color: #408080; font-style: italic; } /* :comment_hashbang */ +.highlight .cm {color: #408080; font-style: italic; } /* :comment_multiline */ +.highlight .cp {color: #BC7A00; font-style: normal; } /* :comment_preproc */ +.highlight .cpf {color: #BC7A00; font-style: normal; } /* :comment_preproc_file */ +.highlight .cs {color: #408080; font-style: italic; } /* :comment_special */ +.highlight .dl {color: #BA2121; } /* :string_delimiter */ +.highlight .err {border: #FF0000; } /* :error */ +.highlight .fm {color: #0000FF; } /* :name_function_magic */ +.highlight .gd {color: #A00000; } /* :generic_deleted */ +.highlight .ge {font-style: italic; } /* :generic_emph */ +.highlight .gh {color: #000080; font-weight: bold; } /* :generic_heading */ +.highlight .gi {color: #00A000; } /* :generic_inserted */ +.highlight .go {color: #888; } /* :generic_output */ +.highlight .gp {color: #000080; font-weight: bold; } /* :generic_prompt */ +.highlight .gr {color: #FF0000; } /* :generic_error */ +.highlight .gs {font-weight: bold; } /* :generic_strong */ +.highlight .gt {color: #04D; } /* :generic_traceback */ +.highlight .gu {color: #800080; font-weight: bold; } /* :generic_subheading */ +.highlight .il {color: #666666; } /* :number_integer_long */ +.highlight .k {color: #008000; font-weight: bold; } /* :keyword */ +.highlight .kc {color: #008000; font-weight: bold; } /* :keyword_constant */ +.highlight .kd {color: #008000; font-weight: bold; } /* :keyword_declaration */ +.highlight .kn {color: #008000; font-weight: bold; } /* :keyword_namespace */ +.highlight .kp {color: #008000; font-weight: normal; } /* :keyword_pseudo */ +.highlight .kr {color: #008000; font-weight: bold; } /* :keyword_reserved */ +.highlight .kt {color: #B00040; font-weight: normal; } /* :keyword_type */ +.highlight .m {color: #666666; } /* :number */ +.highlight .mb {color: #666666; } /* :number_bin */ +.highlight .mf {color: #666666; } /* :number_float */ +.highlight .mh {color: #666666; } /* :number_hex */ +.highlight .mi {color: #666666; } /* :number_integer */ +.highlight .mo {color: #666666; } /* :number_oct */ +.highlight .na {color: #7D9029; } /* :name_attribute */ +.highlight .nb {color: #008000; } /* :name_builtin */ +.highlight .nc {color: #0000FF; font-weight: bold; } /* :name_class */ +.highlight .nd {color: #AA22FF; } /* :name_decorator */ +.highlight .ne {color: #D2413A; font-weight: bold; } /* :name_exception */ +.highlight .nf {color: #0000FF; } /* :name_function */ +.highlight .ni {color: #999999; font-weight: bold; } /* :name_entity */ +.highlight .nl {color: #A0A000; } /* :name_label */ +.highlight .nn {color: #0000FF; font-weight: bold; } /* :name_namespace */ +.highlight .no {color: #880000; } /* :name_constant */ +.highlight .nt {color: #008000; font-weight: bold; } /* :name_tag */ +.highlight .nv {color: #19177C; } /* :name_variable */ +.highlight .o {color: #666666; } /* :operator */ +.highlight .ow {color: #AA22FF; font-weight: bold; } /* :operator_word */ +.highlight .s {color: #BA2121; } /* :string */ +.highlight .s1 {color: #BA2121; } /* :string_single */ +.highlight .s2 {color: #BA2121; } /* :string_double */ +.highlight .sa {color: #BA2121; } /* :string_affix */ +.highlight .sb {color: #BA2121; } /* :string_backtick */ +.highlight .sc {color: #BA2121; } /* :string_char */ +.highlight .sd {color: #BA2121; font-style: italic; } /* :string_doc */ +.highlight .se {color: #BB6622; font-weight: bold; } /* :string_escape */ +.highlight .sh {color: #BA2121; } /* :string_heredoc */ +.highlight .si {color: #BB6688; font-weight: bold; } /* :string_interpol */ +.highlight .sr {color: #BB6688; } /* :string_regex */ +.highlight .ss {color: #19177C; } /* :string_symbol */ +.highlight .sx {color: #008000; } /* :string_other */ +.highlight .sx {color: #BA2121; } /* :string_sigil */ +.highlight .vc {color: #19177C; } /* :name_variable_class */ +.highlight .vg {color: #19177C; } /* :name_variable_global */ +.highlight .vi {color: #19177C; } /* :name_variable_instance */ +.highlight .vm {color: #19177C; } /* :name_variable_magic */ diff --git a/e2e/lib/e2e/place/helper.ex b/e2e/lib/e2e/place/helper.ex index e72d616..8d54fa5 100644 --- a/e2e/lib/e2e/place/helper.ex +++ b/e2e/lib/e2e/place/helper.ex @@ -1,7 +1,6 @@ defmodule E2e.Place.Helper do @moduledoc false - alias E2e.Place - + alias E2e.Repo require Logger def country_name_from_code(country_code) do @@ -9,40 +8,64 @@ defmodule E2e.Place.Helper do country_name end - # Save from seeds def fetch_and_insert_cities() do - priv_dir = :code.priv_dir(:e2e) - file_path = Path.join([priv_dir, "repo", "seeds", "cities.tar.gz"]) - - {:ok, file} = File.open(file_path, [:read, :utf8, :compressed]) + read_compressed("cities.tar.gz") + |> Jason.decode!() + |> Enum.chunk_every(500) + |> Enum.each(fn batch -> + entries = + Enum.map(batch, fn city -> + %{ + id: city["id"], + name: city["name"], + iata_code: city["iata_code"], + iata_country_code: city["iata_country_code"], + # embedded airports stored as jsonb + airports: city["airports"] |> encode_embed() + } + end) - all_cities = IO.read(file, :eof) + Repo.insert_all("cities", entries, on_conflict: :nothing, conflict_target: :id) + end) + end - File.close(file) + def fetch_and_insert_airports() do + read_compressed("airports.tar.gz") + |> Jason.decode!() + |> Enum.chunk_every(500) + |> Enum.each(fn batch -> + entries = + Enum.map(batch, fn airport -> + %{ + id: airport["id"], + name: airport["name"], + iata_code: airport["iata_code"], + city_name: airport["city_name"], + iata_city_code: airport["iata_city_code"], + iata_country_code: airport["iata_country_code"], + icao_code: airport["icao_code"], + latitude: airport["latitude"], + longitude: airport["longitude"], + time_zone: airport["time_zone"], + # embedded city stored as jsonb + city: airport["city"] |> encode_embed() + } + end) - for city <- Jason.decode!(all_cities) do - case Place.create_city(city) do - {:error, changeset} -> Logger.debug(changeset) - {:ok, _city} -> :ok - end - end + Repo.insert_all("airports", entries, on_conflict: :nothing, conflict_target: :id) + end) end - def fetch_and_insert_airports() do + defp read_compressed(filename) do priv_dir = :code.priv_dir(:e2e) - file_path = Path.join([priv_dir, "repo", "seeds", "airports.tar.gz"]) - + file_path = Path.join([priv_dir, "repo", "seeds", filename]) {:ok, file} = File.open(file_path, [:read, :utf8, :compressed]) - - all_airports = IO.read(file, :eof) - + data = IO.read(file, :eof) File.close(file) - - for airport <- Jason.decode!(all_airports) do - case Place.create_airport(airport) do - {:error, changeset} -> Logger.debug(changeset) - {:ok, _airport} -> :ok - end - end + data end + + defp encode_embed(nil), do: nil + defp encode_embed(data) when is_list(data), do: data + defp encode_embed(data) when is_map(data), do: data end diff --git a/e2e/lib/e2e_web/code_examples.ex b/e2e/lib/e2e_web/code_examples.ex new file mode 100644 index 0000000..42d4e0f --- /dev/null +++ b/e2e/lib/e2e_web/code_examples.ex @@ -0,0 +1,21 @@ +defmodule E2eWeb.CodeExamples do + @code_examples_path Path.join(:code.priv_dir(:e2e), "code_examples") + + def load(name) do + path = Path.join(@code_examples_path, name) + + case File.read(path) do + {:ok, content} -> content + {:error, _} -> "" + end + end + + def all do + %{ + elixir: load("hello.ex"), + html: load("component.html"), + css: load("styles.css"), + js: load("app.js") + } + end +end diff --git a/e2e/lib/e2e_web/components/layouts.ex b/e2e/lib/e2e_web/components/layouts.ex index 3350070..ba01c8c 100644 --- a/e2e/lib/e2e_web/components/layouts.ex +++ b/e2e/lib/e2e_web/components/layouts.ex @@ -20,7 +20,7 @@ defmodule E2eWeb.Layouts do ## Examples - +

Content

@@ -29,6 +29,8 @@ defmodule E2eWeb.Layouts do attr :mode, :string, default: "light", doc: "the mode (dark or light) from cookie/session" + attr :theme, :string, default: "neo", doc: "the theme (neo, uno, duo, leo) from cookie/session" + attr :current_scope, :map, default: nil, doc: "the current [scope](https://hexdocs.pm/phoenix/scopes.html)" @@ -56,8 +58,8 @@ defmodule E2eWeb.Layouts do ~H"""
-
-
+
+
<.dialog id="menu-dialog" class="dialog dialog--side lg:hidden"> <:trigger class="button button--sm button--circle rounded-full" aria_label="Open menu"> <.icon name="hero-bars-3" class="icon" /> @@ -126,11 +128,12 @@ defmodule E2eWeb.Layouts do fill="var(--color-layer--brand)" /> - Corex +
-
+
<.locale_switcher :if={@locale} locale={@locale} current_path={@current_path} /> + <.theme_toggle theme={@theme} /> <.mode_toggle mode={@mode} />
@@ -253,6 +256,44 @@ defmodule E2eWeb.Layouts do """ end + attr :theme, :string, + default: "neo", + values: ["neo", "uno", "duo", "leo"], + doc: "the theme from cookie/session" + + @doc """ + Provides theme selection using the select component. + """ + def theme_toggle(assigns) do + ~H""" + <.select + id="theme-select" + class="select select--sm select--micro" + collection={[ + %{id: "neo", label: "Neo"}, + %{id: "uno", label: "Uno"}, + %{id: "duo", label: "Duo"}, + %{id: "leo", label: "Leo"} + ]} + value={[@theme]} + on_value_change_client="phx:set-theme" + > + <:label class="sr-only"> + Theme + + <:item :let={item}> + {item.label} + + <:trigger> + <.icon name="hero-swatch" /> + + <:item_indicator> + <.icon name="hero-check" /> + + + """ + end + attr :mode, :string, default: "light", values: ["light", "dark"], @@ -260,7 +301,6 @@ defmodule E2eWeb.Layouts do @doc """ Provides dark vs light theme toggle using toggle_group. - """ def mode_toggle(assigns) do ~H""" @@ -334,6 +374,14 @@ defmodule E2eWeb.Layouts do [label: "Async", id: "/#{locale}/async/accordion"] ] ], + [ + label: "Action", + id: "action", + children: [ + [label: "Controller", id: "/#{locale}/action"], + [label: "Live", id: "/#{locale}/live/action"] + ] + ], [ label: "Angle Slider", id: "angle-slider", @@ -376,6 +424,14 @@ defmodule E2eWeb.Layouts do [label: "Live", id: "/#{locale}/live/clipboard"] ] ], + [ + label: "Code", + id: "code", + children: [ + [label: "Controller", id: "/#{locale}/code"], + [label: "Live", id: "/#{locale}/live/code"] + ] + ], [ label: "Collapsible", id: "collapsible", @@ -442,6 +498,14 @@ defmodule E2eWeb.Layouts do [label: "Live", id: "/#{locale}/live/menu"] ] ], + [ + label: "Navigate", + id: "navigate", + children: [ + [label: "Controller", id: "/#{locale}/navigate"], + [label: "Live", id: "/#{locale}/live/navigate"] + ] + ], [ label: "Number Input", id: "number-input", diff --git a/e2e/lib/e2e_web/components/layouts/captures.html.heex b/e2e/lib/e2e_web/components/layouts/captures.html.heex index 9cd2be5..2aa485e 100644 --- a/e2e/lib/e2e_web/components/layouts/captures.html.heex +++ b/e2e/lib/e2e_web/components/layouts/captures.html.heex @@ -8,7 +8,7 @@ else: "ltr" ) } - data-theme="neo" + data-theme={assigns[:theme] || "neo"} data-mode={assigns[:mode]} > @@ -78,6 +78,24 @@ const mode = Array.isArray(value) && value[0] ? value[0] : "light"; setMode(mode); }); + + const validThemes = ["neo", "uno", "duo", "leo"]; + const setTheme = (theme) => { + const resolved = validThemes.includes(theme) ? theme : "neo"; + localStorage.setItem("phx:theme", resolved); + document.cookie = "phx_theme=" + resolved + "; path=/; max-age=31536000"; + document.documentElement.setAttribute("data-theme", resolved); + }; + + setTheme(localStorage.getItem("phx:theme") || document.documentElement.getAttribute("data-theme") || "neo"); + + window.addEventListener("storage", (e) => e.key === "phx:theme" && e.newValue && setTheme(e.newValue)); + + window.addEventListener("phx:set-theme", (e) => { + const value = e.detail?.value; + const theme = Array.isArray(value) && value[0] ? value[0] : "neo"; + setTheme(theme); + }); })(); @@ -100,6 +118,7 @@
+ <.theme_toggle theme={assigns[:theme] || "neo"} /> <.mode_toggle mode={@mode} />
@@ -78,6 +78,24 @@ const mode = Array.isArray(value) && value[0] ? value[0] : "light"; setMode(mode); }); + + const validThemes = ["neo", "uno", "duo", "leo"]; + const setTheme = (theme) => { + const resolved = validThemes.includes(theme) ? theme : "neo"; + localStorage.setItem("phx:theme", resolved); + document.cookie = "phx_theme=" + resolved + "; path=/; max-age=31536000"; + document.documentElement.setAttribute("data-theme", resolved); + }; + + setTheme(localStorage.getItem("phx:theme") || document.documentElement.getAttribute("data-theme") || "neo"); + + window.addEventListener("storage", (e) => e.key === "phx:theme" && e.newValue && setTheme(e.newValue)); + + window.addEventListener("phx:set-theme", (e) => { + const value = e.detail?.value; + const theme = Array.isArray(value) && value[0] ? value[0] : "neo"; + setTheme(theme); + }); })(); diff --git a/e2e/lib/e2e_web/controllers/page_controller.ex b/e2e/lib/e2e_web/controllers/page_controller.ex index 0920eb6..ecd174e 100644 --- a/e2e/lib/e2e_web/controllers/page_controller.ex +++ b/e2e/lib/e2e_web/controllers/page_controller.ex @@ -9,6 +9,14 @@ defmodule E2eWeb.PageController do render(conn, :accordion_page) end + def action_page(conn, _params) do + render(conn, :action_page) + end + + def navigate_page(conn, _params) do + render(conn, :navigate_page) + end + def switch_page(conn, _params) do render(conn, :switch_page) end @@ -79,6 +87,21 @@ defmodule E2eWeb.PageController do render(conn, :clipboard_page) end + def code_page(conn, _params) do + heredoc_example = """ + defmodule Hello do + def world do + "Hello, World!" + end + end + """ + + conn + |> assign(:code_examples, E2eWeb.CodeExamples.all()) + |> assign(:heredoc_example, heredoc_example) + |> render(:code_page) + end + def date_picker_page(conn, _params) do render(conn, :date_picker_page) end diff --git a/e2e/lib/e2e_web/controllers/page_html.ex b/e2e/lib/e2e_web/controllers/page_html.ex index 2651896..dfb8e64 100644 --- a/e2e/lib/e2e_web/controllers/page_html.ex +++ b/e2e/lib/e2e_web/controllers/page_html.ex @@ -37,6 +37,13 @@ defmodule E2eWeb.PageHTML do [label: "Live", id: "/#{locale}/live/clipboard"] ] ], + [ + label: "Code", + id: "code", + children: [ + [label: "Controller", id: "/#{locale}/code"] + ] + ], [ label: "Collapsible", id: "collapsible", diff --git a/e2e/lib/e2e_web/controllers/page_html/accordion_page.html.heex b/e2e/lib/e2e_web/controllers/page_html/accordion_page.html.heex index ff967f5..3a9c614 100644 --- a/e2e/lib/e2e_web/controllers/page_html/accordion_page.html.heex +++ b/e2e/lib/e2e_web/controllers/page_html/accordion_page.html.heex @@ -1,24 +1,30 @@ - +

Accordion

Controller View

Client Api

- - - +
diff --git a/e2e/lib/e2e_web/controllers/page_html/action_page.html.heex b/e2e/lib/e2e_web/controllers/page_html/action_page.html.heex new file mode 100644 index 0000000..9fda167 --- /dev/null +++ b/e2e/lib/e2e_web/controllers/page_html/action_page.html.heex @@ -0,0 +1,71 @@ + +

Action

+

Controller View

+

Anatomy

+
+
+ <.action class="button">Text + <.action class="button"> + Text and SVG + + + <.action class="button button--square" aria_label="Button text"> + + + <.action class="button button--square" aria_label="Button text">B +
+
+ +

Color

+
+
+ <.action class="button">Text + <.action class="button button--accent">Text + <.action class="button button--brand">Text + <.action class="button button--alert">Text + <.action class="button button--info">Text + <.action class="button button--success">Text +
+
+ +

Size

+
+
+ <.action class="button button--sm">Button SM + <.action class="button">Button MD + <.action class="button button--lg">Button LG + <.action class="button button--xl">Button XL +
+
+ +

Shape

+
+
+ <.action class="button button--square" aria_label="Square button"> + + + <.action class="button button--square" aria_label="Square button">B + <.action class="button button--circle" aria_label="Circle button"> + + + <.action class="button button--circle" aria_label="Circle button">B +
+
+ +

Disabled

+
+
+ <.action class="button" disabled>Text + <.action class="button button--accent" disabled>Text + <.action class="button button--square" aria_label="Disabled" disabled> + + +
+
+
diff --git a/e2e/lib/e2e_web/controllers/page_html/angle_slider_page.html.heex b/e2e/lib/e2e_web/controllers/page_html/angle_slider_page.html.heex index fe1a12e..1aec8ec 100644 --- a/e2e/lib/e2e_web/controllers/page_html/angle_slider_page.html.heex +++ b/e2e/lib/e2e_web/controllers/page_html/angle_slider_page.html.heex @@ -1,33 +1,39 @@ - +

Angle Slider

Controller View

Client Api

- - - - +
<.angle_slider id="my-angle-slider" class="angle-slider" marker_values={[0, 90, 180, 270]}> diff --git a/e2e/lib/e2e_web/controllers/page_html/avatar_page.html.heex b/e2e/lib/e2e_web/controllers/page_html/avatar_page.html.heex index 44627ad..50d95d9 100644 --- a/e2e/lib/e2e_web/controllers/page_html/avatar_page.html.heex +++ b/e2e/lib/e2e_web/controllers/page_html/avatar_page.html.heex @@ -1,4 +1,10 @@ - +

Avatar

Controller View

diff --git a/e2e/lib/e2e_web/controllers/page_html/carousel_page.html.heex b/e2e/lib/e2e_web/controllers/page_html/carousel_page.html.heex index 5ab2ec8..7612ff5 100644 --- a/e2e/lib/e2e_web/controllers/page_html/carousel_page.html.heex +++ b/e2e/lib/e2e_web/controllers/page_html/carousel_page.html.heex @@ -1,4 +1,10 @@ - +

Carousel

Controller View

diff --git a/e2e/lib/e2e_web/controllers/page_html/checkbox_page.html.heex b/e2e/lib/e2e_web/controllers/page_html/checkbox_page.html.heex index 04245ec..7e4d781 100644 --- a/e2e/lib/e2e_web/controllers/page_html/checkbox_page.html.heex +++ b/e2e/lib/e2e_web/controllers/page_html/checkbox_page.html.heex @@ -1,29 +1,35 @@ - +

Checkbox

Controller View

Client Api

- + - - +
<.checkbox id="my-checkbox" class="checkbox"> diff --git a/e2e/lib/e2e_web/controllers/page_html/clipboard_page.html.heex b/e2e/lib/e2e_web/controllers/page_html/clipboard_page.html.heex index ce70ef6..6fa99c3 100644 --- a/e2e/lib/e2e_web/controllers/page_html/clipboard_page.html.heex +++ b/e2e/lib/e2e_web/controllers/page_html/clipboard_page.html.heex @@ -1,26 +1,32 @@ - +

Clipboard

Controller View

Client Api

- - - +
<.clipboard id="my-clipboard" diff --git a/e2e/lib/e2e_web/controllers/page_html/code_page.html.heex b/e2e/lib/e2e_web/controllers/page_html/code_page.html.heex new file mode 100644 index 0000000..222f840 --- /dev/null +++ b/e2e/lib/e2e_web/controllers/page_html/code_page.html.heex @@ -0,0 +1,104 @@ + +

Code

+

Controller View

+

Elixir

+
+
+ <.clipboard + id="code-elixir" + class="clipboard clipboard--sm absolute top-ui-padding right-ui-padding z-10" + value={@code_examples.elixir} + input={false} + trigger_aria_label="Copy code" + > + <:trigger> + <.icon name="hero-clipboard" class="icon data-copy" /> + <.icon name="hero-check" class="icon data-copied" /> + + + <.code code={@code_examples.elixir} language={:elixir} class="code" /> +
+
+ +

HTML

+
+
+ <.clipboard + id="code-html" + class="clipboard clipboard--sm absolute top-ui-padding right-ui-padding z-10" + value={@code_examples.html} + input={false} + trigger_aria_label="Copy code" + > + <:trigger> + <.icon name="hero-clipboard" class="icon data-copy" /> + <.icon name="hero-check" class="icon data-copied" /> + + + <.code code={@code_examples.html} language={:html} class="code" /> +
+
+ +

CSS

+
+
+ <.clipboard + id="code-css" + class="clipboard clipboard--sm absolute top-ui-padding right-ui-padding z-10" + value={@code_examples.css} + input={false} + trigger_aria_label="Copy code" + > + <:trigger> + <.icon name="hero-clipboard" class="icon data-copy" /> + <.icon name="hero-check" class="icon data-copied" /> + + + <.code code={@code_examples.css} language={:css} class="code" /> +
+
+ +

JavaScript

+
+
+ <.clipboard + id="code-js" + class="clipboard clipboard--sm absolute top-ui-padding right-ui-padding z-10" + value={@code_examples.js} + input={false} + trigger_aria_label="Copy code" + > + <:trigger> + <.icon name="hero-clipboard" class="icon data-copy" /> + <.icon name="hero-check" class="icon data-copied" /> + + + <.code code={@code_examples.js} language={:js} class="code" /> +
+
+ +

Inline with heredoc

+
+
+ <.clipboard + id="code-heredoc" + class="clipboard clipboard--sm absolute top-ui-padding right-ui-padding z-10" + value={@heredoc_example} + input={false} + trigger_aria_label="Copy code" + > + <:trigger> + <.icon name="hero-clipboard" class="icon data-copy" /> + <.icon name="hero-check" class="icon data-copied" /> + + + <.code language={:elixir} class="code" code={@heredoc_example} /> +
+
+
diff --git a/e2e/lib/e2e_web/controllers/page_html/collapsible_page.html.heex b/e2e/lib/e2e_web/controllers/page_html/collapsible_page.html.heex index 5a9d1ca..94d2e02 100644 --- a/e2e/lib/e2e_web/controllers/page_html/collapsible_page.html.heex +++ b/e2e/lib/e2e_web/controllers/page_html/collapsible_page.html.heex @@ -1,21 +1,27 @@ - +

Collapsible

Controller View

Client Api

- - +
diff --git a/e2e/lib/e2e_web/controllers/page_html/combobox_page.html.heex b/e2e/lib/e2e_web/controllers/page_html/combobox_page.html.heex index cfc59e7..62e0f57 100644 --- a/e2e/lib/e2e_web/controllers/page_html/combobox_page.html.heex +++ b/e2e/lib/e2e_web/controllers/page_html/combobox_page.html.heex @@ -1,4 +1,10 @@ - +

Combobox

Live View

diff --git a/e2e/lib/e2e_web/controllers/page_html/date_picker_page.html.heex b/e2e/lib/e2e_web/controllers/page_html/date_picker_page.html.heex index 2bcad98..ae322e0 100644 --- a/e2e/lib/e2e_web/controllers/page_html/date_picker_page.html.heex +++ b/e2e/lib/e2e_web/controllers/page_html/date_picker_page.html.heex @@ -1,20 +1,26 @@ - +

Date Picker

Controller View

Client Api

- - +
<.date_picker id="my-date-picker" diff --git a/e2e/lib/e2e_web/controllers/page_html/dialog_page.html.heex b/e2e/lib/e2e_web/controllers/page_html/dialog_page.html.heex index 360843b..b0a040a 100644 --- a/e2e/lib/e2e_web/controllers/page_html/dialog_page.html.heex +++ b/e2e/lib/e2e_web/controllers/page_html/dialog_page.html.heex @@ -1,14 +1,20 @@ - +

Dialog

Controller View

Client Api

- +
<.dialog id="my-dialog" class="dialog"> <:trigger>Open Dialog @@ -18,12 +24,12 @@ <:content>

Dialog content goes here. You can add any content you want inside the dialog.

- + <:close_trigger> <.icon name="hero-x-mark" class="icon" /> diff --git a/e2e/lib/e2e_web/controllers/page_html/editable_page.html.heex b/e2e/lib/e2e_web/controllers/page_html/editable_page.html.heex index 63fdc71..7e43978 100644 --- a/e2e/lib/e2e_web/controllers/page_html/editable_page.html.heex +++ b/e2e/lib/e2e_web/controllers/page_html/editable_page.html.heex @@ -1,4 +1,10 @@ - +
diff --git a/e2e/lib/e2e_web/controllers/page_html/listbox_page.html.heex b/e2e/lib/e2e_web/controllers/page_html/listbox_page.html.heex index 25041d0..92dd0c2 100644 --- a/e2e/lib/e2e_web/controllers/page_html/listbox_page.html.heex +++ b/e2e/lib/e2e_web/controllers/page_html/listbox_page.html.heex @@ -1,4 +1,10 @@ - +

Listbox

Controller View

diff --git a/e2e/lib/e2e_web/controllers/page_html/menu_page.html.heex b/e2e/lib/e2e_web/controllers/page_html/menu_page.html.heex index 82b049c..8694663 100644 --- a/e2e/lib/e2e_web/controllers/page_html/menu_page.html.heex +++ b/e2e/lib/e2e_web/controllers/page_html/menu_page.html.heex @@ -1,4 +1,10 @@ - +

Menu

Controller View

diff --git a/e2e/lib/e2e_web/controllers/page_html/navigate_page.html.heex b/e2e/lib/e2e_web/controllers/page_html/navigate_page.html.heex new file mode 100644 index 0000000..da4a987 --- /dev/null +++ b/e2e/lib/e2e_web/controllers/page_html/navigate_page.html.heex @@ -0,0 +1,78 @@ + +

Navigate

+

Controller View

+

Anatomy

+
+
+ <.navigate to="#" class="link">Internal Link + <.navigate to="#" class="link"> + Internal Link + + + <.navigate to="#" class="link" aria_label="Internal link icon only"> + + + <.navigate to="https://example.com" class="link" external> + External Link + + Opens in a new window + + + + <.navigate to="#" class="link" download="report.pdf"> + Download Link + + Download PDF, 2MB + + + +
+
+ +

Color

+
+
+ <.navigate to="#" class="link link--accent">Internal Link + <.navigate to="#" class="link link--brand">Internal Link + <.navigate to="#" class="link link--alert">Internal Link + <.navigate to="#" class="link link--info">Internal Link + <.navigate to="#" class="link link--success">Internal Link +
+
+ +

Size

+
+
+ <.navigate to="#" class="link link--sm">Internal Link + <.navigate to="#" class="link link--md">Internal Link + <.navigate to="#" class="link link--lg">Internal Link + <.navigate to="#" class="link link--xl">Internal Link +
+
+
diff --git a/e2e/lib/e2e_web/controllers/page_html/number_input_page.html.heex b/e2e/lib/e2e_web/controllers/page_html/number_input_page.html.heex index 5d05fe1..1a7c6db 100644 --- a/e2e/lib/e2e_web/controllers/page_html/number_input_page.html.heex +++ b/e2e/lib/e2e_web/controllers/page_html/number_input_page.html.heex @@ -1,4 +1,10 @@ - +

Number Input

Controller View

diff --git a/e2e/lib/e2e_web/controllers/page_html/password_input_page.html.heex b/e2e/lib/e2e_web/controllers/page_html/password_input_page.html.heex index 03eb14b..30b4404 100644 --- a/e2e/lib/e2e_web/controllers/page_html/password_input_page.html.heex +++ b/e2e/lib/e2e_web/controllers/page_html/password_input_page.html.heex @@ -1,4 +1,10 @@ - +

Password Input

Controller View

diff --git a/e2e/lib/e2e_web/controllers/page_html/pin_input_page.html.heex b/e2e/lib/e2e_web/controllers/page_html/pin_input_page.html.heex index c96eae3..0f1905a 100644 --- a/e2e/lib/e2e_web/controllers/page_html/pin_input_page.html.heex +++ b/e2e/lib/e2e_web/controllers/page_html/pin_input_page.html.heex @@ -1,4 +1,10 @@ - +

Pin Input

Controller View

diff --git a/e2e/lib/e2e_web/controllers/page_html/radio_group_page.html.heex b/e2e/lib/e2e_web/controllers/page_html/radio_group_page.html.heex index 2ace990..f84bbe4 100644 --- a/e2e/lib/e2e_web/controllers/page_html/radio_group_page.html.heex +++ b/e2e/lib/e2e_web/controllers/page_html/radio_group_page.html.heex @@ -1,4 +1,10 @@ - +

Radio Group

Controller View

diff --git a/e2e/lib/e2e_web/controllers/page_html/select_page.html.heex b/e2e/lib/e2e_web/controllers/page_html/select_page.html.heex index 16cf3ce..f44ed0d 100644 --- a/e2e/lib/e2e_web/controllers/page_html/select_page.html.heex +++ b/e2e/lib/e2e_web/controllers/page_html/select_page.html.heex @@ -1,4 +1,10 @@ - +

Select

Live View

diff --git a/e2e/lib/e2e_web/controllers/page_html/signature_page.html.heex b/e2e/lib/e2e_web/controllers/page_html/signature_page.html.heex index 171e01f..01a6387 100644 --- a/e2e/lib/e2e_web/controllers/page_html/signature_page.html.heex +++ b/e2e/lib/e2e_web/controllers/page_html/signature_page.html.heex @@ -1,12 +1,18 @@ - +

Signature Pad

Controller View

Client API

- +
<.signature_pad id="my-signature-pad" on_draw_end="signature_drawn"> diff --git a/e2e/lib/e2e_web/controllers/page_html/switch_page.html.heex b/e2e/lib/e2e_web/controllers/page_html/switch_page.html.heex index b7e024e..1f2c803 100644 --- a/e2e/lib/e2e_web/controllers/page_html/switch_page.html.heex +++ b/e2e/lib/e2e_web/controllers/page_html/switch_page.html.heex @@ -1,4 +1,10 @@ - +

Switch

Controller View

<.switch class="switch"> diff --git a/e2e/lib/e2e_web/controllers/page_html/tabs_page.html.heex b/e2e/lib/e2e_web/controllers/page_html/tabs_page.html.heex index 37229b1..b74dcd5 100644 --- a/e2e/lib/e2e_web/controllers/page_html/tabs_page.html.heex +++ b/e2e/lib/e2e_web/controllers/page_html/tabs_page.html.heex @@ -1,23 +1,29 @@ - +

Tabs

Controller View

Client Api

- - - +
<.tabs id="my-tabs" diff --git a/e2e/lib/e2e_web/controllers/page_html/timer_page.html.heex b/e2e/lib/e2e_web/controllers/page_html/timer_page.html.heex index 4bb2188..3c6497c 100644 --- a/e2e/lib/e2e_web/controllers/page_html/timer_page.html.heex +++ b/e2e/lib/e2e_web/controllers/page_html/timer_page.html.heex @@ -1,4 +1,10 @@ - +

Timer

Controller View

diff --git a/e2e/lib/e2e_web/controllers/page_html/toast_page.html.heex b/e2e/lib/e2e_web/controllers/page_html/toast_page.html.heex index 7bda6ca..24b5e66 100644 --- a/e2e/lib/e2e_web/controllers/page_html/toast_page.html.heex +++ b/e2e/lib/e2e_web/controllers/page_html/toast_page.html.heex @@ -1,4 +1,10 @@ - +

Toast

Controller View

@@ -35,13 +41,13 @@
- <.button class="button button--accent">Create Flash Message + <.action class="button button--accent">Create Flash Message

Client Api

- + - - + - +
diff --git a/e2e/lib/e2e_web/controllers/page_html/toggle_group_page.html.heex b/e2e/lib/e2e_web/controllers/page_html/toggle_group_page.html.heex index 1a7523c..8595289 100644 --- a/e2e/lib/e2e_web/controllers/page_html/toggle_group_page.html.heex +++ b/e2e/lib/e2e_web/controllers/page_html/toggle_group_page.html.heex @@ -1,28 +1,34 @@ - +

Toggle Group

Controller View

Client Api

- - - +
<.toggle_group id="my-toggle-group" class="toggle-group"> <:item value="lorem"> diff --git a/e2e/lib/e2e_web/controllers/page_html/tree_view_page.html.heex b/e2e/lib/e2e_web/controllers/page_html/tree_view_page.html.heex index 775c488..0539d62 100644 --- a/e2e/lib/e2e_web/controllers/page_html/tree_view_page.html.heex +++ b/e2e/lib/e2e_web/controllers/page_html/tree_view_page.html.heex @@ -1,33 +1,39 @@ - +

Tree View

Controller View

Client Api

- - - - +
<.tree_view diff --git a/e2e/lib/e2e_web/controllers/user_html/edit.html.heex b/e2e/lib/e2e_web/controllers/user_html/edit.html.heex index 184905e..fa49184 100644 --- a/e2e/lib/e2e_web/controllers/user_html/edit.html.heex +++ b/e2e/lib/e2e_web/controllers/user_html/edit.html.heex @@ -1,4 +1,10 @@ - + <.header> Edit User {@user.id} <:subtitle>Use this form to manage user records in your database. diff --git a/e2e/lib/e2e_web/controllers/user_html/index.html.heex b/e2e/lib/e2e_web/controllers/user_html/index.html.heex index 47819fd..67cee01 100644 --- a/e2e/lib/e2e_web/controllers/user_html/index.html.heex +++ b/e2e/lib/e2e_web/controllers/user_html/index.html.heex @@ -1,4 +1,10 @@ - + <.header> Listing Users <:actions> diff --git a/e2e/lib/e2e_web/controllers/user_html/new.html.heex b/e2e/lib/e2e_web/controllers/user_html/new.html.heex index 62ca3d4..bbea01c 100644 --- a/e2e/lib/e2e_web/controllers/user_html/new.html.heex +++ b/e2e/lib/e2e_web/controllers/user_html/new.html.heex @@ -1,4 +1,10 @@ - + <.header> New User <:subtitle>Use this form to manage user records in your database. diff --git a/e2e/lib/e2e_web/controllers/user_html/show.html.heex b/e2e/lib/e2e_web/controllers/user_html/show.html.heex index 6eb80a5..9b950d7 100644 --- a/e2e/lib/e2e_web/controllers/user_html/show.html.heex +++ b/e2e/lib/e2e_web/controllers/user_html/show.html.heex @@ -1,4 +1,10 @@ - + <.header> User {@user.id} <:subtitle>This is a user record from your database. diff --git a/e2e/lib/e2e_web/live/accordion_async_live.ex b/e2e/lib/e2e_web/live/accordion_async_live.ex index ed7b102..b976b0c 100644 --- a/e2e/lib/e2e_web/live/accordion_async_live.ex +++ b/e2e/lib/e2e_web/live/accordion_async_live.ex @@ -40,7 +40,13 @@ defmodule E2eWeb.AccordionAsyncLive do def render(assigns) do ~H""" - +

Accordion

Async

diff --git a/e2e/lib/e2e_web/live/accordion_controlled_live.ex b/e2e/lib/e2e_web/live/accordion_controlled_live.ex index 7c28366..7425e0c 100644 --- a/e2e/lib/e2e_web/live/accordion_controlled_live.ex +++ b/e2e/lib/e2e_web/live/accordion_controlled_live.ex @@ -47,7 +47,13 @@ defmodule E2eWeb.AccordionControlledLive do def render(assigns) do ~H""" - +

Accordion

Live View

diff --git a/e2e/lib/e2e_web/live/accordion_live.ex b/e2e/lib/e2e_web/live/accordion_live.ex index 0a41b9e..3e8e1d8 100644 --- a/e2e/lib/e2e_web/live/accordion_live.ex +++ b/e2e/lib/e2e_web/live/accordion_live.ex @@ -32,7 +32,13 @@ defmodule E2eWeb.AccordionLive do def render(assigns) do ~H""" - +

Accordion

Live View

diff --git a/e2e/lib/e2e_web/live/accordion_play_live.ex b/e2e/lib/e2e_web/live/accordion_play_live.ex index 534c526..b5c03e6 100644 --- a/e2e/lib/e2e_web/live/accordion_play_live.ex +++ b/e2e/lib/e2e_web/live/accordion_play_live.ex @@ -91,7 +91,13 @@ defmodule E2eWeb.AccordionPlayLive do @impl true def render(assigns) do ~H""" - +

Accordion

Playground

diff --git a/e2e/lib/e2e_web/live/action_live.ex b/e2e/lib/e2e_web/live/action_live.ex new file mode 100644 index 0000000..de6868c --- /dev/null +++ b/e2e/lib/e2e_web/live/action_live.ex @@ -0,0 +1,83 @@ +defmodule E2eWeb.ActionLive do + use E2eWeb, :live_view + + def mount(_params, _session, socket) do + {:ok, socket} + end + + def render(assigns) do + ~H""" + +

Action

+

Live View

+

Anatomy

+
+
+ <.action class="button">Text + <.action class="button"> + Text and SVG + + + <.action class="button button--square" aria_label="Button text"> + + + <.action class="button button--square" aria_label="Button text">B +
+
+ +

Color

+
+
+ <.action class="button">Text + <.action class="button button--accent">Text + <.action class="button button--brand">Text + <.action class="button button--alert">Text + <.action class="button button--info">Text + <.action class="button button--success">Text +
+
+ +

Size

+
+
+ <.action class="button button--sm">Button SM + <.action class="button">Button MD + <.action class="button button--lg">Button LG + <.action class="button button--xl">Button XL +
+
+ +

Shape

+
+
+ <.action class="button button--square" aria_label="Square button"> + + + <.action class="button button--square" aria_label="Square button">B + <.action class="button button--circle" aria_label="Circle button"> + + + <.action class="button button--circle" aria_label="Circle button">B +
+
+ +

Disabled

+
+
+ <.action class="button" disabled>Text + <.action class="button button--accent" disabled>Text + <.action class="button button--square" aria_label="Disabled" disabled> + + +
+
+
+ """ + end +end diff --git a/e2e/lib/e2e_web/live/admin_live/form.ex b/e2e/lib/e2e_web/live/admin_live/form.ex index fd176fa..c5cbca9 100644 --- a/e2e/lib/e2e_web/live/admin_live/form.ex +++ b/e2e/lib/e2e_web/live/admin_live/form.ex @@ -7,7 +7,13 @@ defmodule E2eWeb.AdminLive.Form do @impl true def render(assigns) do ~H""" - + <.header> {@page_title} <:subtitle>Use this form to manage admin records in your database. diff --git a/e2e/lib/e2e_web/live/admin_live/index.ex b/e2e/lib/e2e_web/live/admin_live/index.ex index 58ffea3..80ae53a 100644 --- a/e2e/lib/e2e_web/live/admin_live/index.ex +++ b/e2e/lib/e2e_web/live/admin_live/index.ex @@ -6,7 +6,13 @@ defmodule E2eWeb.AdminLive.Index do @impl true def render(assigns) do ~H""" - + <.header> Listing Admins <:actions> diff --git a/e2e/lib/e2e_web/live/admin_live/show.ex b/e2e/lib/e2e_web/live/admin_live/show.ex index abf6283..bc79b3e 100644 --- a/e2e/lib/e2e_web/live/admin_live/show.ex +++ b/e2e/lib/e2e_web/live/admin_live/show.ex @@ -6,7 +6,13 @@ defmodule E2eWeb.AdminLive.Show do @impl true def render(assigns) do ~H""" - + <.header> Admin {@admin.id} <:subtitle>This is a admin record from your database. diff --git a/e2e/lib/e2e_web/live/angle_slider_controlled_live.ex b/e2e/lib/e2e_web/live/angle_slider_controlled_live.ex index 8773037..73fcea0 100644 --- a/e2e/lib/e2e_web/live/angle_slider_controlled_live.ex +++ b/e2e/lib/e2e_web/live/angle_slider_controlled_live.ex @@ -26,7 +26,13 @@ defmodule E2eWeb.AngleSliderControlledLive do def render(assigns) do ~H""" - +

Angle Slider

Controlled

diff --git a/e2e/lib/e2e_web/live/angle_slider_live.ex b/e2e/lib/e2e_web/live/angle_slider_live.ex index 882f11e..13933d8 100644 --- a/e2e/lib/e2e_web/live/angle_slider_live.ex +++ b/e2e/lib/e2e_web/live/angle_slider_live.ex @@ -37,7 +37,13 @@ defmodule E2eWeb.AngleSliderLive do def render(assigns) do ~H""" - +

Angle Slider

Live View

diff --git a/e2e/lib/e2e_web/live/angle_slider_play_live.ex b/e2e/lib/e2e_web/live/angle_slider_play_live.ex index 9c6691d..8c8b361 100644 --- a/e2e/lib/e2e_web/live/angle_slider_play_live.ex +++ b/e2e/lib/e2e_web/live/angle_slider_play_live.ex @@ -90,7 +90,13 @@ defmodule E2eWeb.AngleSliderPlayLive do @impl true def render(assigns) do ~H""" - +

Angle Slider

Playground

@@ -130,7 +136,7 @@ defmodule E2eWeb.AngleSliderPlayLive do checked={@controls.show_markers} on_checked_change="control_changed" > - <:label>Show Markers (0°, 90°, 180°, 270°) + <:label>Show Markers <.toggle_group diff --git a/e2e/lib/e2e_web/live/avatar_live.ex b/e2e/lib/e2e_web/live/avatar_live.ex index 9af0750..ad311eb 100644 --- a/e2e/lib/e2e_web/live/avatar_live.ex +++ b/e2e/lib/e2e_web/live/avatar_live.ex @@ -7,7 +7,13 @@ defmodule E2eWeb.AvatarLive do def render(assigns) do ~H""" - +

Avatar

Live View

diff --git a/e2e/lib/e2e_web/live/carousel_live.ex b/e2e/lib/e2e_web/live/carousel_live.ex index 6f2ff3f..3c96752 100644 --- a/e2e/lib/e2e_web/live/carousel_live.ex +++ b/e2e/lib/e2e_web/live/carousel_live.ex @@ -7,7 +7,13 @@ defmodule E2eWeb.CarouselLive do def render(assigns) do ~H""" - +

Carousel

Live View

diff --git a/e2e/lib/e2e_web/live/checkbox_live.ex b/e2e/lib/e2e_web/live/checkbox_live.ex index 06bbd58..68b89f0 100644 --- a/e2e/lib/e2e_web/live/checkbox_live.ex +++ b/e2e/lib/e2e_web/live/checkbox_live.ex @@ -15,7 +15,13 @@ defmodule E2eWeb.CheckboxLive do def render(assigns) do ~H""" - +

Checkbox

Live View

diff --git a/e2e/lib/e2e_web/live/clipboard_live.ex b/e2e/lib/e2e_web/live/clipboard_live.ex index 080d820..1b5c5ce 100644 --- a/e2e/lib/e2e_web/live/clipboard_live.ex +++ b/e2e/lib/e2e_web/live/clipboard_live.ex @@ -19,7 +19,13 @@ defmodule E2eWeb.ClipboardLive do def render(assigns) do ~H""" - +

Clipboard

Live View

diff --git a/e2e/lib/e2e_web/live/code_live.ex b/e2e/lib/e2e_web/live/code_live.ex new file mode 100644 index 0000000..2f0d1d7 --- /dev/null +++ b/e2e/lib/e2e_web/live/code_live.ex @@ -0,0 +1,129 @@ +defmodule E2eWeb.CodeLive do + use E2eWeb, :live_view + + def mount(_params, _session, socket) do + heredoc_example = """ + defmodule Hello do + def world do + "Hello, World!" + end + end + """ + + socket = + socket + |> assign(:code_examples, E2eWeb.CodeExamples.all()) + |> assign(:heredoc_example, heredoc_example) + + {:ok, socket} + end + + def render(assigns) do + ~H""" + +

Code

+

Live View

+

Elixir

+
+
+ <.clipboard + id="code-elixir" + class="clipboard clipboard--sm absolute top-ui-padding right-ui-padding z-10" + value={@code_examples.elixir} + input={false} + trigger_aria_label="Copy code" + > + <:trigger> + <.icon name="hero-clipboard" class="icon data-copy" /> + <.icon name="hero-check" class="icon data-copied" /> + + + <.code code={@code_examples.elixir} language={:elixir} class="code" /> +
+
+ +

HTML

+
+
+ <.clipboard + id="code-html" + class="clipboard clipboard--sm absolute top-ui-padding right-ui-padding z-10" + value={@code_examples.html} + input={false} + trigger_aria_label="Copy code" + > + <:trigger> + <.icon name="hero-clipboard" class="icon data-copy" /> + <.icon name="hero-check" class="icon data-copied" /> + + + <.code code={@code_examples.html} language={:html} class="code" /> +
+
+ +

CSS

+
+
+ <.clipboard + id="code-css" + class="clipboard clipboard--sm absolute top-ui-padding right-ui-padding z-10" + value={@code_examples.css} + input={false} + trigger_aria_label="Copy code" + > + <:trigger> + <.icon name="hero-clipboard" class="icon data-copy" /> + <.icon name="hero-check" class="icon data-copied" /> + + + <.code code={@code_examples.css} language={:css} class="code" /> +
+
+ +

JavaScript

+
+
+ <.clipboard + id="code-js" + class="clipboard clipboard--sm absolute top-ui-padding right-ui-padding z-10" + value={@code_examples.js} + input={false} + trigger_aria_label="Copy code" + > + <:trigger> + <.icon name="hero-clipboard" class="icon data-copy" /> + <.icon name="hero-check" class="icon data-copied" /> + + + <.code code={@code_examples.js} language={:js} class="code" /> +
+
+ +

Inline with heredoc

+
+
+ <.clipboard + id="code-heredoc" + class="clipboard clipboard--sm absolute top-ui-padding right-ui-padding z-10" + value={@heredoc_example} + input={false} + trigger_aria_label="Copy code" + > + <:trigger> + <.icon name="hero-clipboard" class="icon data-copy" /> + <.icon name="hero-check" class="icon data-copied" /> + + + <.code language={:elixir} class="code" code={@heredoc_example} /> +
+
+
+ """ + end +end diff --git a/e2e/lib/e2e_web/live/collapsible_live.ex b/e2e/lib/e2e_web/live/collapsible_live.ex index 15e5009..0cebb4d 100644 --- a/e2e/lib/e2e_web/live/collapsible_live.ex +++ b/e2e/lib/e2e_web/live/collapsible_live.ex @@ -19,7 +19,13 @@ defmodule E2eWeb.CollapsibleLive do def render(assigns) do ~H""" - +

Collapsible

Live View

diff --git a/e2e/lib/e2e_web/live/combobox_fetch.ex b/e2e/lib/e2e_web/live/combobox_fetch.ex index b909401..d821fd9 100644 --- a/e2e/lib/e2e_web/live/combobox_fetch.ex +++ b/e2e/lib/e2e_web/live/combobox_fetch.ex @@ -84,7 +84,13 @@ defmodule E2eWeb.ComboboxFetch do def render(assigns) do ~H""" - +

Combobox

Server-side Filtering

diff --git a/e2e/lib/e2e_web/live/combobox_form.ex b/e2e/lib/e2e_web/live/combobox_form.ex index 080b624..00a2a9c 100644 --- a/e2e/lib/e2e_web/live/combobox_form.ex +++ b/e2e/lib/e2e_web/live/combobox_form.ex @@ -149,7 +149,13 @@ defmodule E2eWeb.ComboboxForm do @impl true def render(assigns) do ~H""" - +

Combobox

Form with validation

@@ -157,7 +163,9 @@ defmodule E2eWeb.ComboboxForm do <.header> Combobox form - <:subtitle>Phoenix form with Ecto validation, database fetching and server side fitlering combobox. + <:subtitle> + Phoenix form with Ecto validation, database fetching and server side fitlering combobox. + <.form diff --git a/e2e/lib/e2e_web/live/combobox_live.ex b/e2e/lib/e2e_web/live/combobox_live.ex index 0c6adc9..8ae8a9e 100644 --- a/e2e/lib/e2e_web/live/combobox_live.ex +++ b/e2e/lib/e2e_web/live/combobox_live.ex @@ -12,7 +12,13 @@ defmodule E2eWeb.ComboboxLive do def render(assigns) do ~H""" - +

Combobox

Live View

diff --git a/e2e/lib/e2e_web/live/date_picker_live.ex b/e2e/lib/e2e_web/live/date_picker_live.ex index e074d97..5851a27 100644 --- a/e2e/lib/e2e_web/live/date_picker_live.ex +++ b/e2e/lib/e2e_web/live/date_picker_live.ex @@ -11,7 +11,13 @@ defmodule E2eWeb.DatePickerLive do def render(assigns) do ~H""" - +

Date Picker

Live View

diff --git a/e2e/lib/e2e_web/live/dialog_live.ex b/e2e/lib/e2e_web/live/dialog_live.ex index 494b6ab..46399c2 100644 --- a/e2e/lib/e2e_web/live/dialog_live.ex +++ b/e2e/lib/e2e_web/live/dialog_live.ex @@ -19,7 +19,13 @@ defmodule E2eWeb.DialogLive do def render(assigns) do ~H""" - +

Dialog

Live View

diff --git a/e2e/lib/e2e_web/live/editable_live.ex b/e2e/lib/e2e_web/live/editable_live.ex index 13a8e90..ca6218d 100644 --- a/e2e/lib/e2e_web/live/editable_live.ex +++ b/e2e/lib/e2e_web/live/editable_live.ex @@ -7,7 +7,13 @@ defmodule E2eWeb.EditableLive do def render(assigns) do ~H""" - +

Editable

Live View

diff --git a/e2e/lib/e2e_web/live/floating_panel_live.ex b/e2e/lib/e2e_web/live/floating_panel_live.ex index 58cffca..d03b0ca 100644 --- a/e2e/lib/e2e_web/live/floating_panel_live.ex +++ b/e2e/lib/e2e_web/live/floating_panel_live.ex @@ -7,7 +7,13 @@ defmodule E2eWeb.FloatingPanelLive do def render(assigns) do ~H""" - +

Floating Panel

Live View

diff --git a/e2e/lib/e2e_web/live/hooks/theme_live.ex b/e2e/lib/e2e_web/live/hooks/theme_live.ex new file mode 100644 index 0000000..c6cbf41 --- /dev/null +++ b/e2e/lib/e2e_web/live/hooks/theme_live.ex @@ -0,0 +1,10 @@ +defmodule E2eWeb.ThemeLive do + @moduledoc """ + Assigns the theme from the session to the LiveView socket. + """ + def on_mount(:default, _params, session, socket) do + theme = session["theme"] || "neo" + + {:cont, Phoenix.Component.assign(socket, :theme, theme)} + end +end diff --git a/e2e/lib/e2e_web/live/listbox_live.ex b/e2e/lib/e2e_web/live/listbox_live.ex index 5fa98b7..2bcacb6 100644 --- a/e2e/lib/e2e_web/live/listbox_live.ex +++ b/e2e/lib/e2e_web/live/listbox_live.ex @@ -7,7 +7,13 @@ defmodule E2eWeb.ListboxLive do def render(assigns) do ~H""" - +

Listbox

Live View

diff --git a/e2e/lib/e2e_web/live/menu_live.ex b/e2e/lib/e2e_web/live/menu_live.ex index 95445be..ded3ccd 100644 --- a/e2e/lib/e2e_web/live/menu_live.ex +++ b/e2e/lib/e2e_web/live/menu_live.ex @@ -16,7 +16,13 @@ defmodule E2eWeb.MenuLive do def render(assigns) do ~H""" - +

Menu

Live View

diff --git a/e2e/lib/e2e_web/live/navigate_live.ex b/e2e/lib/e2e_web/live/navigate_live.ex new file mode 100644 index 0000000..011c644 --- /dev/null +++ b/e2e/lib/e2e_web/live/navigate_live.ex @@ -0,0 +1,90 @@ +defmodule E2eWeb.NavigateLive do + use E2eWeb, :live_view + + def mount(_params, _session, socket) do + {:ok, socket} + end + + def render(assigns) do + ~H""" + +

Navigate

+

Live View

+

Anatomy

+
+
+ <.navigate to="#" class="link">Internal Link + <.navigate to="#" class="link"> + Internal Link + + + <.navigate to="#" class="link" aria_label="Internal link icon only"> + + + <.navigate to="https://example.com" class="link" external> + External Link + + Opens in a new window + + + + <.navigate to="#" class="link" download="report.pdf"> + Download Link + + Download PDF, 2MB + + + +
+
+ +

Color

+
+
+ <.navigate to="#" class="link link--accent">Internal Link + <.navigate to="#" class="link link--brand">Internal Link + <.navigate to="#" class="link link--alert">Internal Link + <.navigate to="#" class="link link--info">Internal Link + <.navigate to="#" class="link link--success">Internal Link +
+
+ +

Size

+
+
+ <.navigate to="#" class="link link--sm">Internal Link + <.navigate to="#" class="link link--md">Internal Link + <.navigate to="#" class="link link--lg">Internal Link + <.navigate to="#" class="link link--xl">Internal Link +
+
+
+ """ + end +end diff --git a/e2e/lib/e2e_web/live/number_input_live.ex b/e2e/lib/e2e_web/live/number_input_live.ex index e3e05c2..fcb7d61 100644 --- a/e2e/lib/e2e_web/live/number_input_live.ex +++ b/e2e/lib/e2e_web/live/number_input_live.ex @@ -7,7 +7,13 @@ defmodule E2eWeb.NumberInputLive do def render(assigns) do ~H""" - +

Number Input

Live View

diff --git a/e2e/lib/e2e_web/live/password_input_live.ex b/e2e/lib/e2e_web/live/password_input_live.ex index 14b0f90..251363b 100644 --- a/e2e/lib/e2e_web/live/password_input_live.ex +++ b/e2e/lib/e2e_web/live/password_input_live.ex @@ -7,7 +7,13 @@ defmodule E2eWeb.PasswordInputLive do def render(assigns) do ~H""" - +

Password Input

Live View

diff --git a/e2e/lib/e2e_web/live/pin_input_live.ex b/e2e/lib/e2e_web/live/pin_input_live.ex index c0037fb..3f1760a 100644 --- a/e2e/lib/e2e_web/live/pin_input_live.ex +++ b/e2e/lib/e2e_web/live/pin_input_live.ex @@ -7,7 +7,13 @@ defmodule E2eWeb.PinInputLive do def render(assigns) do ~H""" - +

Pin Input

Live View

diff --git a/e2e/lib/e2e_web/live/radio_group_live.ex b/e2e/lib/e2e_web/live/radio_group_live.ex index 765119c..1ec9821 100644 --- a/e2e/lib/e2e_web/live/radio_group_live.ex +++ b/e2e/lib/e2e_web/live/radio_group_live.ex @@ -7,7 +7,13 @@ defmodule E2eWeb.RadioGroupLive do def render(assigns) do ~H""" - +

Radio Group

Live View

diff --git a/e2e/lib/e2e_web/live/select_live.ex b/e2e/lib/e2e_web/live/select_live.ex index 3773771..7493e48 100644 --- a/e2e/lib/e2e_web/live/select_live.ex +++ b/e2e/lib/e2e_web/live/select_live.ex @@ -7,7 +7,13 @@ defmodule E2eWeb.SelectLive do def render(assigns) do ~H""" - +

Select

Live View

diff --git a/e2e/lib/e2e_web/live/signature_live.ex b/e2e/lib/e2e_web/live/signature_live.ex index 1376b74..46998aa 100644 --- a/e2e/lib/e2e_web/live/signature_live.ex +++ b/e2e/lib/e2e_web/live/signature_live.ex @@ -20,7 +20,13 @@ defmodule E2eWeb.SignatureLive do def render(assigns) do ~H""" - +

Signature Pad

Live View

diff --git a/e2e/lib/e2e_web/live/switch_live.ex b/e2e/lib/e2e_web/live/switch_live.ex index 1bdfa16..091858a 100644 --- a/e2e/lib/e2e_web/live/switch_live.ex +++ b/e2e/lib/e2e_web/live/switch_live.ex @@ -7,7 +7,13 @@ defmodule E2eWeb.SwitchLive do def render(assigns) do ~H""" - +

Switch

Live View

<.switch class="switch"> diff --git a/e2e/lib/e2e_web/live/tabs_live.ex b/e2e/lib/e2e_web/live/tabs_live.ex index 467de87..ec49ef9 100644 --- a/e2e/lib/e2e_web/live/tabs_live.ex +++ b/e2e/lib/e2e_web/live/tabs_live.ex @@ -11,7 +11,13 @@ defmodule E2eWeb.TabsLive do def render(assigns) do ~H""" - +

Tabs

Live View

diff --git a/e2e/lib/e2e_web/live/timer_live.ex b/e2e/lib/e2e_web/live/timer_live.ex index e4e88b5..e3d9887 100644 --- a/e2e/lib/e2e_web/live/timer_live.ex +++ b/e2e/lib/e2e_web/live/timer_live.ex @@ -7,7 +7,13 @@ defmodule E2eWeb.TimerLive do def render(assigns) do ~H""" - +

Timer

Live View

diff --git a/e2e/lib/e2e_web/live/toast_live.ex b/e2e/lib/e2e_web/live/toast_live.ex index f34cd53..eab9f37 100644 --- a/e2e/lib/e2e_web/live/toast_live.ex +++ b/e2e/lib/e2e_web/live/toast_live.ex @@ -60,7 +60,13 @@ defmodule E2eWeb.ToastLive do def render(assigns) do ~H""" - +

Toast

Live View

diff --git a/e2e/lib/e2e_web/live/toggle_group_live.ex b/e2e/lib/e2e_web/live/toggle_group_live.ex index 5e527ae..0d68f34 100644 --- a/e2e/lib/e2e_web/live/toggle_group_live.ex +++ b/e2e/lib/e2e_web/live/toggle_group_live.ex @@ -11,7 +11,13 @@ defmodule E2eWeb.ToggleGroupLive do def render(assigns) do ~H""" - +

Toggle Group

Live View

diff --git a/e2e/lib/e2e_web/live/tree_view_live.ex b/e2e/lib/e2e_web/live/tree_view_live.ex index 22d3581..ebf6f81 100644 --- a/e2e/lib/e2e_web/live/tree_view_live.ex +++ b/e2e/lib/e2e_web/live/tree_view_live.ex @@ -77,7 +77,13 @@ defmodule E2eWeb.TreeViewLive do def render(assigns) do ~H""" - +

Tree View

Live View

diff --git a/e2e/lib/e2e_web/plugs/theme.ex b/e2e/lib/e2e_web/plugs/theme.ex new file mode 100644 index 0000000..8593a6d --- /dev/null +++ b/e2e/lib/e2e_web/plugs/theme.ex @@ -0,0 +1,25 @@ +defmodule E2eWeb.Plugs.Theme do + @moduledoc """ + Reads the theme from the phx_theme cookie and puts it in assigns and session. + Allows the server to render the correct theme in the initial HTML (no flash). + """ + import Plug.Conn + + @valid_themes ~w(neo uno duo leo) + + def init(opts), do: opts + + def call(conn, _opts) do + theme = + conn.cookies["phx_theme"] + |> parse_theme() + + conn + |> assign(:theme, theme) + |> put_session(:theme, theme) + end + + defp parse_theme(nil), do: "neo" + defp parse_theme(theme) when theme in @valid_themes, do: theme + defp parse_theme(_), do: "neo" +end diff --git a/e2e/lib/e2e_web/router.ex b/e2e/lib/e2e_web/router.ex index f313202..1936ba1 100644 --- a/e2e/lib/e2e_web/router.ex +++ b/e2e/lib/e2e_web/router.ex @@ -8,6 +8,7 @@ defmodule E2eWeb.Router do plug :fetch_flash plug :fetch_live_flash plug E2eWeb.Plugs.Mode + plug E2eWeb.Plugs.Theme plug E2eWeb.Plugs.Locale plug :put_root_layout, html: {E2eWeb.Layouts, :root} plug :protect_from_forgery @@ -31,13 +32,16 @@ defmodule E2eWeb.Router do scope "/:locale", E2eWeb do pipe_through :browser - live_session :default, on_mount: [E2eWeb.ModeLive, E2eWeb.SharedEvents] do + live_session :default, on_mount: [E2eWeb.ModeLive, E2eWeb.ThemeLive, E2eWeb.SharedEvents] do live "/live/accordion", AccordionLive live "/playground/accordion", AccordionPlayLive live "/controlled/accordion", AccordionControlledLive live "/async/accordion", AccordionAsyncLive live "/live/checkbox", CheckboxLive live "/live/clipboard", ClipboardLive + live "/live/code", CodeLive + live "/live/action", ActionLive + live "/live/navigate", NavigateLive live "/live/collapsible", CollapsibleLive live "/live/combobox", ComboboxLive live "/live/combobox-fetch", ComboboxFetch @@ -71,9 +75,13 @@ defmodule E2eWeb.Router do get "/", PageController, :home get "/accordion", PageController, :accordion_page + get "/action", PageController, :action_page get "/checkbox", PageController, :checkbox_page get "/clipboard", PageController, :clipboard_page + get "/code", PageController, :code_page + + get "/navigate", PageController, :navigate_page get "/collapsible", PageController, :collapsible_page @@ -109,7 +117,7 @@ defmodule E2eWeb.Router do get "/radio-group", PageController, :radio_group_page get "/timer", PageController, :timer_page - live_session :browser, on_mount: [E2eWeb.ModeLive, E2eWeb.SharedEvents] do + live_session :browser, on_mount: [E2eWeb.ModeLive, E2eWeb.ThemeLive, E2eWeb.SharedEvents] do live "/admins", AdminLive.Index, :index live "/admins/new", AdminLive.Form, :new live "/admins/:id", AdminLive.Show, :show diff --git a/e2e/mix.exs b/e2e/mix.exs index ee79b79..4ccaec4 100644 --- a/e2e/mix.exs +++ b/e2e/mix.exs @@ -68,6 +68,11 @@ defmodule E2e.MixProject do {:dns_cluster, "~> 0.2.0"}, {:bandit, "~> 1.5"}, {:corex, path: "../../corex"}, + {:makeup, "~> 1.2"}, + {:makeup_elixir, "~> 1.0.1 or ~> 1.1"}, + {:makeup_html, "~> 0.2"}, + {:makeup_css, "~> 0.2"}, + {:makeup_js, "~> 0.1.0"}, {:wallaby, "~> 0.30", only: :test}, {:a11y_audit, "~> 0.3.1", only: :test}, {:flagpack, "~> 0.6.0"}, diff --git a/e2e/mix.lock b/e2e/mix.lock index 2b8311d..c7a1bbd 100644 --- a/e2e/mix.lock +++ b/e2e/mix.lock @@ -27,11 +27,17 @@ "jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"}, "lazy_html": {:hex, :lazy_html, "0.1.10", "ffe42a0b4e70859cf21a33e12a251e0c76c1dff76391609bd56702a0ef5bc429", [:make, :mix], [{:cc_precompiler, "~> 0.1", [hex: :cc_precompiler, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.9.0", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:fine, "~> 0.1.0", [hex: :fine, repo: "hexpm", optional: false]}], "hexpm", "50f67e5faa09d45a99c1ddf3fac004f051997877dc8974c5797bb5ccd8e27058"}, "live_capture": {:hex, :live_capture, "0.2.7", "d06d636b468d3579c30dc6be35fea6c2f805b1e9d4dd67d8d96184ec6852ee60", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 1.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}], "hexpm", "e5d176d0b90292683faf5852dbd67a2d331f28430d39624754e209da20378e25"}, + "makeup": {:hex, :makeup, "1.2.1", "e90ac1c65589ef354378def3ba19d401e739ee7ee06fb47f94c687016e3713d1", [:mix], [{:nimble_parsec, "~> 1.4", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "d36484867b0bae0fea568d10131197a4c2e47056a6fbe84922bf6ba71c8d17ce"}, + "makeup_css": {:hex, :makeup_css, "0.2.3", "abbe801a520b88a0430684293f98745a7b082fb47c3902c61ec277a0fb7b79d7", [:mix], [{:makeup, "~> 1.1", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "656afcd4e96468311ac9b34ce4c91bb8521a48203afa33982d5f21edc012e79a"}, + "makeup_elixir": {:hex, :makeup_elixir, "1.0.1", "e928a4f984e795e41e3abd27bfc09f51db16ab8ba1aebdba2b3a575437efafc2", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "7284900d412a3e5cfd97fdaed4f5ed389b8f2b4cb49efc0eb3bd10e2febf9507"}, + "makeup_html": {:hex, :makeup_html, "0.2.0", "9f810da8d43d625ccd3f7ea25997e588fa541d80e0a8c6b895157ad5c7e9ca13", [:mix], [{:makeup, "~> 1.2", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "0856f7beb9a6a642ab1307e06d990fe39f0ba58690d0b8e662aa2e027ba331b2"}, + "makeup_js": {:hex, :makeup_js, "0.1.0", "ffa8ce9db95d14dcd09045334539d5992d540d63598c592d4805b7674bdd6675", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "3f0c1a5eb52c9737b1679c926574e83bb260ccdedf08b58ee96cca7c685dea75"}, "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"}, "mime": {:hex, :mime, "2.0.7", "b8d739037be7cd402aee1ba0306edfdef982687ee7e9859bee6198c1e7e2f128", [:mix], [], "hexpm", "6171188e399ee16023ffc5b76ce445eb6d9672e2e241d2df6050f3c771e80ccd"}, "mimerl": {:hex, :mimerl, "1.4.0", "3882a5ca67fbbe7117ba8947f27643557adec38fa2307490c4c4207624cb213b", [:rebar3], [], "hexpm", "13af15f9f68c65884ecca3a3891d50a7b57d82152792f3e19d88650aa126b144"}, "mint": {:hex, :mint, "1.7.1", "113fdb2b2f3b59e47c7955971854641c61f378549d73e829e1768de90fc1abf1", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1 or ~> 0.2.0 or ~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "fceba0a4d0f24301ddee3024ae116df1c3f4bb7a563a731f45fdfeb9d39a231b"}, "nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"}, + "nimble_parsec": {:hex, :nimble_parsec, "1.4.2", "8efba0122db06df95bfaa78f791344a89352ba04baedd3849593bfce4d0dc1c6", [:mix], [], "hexpm", "4b21398942dda052b403bbe1da991ccd03a053668d147d53fb8c4e0efe09c973"}, "nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"}, "parse_trans": {:hex, :parse_trans, "3.4.1", "6e6aa8167cb44cc8f39441d05193be6e6f4e7c2946cb2759f015f8c56b76e5ff", [:rebar3], [], "hexpm", "620a406ce75dada827b82e453c19cf06776be266f5a67cff34e1ef2cbb60e49a"}, "phoenix": {:hex, :phoenix, "1.8.3", "49ac5e485083cb1495a905e47eb554277bdd9c65ccb4fc5100306b350151aa95", [:mix], [{:bandit, "~> 1.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.7", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.5.3", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "36169f95cc2e155b78be93d9590acc3f462f1e5438db06e6248613f27c80caec"}, diff --git a/e2e/priv/code_examples/app.js b/e2e/priv/code_examples/app.js new file mode 100644 index 0000000..2184af5 --- /dev/null +++ b/e2e/priv/code_examples/app.js @@ -0,0 +1,6 @@ +document.addEventListener("DOMContentLoaded", () => { + const button = document.querySelector("button"); + button?.addEventListener("click", () => { + console.log("Clicked!"); + }); +}); diff --git a/e2e/priv/code_examples/component.html b/e2e/priv/code_examples/component.html new file mode 100644 index 0000000..5b3ad8e --- /dev/null +++ b/e2e/priv/code_examples/component.html @@ -0,0 +1,3 @@ +
+

Hello, <%= @name %>!

+
diff --git a/e2e/priv/code_examples/hello.ex b/e2e/priv/code_examples/hello.ex new file mode 100644 index 0000000..a242b2e --- /dev/null +++ b/e2e/priv/code_examples/hello.ex @@ -0,0 +1,5 @@ +defmodule Hello do + def world do + "Hello, World!" + end +end diff --git a/e2e/priv/code_examples/styles.css b/e2e/priv/code_examples/styles.css new file mode 100644 index 0000000..370ec50 --- /dev/null +++ b/e2e/priv/code_examples/styles.css @@ -0,0 +1,8 @@ +.greeting { + padding: 1rem; + background: var(--color-layer); +} + +.greeting h1 { + font-size: 1.5rem; +} diff --git a/e2e/priv/static/images/apple-touch-icon-180x180-480d4b1992079367b6cda44422dcbc96.png b/e2e/priv/static/images/apple-touch-icon-180x180-480d4b1992079367b6cda44422dcbc96.png new file mode 100644 index 0000000..13b654c Binary files /dev/null and b/e2e/priv/static/images/apple-touch-icon-180x180-480d4b1992079367b6cda44422dcbc96.png differ diff --git a/e2e/priv/static/images/avatar-bad9d852614f92cdfb9e547ffaede5ae.png b/e2e/priv/static/images/avatar-bad9d852614f92cdfb9e547ffaede5ae.png new file mode 100644 index 0000000..cd084b0 Binary files /dev/null and b/e2e/priv/static/images/avatar-bad9d852614f92cdfb9e547ffaede5ae.png differ diff --git a/e2e/priv/static/images/beach-53c468d9dab6f884c050d58958e6c3d2.jpg b/e2e/priv/static/images/beach-53c468d9dab6f884c050d58958e6c3d2.jpg new file mode 100644 index 0000000..3a2e18f Binary files /dev/null and b/e2e/priv/static/images/beach-53c468d9dab6f884c050d58958e6c3d2.jpg differ diff --git a/e2e/priv/static/images/corex-ui-og-8952a72b82157d452a54bfc82c001011.jpg b/e2e/priv/static/images/corex-ui-og-8952a72b82157d452a54bfc82c001011.jpg new file mode 100644 index 0000000..2a0dc6e Binary files /dev/null and b/e2e/priv/static/images/corex-ui-og-8952a72b82157d452a54bfc82c001011.jpg differ diff --git a/e2e/priv/static/images/fall-5f37212ea29d4300569b148c4b2eb2bc.jpg b/e2e/priv/static/images/fall-5f37212ea29d4300569b148c4b2eb2bc.jpg new file mode 100644 index 0000000..1cbbd98 Binary files /dev/null and b/e2e/priv/static/images/fall-5f37212ea29d4300569b148c4b2eb2bc.jpg differ diff --git a/e2e/priv/static/images/favicon-7e94605551aa8aa20243bed3756d9343.ico b/e2e/priv/static/images/favicon-7e94605551aa8aa20243bed3756d9343.ico new file mode 100644 index 0000000..a263d84 Binary files /dev/null and b/e2e/priv/static/images/favicon-7e94605551aa8aa20243bed3756d9343.ico differ diff --git a/e2e/priv/static/images/maskable-icon-512x512-1e9be444ed2d780f521ab103340c3f15.png b/e2e/priv/static/images/maskable-icon-512x512-1e9be444ed2d780f521ab103340c3f15.png new file mode 100644 index 0000000..db67fc0 Binary files /dev/null and b/e2e/priv/static/images/maskable-icon-512x512-1e9be444ed2d780f521ab103340c3f15.png differ diff --git a/e2e/priv/static/images/pwa-192x192-d874e80476b7bb8610a27acd76e24c58.png b/e2e/priv/static/images/pwa-192x192-d874e80476b7bb8610a27acd76e24c58.png new file mode 100644 index 0000000..029c1a9 Binary files /dev/null and b/e2e/priv/static/images/pwa-192x192-d874e80476b7bb8610a27acd76e24c58.png differ diff --git a/e2e/priv/static/images/pwa-512x512-b05b19fb9e7210bcde2ed7bdfa24bc27.png b/e2e/priv/static/images/pwa-512x512-b05b19fb9e7210bcde2ed7bdfa24bc27.png new file mode 100644 index 0000000..039fe66 Binary files /dev/null and b/e2e/priv/static/images/pwa-512x512-b05b19fb9e7210bcde2ed7bdfa24bc27.png differ diff --git a/e2e/priv/static/images/pwa-64x64-e3204a9e30605560ebcf76a7280099a0.png b/e2e/priv/static/images/pwa-64x64-e3204a9e30605560ebcf76a7280099a0.png new file mode 100644 index 0000000..5e21a1b Binary files /dev/null and b/e2e/priv/static/images/pwa-64x64-e3204a9e30605560ebcf76a7280099a0.png differ diff --git a/e2e/priv/static/images/sand-cb1e068d7c2553040ad21f38a607ef92.jpg b/e2e/priv/static/images/sand-cb1e068d7c2553040ad21f38a607ef92.jpg new file mode 100644 index 0000000..2732344 Binary files /dev/null and b/e2e/priv/static/images/sand-cb1e068d7c2553040ad21f38a607ef92.jpg differ diff --git a/e2e/priv/static/images/star-1981517c7246c2e0e2de660489b98c6d.jpg b/e2e/priv/static/images/star-1981517c7246c2e0e2de660489b98c6d.jpg new file mode 100644 index 0000000..973d492 Binary files /dev/null and b/e2e/priv/static/images/star-1981517c7246c2e0e2de660489b98c6d.jpg differ diff --git a/e2e/priv/static/images/winter-e3e112f7d35ec9131ea2063c45eef8b9.jpg b/e2e/priv/static/images/winter-e3e112f7d35ec9131ea2063c45eef8b9.jpg new file mode 100644 index 0000000..4182857 Binary files /dev/null and b/e2e/priv/static/images/winter-e3e112f7d35ec9131ea2063c45eef8b9.jpg differ diff --git a/e2e/test/components/action_test.exs b/e2e/test/components/action_test.exs new file mode 100644 index 0000000..cd5f072 --- /dev/null +++ b/e2e/test/components/action_test.exs @@ -0,0 +1,16 @@ +defmodule E2eWeb.ActionTest do + use ExUnit.Case, async: true + use Wallaby.Feature + + alias E2eWeb.ActionModel, as: Action + + for mode <- [:static, :live] do + @mode mode + + feature "#{@mode} - Action has no A11y violations", %{session: session} do + session + |> Action.goto(@mode) + |> Action.check_accessibility() + end + end +end diff --git a/e2e/test/components/code_test.exs b/e2e/test/components/code_test.exs new file mode 100644 index 0000000..32d9e5e --- /dev/null +++ b/e2e/test/components/code_test.exs @@ -0,0 +1,16 @@ +defmodule E2eWeb.CodeTest do + use ExUnit.Case, async: true + use Wallaby.Feature + + alias E2eWeb.CodeModel, as: Code + + for mode <- [:static, :live] do + @mode mode + + feature "#{@mode} - Code has no A11y violations", %{session: session} do + session + |> Code.goto(@mode) + |> Code.check_accessibility() + end + end +end diff --git a/e2e/test/components/navigate_test.exs b/e2e/test/components/navigate_test.exs new file mode 100644 index 0000000..42a2d1c --- /dev/null +++ b/e2e/test/components/navigate_test.exs @@ -0,0 +1,16 @@ +defmodule E2eWeb.NavigateTest do + use ExUnit.Case, async: true + use Wallaby.Feature + + alias E2eWeb.NavigateModel, as: Navigate + + for mode <- [:static, :live] do + @mode mode + + feature "#{@mode} - Navigate has no A11y violations", %{session: session} do + session + |> Navigate.goto(@mode) + |> Navigate.check_accessibility() + end + end +end diff --git a/e2e/test/support/models/action_model.ex b/e2e/test/support/models/action_model.ex new file mode 100644 index 0000000..6e9802d --- /dev/null +++ b/e2e/test/support/models/action_model.ex @@ -0,0 +1,3 @@ +defmodule E2eWeb.ActionModel do + use E2eWeb.Model, component: "action" +end diff --git a/e2e/test/support/models/code_model.ex b/e2e/test/support/models/code_model.ex new file mode 100644 index 0000000..ad61d48 --- /dev/null +++ b/e2e/test/support/models/code_model.ex @@ -0,0 +1,3 @@ +defmodule E2eWeb.CodeModel do + use E2eWeb.Model, component: "code" +end diff --git a/e2e/test/support/models/navigate_model.ex b/e2e/test/support/models/navigate_model.ex new file mode 100644 index 0000000..04a3985 --- /dev/null +++ b/e2e/test/support/models/navigate_model.ex @@ -0,0 +1,3 @@ +defmodule E2eWeb.NavigateModel do + use E2eWeb.Model, component: "navigate" +end diff --git a/guides/installation.md b/guides/installation.md index 747f725..117d2a1 100644 --- a/guides/installation.md +++ b/guides/installation.md @@ -44,7 +44,7 @@ Add `corex` to your `mix.exs` dependencies: ```elixir def deps do [ - {:corex, "~> 0.1.0-alpha.26"} + {:corex, "~> 0.1.0-alpha.27"} ] end ``` diff --git a/guides/theming.md b/guides/theming.md new file mode 100644 index 0000000..17d41f7 --- /dev/null +++ b/guides/theming.md @@ -0,0 +1,202 @@ +# Theming + +## Introduction + +This guide explains how to set up **theme selection** using the `data-theme` attribute and the `Corex.Select` component in Phoenix LiveView. + +Theme (neo, uno, duo, leo) is independent from mode (light/dark). The CSS token system uses both: `[data-theme="neo"][data-mode="dark"]`. See [Dark Mode](dark_mode.html) for mode setup. + +This approach uses [Plugs](https://hexdocs.pm/plug/Plug.html) to load the correct theme on initial render, avoiding FOUC (Flash of Unstyled Content). + +--- + +## Implementation + +### 1. Create the Theme Plug + +Create a plug that reads the theme from the `phx_theme` cookie and puts it in assigns and session: + +```elixir +defmodule MyAppWeb.Plugs.Theme do + @moduledoc """ + Reads the theme from the phx_theme cookie and puts it in assigns and session. + Allows the server to render the correct theme in the initial HTML (no flash). + """ + import Plug.Conn + + @valid_themes ~w(neo uno duo leo) + + def init(opts), do: opts + + def call(conn, _opts) do + theme = + conn.cookies["phx_theme"] + |> parse_theme() + + conn + |> assign(:theme, theme) + |> put_session(:theme, theme) + end + + defp parse_theme(nil), do: "neo" + defp parse_theme(theme) when theme in @valid_themes, do: theme + defp parse_theme(_), do: "neo" +end +``` + +### 2. Add the Plug to Your Router + +Add the Theme plug to your browser pipeline, after the Mode plug: + +```elixir +pipeline :browser do + plug :accepts, ["html"] + plug :fetch_session + plug :fetch_live_flash + plug MyAppWeb.Plugs.Mode + plug MyAppWeb.Plugs.Theme + plug :put_root_layout, html: {MyAppWeb.Layouts, :root} + plug :protect_from_forgery + plug :put_secure_browser_headers +end +``` + +### 3. Create the Theme Live Hook + +Create a LiveView hook that assigns the theme from the session: + +```elixir +defmodule MyAppWeb.ThemeLive do + @moduledoc """ + Assigns the theme from the session to the LiveView socket. + """ + def on_mount(:default, _params, session, socket) do + theme = session["theme"] || "neo" + + {:cont, Phoenix.Component.assign(socket, :theme, theme)} + end +end +``` + +Add it to your live sessions: + +```elixir +live_session :default, on_mount: [MyAppWeb.ModeLive, MyAppWeb.ThemeLive, MyAppWeb.SharedEvents] do + live "/", PageLive, :index +end +``` + +### 4. Configure Your Root Layout + +Update your `root.html.heex` to use a dynamic `data-theme` attribute and add the theme script (alongside the mode script): + +```heex + + + + + + {@inner_content} + + +``` + +### 5. Theme Select Component + +Add a theme switcher using the Select component: + +```elixir +attr :theme, :string, + default: "neo", + values: ["neo", "uno", "duo", "leo"], + doc: "the theme from cookie/session" + +def theme_toggle(assigns) do + ~H""" + <.select + id="theme-select" + class="select select--sm select--micro" + collection={[ + %{id: "neo", label: "Neo"}, + %{id: "uno", label: "Uno"}, + %{id: "duo", label: "Duo"}, + %{id: "leo", label: "Leo"} + ]} + value={[@theme]} + on_value_change_client="phx:set-theme" + > + <:label class="sr-only"> + Theme + + <:item :let={item}> + {item.label} + + <:trigger> + <.icon name="hero-swatch" /> + + <:item_indicator> + <.icon name="hero-check" /> + + + """ +end +``` + +### 6. Styling + +Import all theme CSS files so switching works. Each theme has light and dark variants, combined with `data-mode`: + +```css +@import "../corex/tokens/themes/neo/light.css"; +@import "../corex/tokens/themes/neo/dark.css"; +@import "../corex/tokens/themes/uno/light.css"; +@import "../corex/tokens/themes/uno/dark.css"; +@import "../corex/tokens/themes/duo/light.css"; +@import "../corex/tokens/themes/duo/dark.css"; +@import "../corex/tokens/themes/leo/light.css"; +@import "../corex/tokens/themes/leo/dark.css"; +``` + +--- + +## Available Themes + +- **neo** – default +- **uno** +- **duo** +- **leo** + +Theme and mode are independent. The active selector is `[data-theme="neo"][data-mode="dark"]` for Neo in dark mode. + +--- + +## Summary + +1. The theme is read from the `phx_theme` cookie on initial load (via Theme plug) +2. The theme is stored in assigns and session +3. A client-side script in `root.html.heex` initializes and persists the theme +4. LiveView receives the theme via the ThemeLive `on_mount` hook +5. The Select component dispatches `phx:set-theme` on change +6. Both cookie and localStorage stay in sync for server and client + +Theme logic is separate from mode logic: use distinct plugs, hooks, cookies, and scripts for each. diff --git a/lib/components/action.ex b/lib/components/action.ex new file mode 100644 index 0000000..773b832 --- /dev/null +++ b/lib/components/action.ex @@ -0,0 +1,66 @@ +defmodule Corex.Action do + @moduledoc """ + Renders a button element for actions based of Phoenix Core Components. + Use the `type` attribute to set the button type. + Icon-only buttons must pass `aria_label` to screen readers. + + ## Examples + ```elixir + <.action>Send! + <.action phx-click="go">Send! + <.action type="submit">Save + <.action aria_label="Close dialog"> + <.icon name="hero-x-mark" aria-hidden="true" /> + + ``` + + ## Styling + + If you wish to use the default Corex styling, you can use the class `button` on the component. + This requires to install `Mix.Tasks.Corex.Design` first and import the component css file. + + ```css + @import "../corex/main.css"; + @import "../corex/tokens/themes/neo/light.css"; + @import "../corex/components/button.css"; + ``` + + You can then use modifiers + + ```heex + <.action class="button button--accent button--lg"> + ``` + + Learn more about modifiers and [Corex Design](https://corex-ui.com/components/button#modifiers) + """ + + @doc type: :component + use Phoenix.Component + + attr(:type, :string, + default: "button", + values: ["button", "submit", "reset"], + doc: "The button type, defaults to button to prevent accidental form submissions" + ) + + attr(:aria_label, :string, + default: nil, + doc: "Required for icon-only buttons, describes the button to screen readers" + ) + + attr(:disabled, :boolean, default: false, doc: "Disables the button") + + attr(:rest, :global, + include: ~w(class name value form phx-click phx-submit phx-disable-with phx-value) + ) + + slot(:inner_block, required: true) + + def action(assigns) do + ~H""" + + """ + end +end diff --git a/lib/components/clipboard.ex b/lib/components/clipboard.ex index 414f467..0168ff1 100644 --- a/lib/components/clipboard.ex +++ b/lib/components/clipboard.ex @@ -159,6 +159,11 @@ defmodule Corex.Clipboard do "Accessible name for the input when it's not associated with a visible label (e.g. \"Value to copy\")" ) + attr(:input, :boolean, + default: true, + doc: "Whether to render the input element. Set to false when using only the trigger to copy." + ) + attr(:rest, :global) slot :label, required: false do @@ -198,6 +203,7 @@ defmodule Corex.Clipboard do
diff --git a/lib/components/code.ex b/lib/components/code.ex new file mode 100644 index 0000000..a6b89b5 --- /dev/null +++ b/lib/components/code.ex @@ -0,0 +1,156 @@ +defmodule Corex.Code do + @moduledoc ~S''' + Displays syntax-highlighted code with Makeup. + + ## Installation + + Add `makeup` and the lexer packages for each language you use: + + ```elixir + defp deps do + [ + {:makeup, "~> 1.2"}, + {:makeup_elixir, "~> 1.0.1 or ~> 1.1"}, + {:makeup_html, "~> 1.0"}, + {:makeup_css, "~> 1.0"}, + {:makeup_js, "~> 1.0"} + ] + end + ``` + + Only `makeup` is required. Add the lexer packages you need; without a lexer, code renders as plain escaped text. + + ## Examples + + ### Basic Usage + + ```heex + <.code code="def hello, do: :world" /> + ``` + + ### Multi-line with heredoc + + Use `"""` heredoc for readable multi-line code in templates: + + ```heex + <.code code={""" + defmodule Hello do + def world, do: "Hello, World!" + end + """} /> + ``` + + ### Loading from a file + + ```heex + <.code code={File.read!("priv/code_examples/example.ex")} language={:elixir} /> + ``` + + Or in a controller, load the file and pass the contents: + + ```elixir + def my_page(conn, _params) do + code = File.read!("priv/code_examples/example.ex") + + render(conn, :my_page, code: code) + end + ``` + + ```heex + <.code code={@code} language={:elixir} /> + ``` + + The `language` attribute maps to Makeup's registry (e.g. `:elixir` → `"elixir"`). + Add the corresponding lexer package to your deps for each language. Without it, code renders as plain escaped text. + + ## Styling + + Use data attributes to target elements: + + ```css + [data-scope="code"][data-part="root"] {} + [data-scope="code"][data-part="content"] {} + ``` + + With Corex Design (`mix corex.design`), syntax highlighting styles are included in + `code.css`. Nothing else is required. + + Without Corex Design, run `mix corex.code` to generate the Makeup stylesheet and + import it in your CSS: + + ```bash + mix corex.code + mix corex.code assets/styles/syntax.css + mix corex.code --force + ``` + + ```css + @import "./code_highlight.css"; + ``` + ''' + + @doc type: :component + use Phoenix.Component + + attr(:code, :string, required: true, doc: "The raw source code to display") + + attr(:language, :atom, + default: :elixir, + doc: + "The language name in Makeup's registry (e.g. :elixir, :html). Add the lexer package to deps; otherwise renders as plain escaped text." + ) + + attr(:rest, :global) + + def code(assigns) do + makeup = Module.concat(["Elixir", "Makeup"]) + + unless Code.ensure_loaded?(makeup) do + raise """ + Corex.Code requires makeup. + + Add it to your mix.exs: + + {:makeup, "~> 1.2"} + + Add lexer packages for each language you use (e.g. makeup_elixir, makeup_html). + """ + end + + assigns = + assigns + |> assign(:lexer, lexer_for(assigns.language)) + |> then(&assign(&1, :highlighted_html, highlight_code(&1))) + + ~H""" +
+
+
{Phoenix.HTML.raw(@highlighted_html)}
+
+
+ """ + end + + defp lexer_for(language) do + name = to_string(language) + registry = Module.concat(["Elixir", "Makeup", "Registry"]) + + case apply(registry, :fetch_lexer_by_name, [name]) do + {:ok, {lexer, _opts}} -> lexer + :error -> nil + end + end + + defp highlight_code(assigns) do + case assigns.lexer do + nil -> + assigns.code + |> Phoenix.HTML.html_escape() + |> Phoenix.HTML.safe_to_string() + + lexer -> + makeup = Module.concat(["Elixir", "Makeup"]) + apply(makeup, :highlight_inner_html, [assigns.code, [lexer: lexer]]) + end + end +end diff --git a/lib/components/combobox.ex b/lib/components/combobox.ex index c09dce3..8427018 100644 --- a/lib/components/combobox.ex +++ b/lib/components/combobox.ex @@ -131,7 +131,7 @@ defmodule Corex.Combobox do ### Server-side Filtering - Use `on_input_value_change` to filter on the server. This example uses a local list; replace with a database query for real apps. + Disable client filtering with `disabled={false}` and use `on_input_value_change` to filter on the server. This example uses a local list; replace with a database query for real apps. ```heex defmodule MyAppWeb.CountryCombobox do @@ -168,6 +168,7 @@ defmodule Corex.Combobox do <.combobox id="country-combobox" collection={@items} + filter={false} on_input_value_change="search" > <:empty>No results diff --git a/lib/components/listbox/connect.ex b/lib/components/listbox/connect.ex index 1e2c7b6..3d2d6a0 100644 --- a/lib/components/listbox/connect.ex +++ b/lib/components/listbox/connect.ex @@ -89,7 +89,7 @@ defmodule Corex.Listbox.Connect do "data-scope" => "listbox", "data-part" => "label", "dir" => assigns.dir, - "id" => "select:#{assigns.id}:label" + "id" => "listbox:#{assigns.id}:label" } end diff --git a/lib/components/navigate.ex b/lib/components/navigate.ex new file mode 100644 index 0000000..9976a18 --- /dev/null +++ b/lib/components/navigate.ex @@ -0,0 +1,109 @@ +defmodule Corex.Navigate do + @moduledoc """ + Renders an anchor element for navigation based of Phoenix Core Components and Phoenix Link + + Supports plain href, LiveView navigate, and LiveView patch. + External links should be flagged with the `external` attribute. + Icon-only links must pass `aria_label` to screen readers. + + ## Examples + + ```heex + <.navigate to="/about">About + <.navigate to={~p"/dashboard"} type="navigate">Dashboard + <.navigate to={~p"/items"} type="patch">Filter + <.navigate to="https://example.com" external> + External + ... + + <.navigate to="/file.pdf" download="report.pdf"> + Download PDF + ... + + <.navigate to="/profile" aria_label="View profile"> + + + ``` + + ## Styling + + If you wish to use the default Corex styling, you can use the class `link` on the component. + This requires to install `Mix.Tasks.Corex.Design` first and import the component css file. + + ```css + @import "../corex/main.css"; + @import "../corex/tokens/themes/neo/light.css"; + @import "../corex/components/link.css"; + ``` + + You can then use modifiers + + ```heex + <.navigate class="link link--accent link--lg"> + ``` + + Learn more about modifiers and [Corex Design](https://corex-ui.com/components/link#modifiers) + """ + + @doc type: :component + use Phoenix.Component + + attr(:to, :string, required: true, doc: "The destination URL") + + attr(:type, :string, + default: "href", + values: ["href", "navigate", "patch"], + doc: "The navigation type, defaults to href" + ) + + attr(:external, :boolean, + default: false, + doc: + "Marks the link as external, only valid with type=\"href\". Adds target=\"_blank\" and rel=\"noopener noreferrer\"" + ) + + attr(:download, :any, + default: nil, + doc: "Prompts the browser to download the target, accepts a boolean or filename string" + ) + + attr(:aria_label, :string, + default: nil, + doc: "Required for icon-only links, describes the link to screen readers" + ) + + attr(:rest, :global, include: ~w(class method replace)) + slot(:inner_block, required: true) + + def navigate(%{rest: %{replace: _}, type: "href"} = assigns) do + IO.warn("<.navigate> replace has no effect with type=\"href\"") + navigate(Map.update!(assigns, :rest, &Map.delete(&1, :replace))) + end + + def navigate(%{rest: %{method: _}, type: type} = assigns) when type in ["navigate", "patch"] do + IO.warn("<.navigate> method has no effect with type=\"#{type}\"") + navigate(Map.update!(assigns, :rest, &Map.delete(&1, :method))) + end + + def navigate(%{external: true, type: type} = assigns) when type in ["navigate", "patch"] do + IO.warn("<.navigate> external has no effect with type=\"#{type}\"") + navigate(%{assigns | external: false}) + end + + def navigate(assigns) do + ~H""" + <.link + href={@type == "href" && @to} + navigate={@type == "navigate" && @to} + patch={@type == "patch" && @to} + download={@download} + aria-label={@aria_label} + target={@external && "_blank"} + rel={@external && "noopener noreferrer"} + {@rest} + > + {render_slot(@inner_block)} + + """ + end +end diff --git a/lib/corex.ex b/lib/corex.ex index 8cf9edb..db6bfaf 100644 --- a/lib/corex.ex +++ b/lib/corex.ex @@ -2,47 +2,45 @@ defmodule Corex do @moduledoc false @components %{ - accordion: - {Corex.Accordion, - [ - accordion: 1, - accordion_skeleton: 1 - ]}, - combobox: {Corex.Combobox, [combobox: 1]}, - switch: {Corex.Switch, [switch: 1]}, - toggle_group: {Corex.ToggleGroup, [toggle_group: 1]}, - toast: - {Corex.Toast, - [ - toast_group: 1, - toast_client_error: 1, - toast_server_error: 1, - toast_connected: 1, - toast_disconnected: 1 - ]}, - form: {Corex.Form, [get_form_id: 1]}, - select: {Corex.Select, [select: 1]}, + accordion: {Corex.Accordion, [accordion: 1, accordion_skeleton: 1]}, + action: {Corex.Action, [action: 1]}, + angle_slider: {Corex.AngleSlider, [angle_slider: 1]}, + avatar: {Corex.Avatar, [avatar: 1]}, + carousel: {Corex.Carousel, [carousel: 1]}, checkbox: {Corex.Checkbox, [checkbox: 1]}, - tabs: {Corex.Tabs, [tabs: 1, tabs_trigger: 1, tabs_content: 1]}, clipboard: {Corex.Clipboard, [clipboard: 1]}, + code: {Corex.Code, [code: 1]}, collapsible: {Corex.Collapsible, [collapsible: 1]}, + combobox: {Corex.Combobox, [combobox: 1]}, + date_picker: {Corex.DatePicker, [date_picker: 1]}, dialog: {Corex.Dialog, [dialog: 1, dialog_title: 1, dialog_description: 1, dialog_close_trigger: 1]}, - date_picker: {Corex.DatePicker, [date_picker: 1]}, - signature_pad: {Corex.SignaturePad, [signature_pad: 1]}, - menu: {Corex.Menu, [menu: 1]}, - tree_view: {Corex.TreeView, [tree_view: 1, tree_item: 1, tree_branch: 1]}, - angle_slider: {Corex.AngleSlider, [angle_slider: 1]}, - avatar: {Corex.Avatar, [avatar: 1]}, - carousel: {Corex.Carousel, [carousel: 1]}, editable: {Corex.Editable, [editable: 1]}, floating_panel: {Corex.FloatingPanel, [floating_panel: 1]}, + form: {Corex.Form, [get_form_id: 1]}, listbox: {Corex.Listbox, [listbox: 1]}, + menu: {Corex.Menu, [menu: 1]}, + navigate: {Corex.Navigate, [navigate: 1]}, number_input: {Corex.NumberInput, [number_input: 1]}, password_input: {Corex.PasswordInput, [password_input: 1]}, pin_input: {Corex.PinInput, [pin_input: 1]}, radio_group: {Corex.RadioGroup, [radio_group: 1]}, - timer: {Corex.Timer, [timer: 1]} + select: {Corex.Select, [select: 1]}, + signature_pad: {Corex.SignaturePad, [signature_pad: 1]}, + switch: {Corex.Switch, [switch: 1]}, + tabs: {Corex.Tabs, [tabs: 1, tabs_trigger: 1, tabs_content: 1]}, + timer: {Corex.Timer, [timer: 1]}, + toast: + {Corex.Toast, + [ + toast_group: 1, + toast_client_error: 1, + toast_server_error: 1, + toast_connected: 1, + toast_disconnected: 1 + ]}, + toggle_group: {Corex.ToggleGroup, [toggle_group: 1]}, + tree_view: {Corex.TreeView, [tree_view: 1, tree_item: 1, tree_branch: 1]} } defmacro __using__(opts \\ []) do diff --git a/lib/mix/tasks/corex.code.ex b/lib/mix/tasks/corex.code.ex new file mode 100644 index 0000000..a7199d3 --- /dev/null +++ b/lib/mix/tasks/corex.code.ex @@ -0,0 +1,91 @@ +defmodule Mix.Tasks.Corex.Code do + use Mix.Task + + @shortdoc "Generate and copy Makeup syntax highlighting stylesheet to your project" + + @default_path "assets/css/code_highlight.css" + + @moduledoc """ + Generates the full Makeup syntax highlighting stylesheet and writes it to your project. + + Run when you add or update Makeup language packages (e.g. makeup_elixir, makeup_html, makeup_css). + + Requires `makeup` and `makeup_elixir` in your deps. + + ## Examples + + # Default path (assets/css/code_highlight.css) + mix corex.code + + # Custom path and filename + mix corex.code assets/styles/syntax.css + + # Override if file already exists + mix corex.code --force + mix corex.code assets/css/code_highlight.css --force + + ## Import + + Add the generated file to your CSS (e.g. assets/css/app.css): + + @import "./code_highlight.css"; + + Works with or without Corex Design. For unstyled projects, import only the highlight file. + """ + + @impl Mix.Task + def run(args) do + Mix.Task.run("app.start") + + {opts, paths, _} = + OptionParser.parse(args, switches: [force: :boolean]) + + path = List.first(paths) || @default_path + force = Keyword.get(opts, :force, false) + full_path = Path.expand(path, File.cwd!()) + + validate_makeup!() + validate_path!(full_path, force) + generate!(full_path) + + Mix.shell().info("Makeup stylesheet written to: #{path}") + end + + defp validate_makeup! do + makeup = Module.concat(["Elixir", "Makeup"]) + + unless Code.ensure_loaded?(makeup) do + Mix.raise(""" + Makeup is not available. Add to your mix.exs: + + defp deps do + [ + {:makeup, "~> 1.2"}, + {:makeup_elixir, "~> 1.0.1 or ~> 1.1"} + ] + end + """) + end + end + + defp validate_path!(full_path, force) do + if File.exists?(full_path) and not force do + Mix.raise(""" + #{full_path} already exists. + + Re-run with --force to overwrite. + """) + end + end + + defp generate!(full_path) do + makeup = Module.concat(["Elixir", "Makeup"]) + stylesheet = apply(makeup, :stylesheet, [:default_style]) + + full_path + |> Path.dirname() + |> File.mkdir_p!() + + File.write!(full_path, stylesheet) + end +end diff --git a/mix.exs b/mix.exs index 728d368..e00d53b 100644 --- a/mix.exs +++ b/mix.exs @@ -1,7 +1,7 @@ defmodule Corex.MixProject do use Mix.Project - @version "0.1.0-alpha.26" + @version "0.1.0-alpha.27" @elixir_requirement "~> 1.15" def project do @@ -96,6 +96,7 @@ defmodule Corex.MixProject do main: "Corex", extras: [ "guides/installation.md", + "guides/theming.md", "guides/dark_mode.md", "guides/locale.md", "guides/rtl.md", @@ -116,11 +117,13 @@ defmodule Corex.MixProject do [ Components: [ Corex.Accordion, + Corex.Action, Corex.AngleSlider, Corex.Avatar, Corex.Carousel, Corex.Checkbox, Corex.Clipboard, + Corex.Code, Corex.Combobox, Corex.Collapsible, Corex.DatePicker, @@ -129,6 +132,7 @@ defmodule Corex.MixProject do Corex.FloatingPanel, Corex.Listbox, Corex.Menu, + Corex.Navigate, Corex.NumberInput, Corex.PasswordInput, Corex.PinInput, diff --git a/package.json b/package.json index b5553d4..38e26e6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "corex", - "version": "0.1.0-alpha.26", + "version": "0.1.0-alpha.27", "description": "The official JavaScript client for the Corex Phoenix Hooks.", "license": "MIT", "module": "./priv/static/corex.mjs", @@ -62,36 +62,36 @@ "@eslint/js": "^9.39.2", "@typescript-eslint/eslint-plugin": "^8.0.0", "@typescript-eslint/parser": "^8.0.0", -"@zag-js/accordion": "^1.34.0", - "@zag-js/angle-slider": "^1.34.0", - "@zag-js/avatar": "^1.34.0", - "@zag-js/carousel": "^1.34.0", - "@zag-js/collection": "^1.34.0", - "@zag-js/checkbox": "^1.34.0", - "@zag-js/clipboard": "^1.34.0", - "@zag-js/collapsible": "^1.34.0", - "@zag-js/combobox": "^1.34.0", - "@zag-js/date-picker": "^1.34.0", - "@zag-js/dialog": "^1.34.0", - "@zag-js/editable": "^1.34.0", - "@zag-js/floating-panel": "^1.34.0", - "@zag-js/listbox": "^1.34.0", - "@zag-js/menu": "^1.34.0", - "@zag-js/number-input": "^1.34.0", - "@zag-js/password-input": "^1.34.0", - "@zag-js/pin-input": "^1.34.0", - "@zag-js/popper": "^1.34.0", - "@zag-js/radio-group": "^1.34.0", - "@zag-js/select": "^1.34.0", - "@zag-js/signature-pad": "^1.34.0", - "@zag-js/switch": "^1.34.0", - "@zag-js/tabs": "^1.34.0", - "@zag-js/timer": "^1.34.0", - "@zag-js/toast": "^1.34.0", - "@zag-js/toggle-group": "^1.34.0", - "@zag-js/tree-view": "^1.34.0", - "@zag-js/types": "^1.34.0", - "@zag-js/vanilla": "^1.34.0", +"@zag-js/accordion": "^1.34.1", + "@zag-js/angle-slider": "^1.34.1", + "@zag-js/avatar": "^1.34.1", + "@zag-js/carousel": "^1.34.1", + "@zag-js/collection": "^1.34.1", + "@zag-js/checkbox": "^1.34.1", + "@zag-js/clipboard": "^1.34.1", + "@zag-js/collapsible": "^1.34.1", + "@zag-js/combobox": "^1.34.1", + "@zag-js/date-picker": "^1.34.1", + "@zag-js/dialog": "^1.34.1", + "@zag-js/editable": "^1.34.1", + "@zag-js/floating-panel": "^1.34.1", + "@zag-js/listbox": "^1.34.1", + "@zag-js/menu": "^1.34.1", + "@zag-js/number-input": "^1.34.1", + "@zag-js/password-input": "^1.34.1", + "@zag-js/pin-input": "^1.34.1", + "@zag-js/popper": "^1.34.1", + "@zag-js/radio-group": "^1.34.1", + "@zag-js/select": "^1.34.1", + "@zag-js/signature-pad": "^1.34.1", + "@zag-js/switch": "^1.34.1", + "@zag-js/tabs": "^1.34.1", + "@zag-js/timer": "^1.34.1", + "@zag-js/toast": "^1.34.1", + "@zag-js/toggle-group": "^1.34.1", + "@zag-js/tree-view": "^1.34.1", + "@zag-js/types": "^1.34.1", + "@zag-js/vanilla": "^1.34.1", "eslint": "^9.39.2", "eslint-config-prettier": "^9.1.0", "eslint-plugin-prettier": "^5.2.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9ad876f..45e1209 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -21,95 +21,95 @@ importers: specifier: ^8.0.0 version: 8.54.0(eslint@9.39.2)(typescript@5.9.3) '@zag-js/accordion': - specifier: ^1.34.0 - version: 1.34.0 + specifier: ^1.34.1 + version: 1.34.1 '@zag-js/angle-slider': - specifier: ^1.34.0 - version: 1.34.0 + specifier: ^1.34.1 + version: 1.34.1 '@zag-js/avatar': - specifier: ^1.34.0 - version: 1.34.0 + specifier: ^1.34.1 + version: 1.34.1 '@zag-js/carousel': - specifier: ^1.34.0 - version: 1.34.0 + specifier: ^1.34.1 + version: 1.34.1 '@zag-js/checkbox': - specifier: ^1.34.0 - version: 1.34.0 + specifier: ^1.34.1 + version: 1.34.1 '@zag-js/clipboard': - specifier: ^1.34.0 - version: 1.34.0 + specifier: ^1.34.1 + version: 1.34.1 '@zag-js/collapsible': - specifier: ^1.34.0 - version: 1.34.0 + specifier: ^1.34.1 + version: 1.34.1 '@zag-js/collection': - specifier: ^1.34.0 - version: 1.34.0 + specifier: ^1.34.1 + version: 1.34.1 '@zag-js/combobox': - specifier: ^1.34.0 - version: 1.34.0 + specifier: ^1.34.1 + version: 1.34.1 '@zag-js/date-picker': - specifier: ^1.34.0 - version: 1.34.0(@internationalized/date@3.11.0) + specifier: ^1.34.1 + version: 1.34.1(@internationalized/date@3.11.0) '@zag-js/dialog': - specifier: ^1.34.0 - version: 1.34.0 + specifier: ^1.34.1 + version: 1.34.1 '@zag-js/editable': - specifier: ^1.34.0 - version: 1.34.0 + specifier: ^1.34.1 + version: 1.34.1 '@zag-js/floating-panel': - specifier: ^1.34.0 - version: 1.34.0 + specifier: ^1.34.1 + version: 1.34.1 '@zag-js/listbox': - specifier: ^1.34.0 - version: 1.34.0 + specifier: ^1.34.1 + version: 1.34.1 '@zag-js/menu': - specifier: ^1.34.0 - version: 1.34.0 + specifier: ^1.34.1 + version: 1.34.1 '@zag-js/number-input': - specifier: ^1.34.0 - version: 1.34.0 + specifier: ^1.34.1 + version: 1.34.1 '@zag-js/password-input': - specifier: ^1.34.0 - version: 1.34.0 + specifier: ^1.34.1 + version: 1.34.1 '@zag-js/pin-input': - specifier: ^1.34.0 - version: 1.34.0 + specifier: ^1.34.1 + version: 1.34.1 '@zag-js/popper': - specifier: ^1.34.0 - version: 1.34.0 + specifier: ^1.34.1 + version: 1.34.1 '@zag-js/radio-group': - specifier: ^1.34.0 - version: 1.34.0 + specifier: ^1.34.1 + version: 1.34.1 '@zag-js/select': - specifier: ^1.34.0 - version: 1.34.0 + specifier: ^1.34.1 + version: 1.34.1 '@zag-js/signature-pad': - specifier: ^1.34.0 - version: 1.34.0 + specifier: ^1.34.1 + version: 1.34.1 '@zag-js/switch': - specifier: ^1.34.0 - version: 1.34.0 + specifier: ^1.34.1 + version: 1.34.1 '@zag-js/tabs': - specifier: ^1.34.0 - version: 1.34.0 + specifier: ^1.34.1 + version: 1.34.1 '@zag-js/timer': - specifier: ^1.34.0 - version: 1.34.0 + specifier: ^1.34.1 + version: 1.34.1 '@zag-js/toast': - specifier: ^1.34.0 - version: 1.34.0 + specifier: ^1.34.1 + version: 1.34.1 '@zag-js/toggle-group': - specifier: ^1.34.0 - version: 1.34.0 + specifier: ^1.34.1 + version: 1.34.1 '@zag-js/tree-view': - specifier: ^1.34.0 - version: 1.34.0 + specifier: ^1.34.1 + version: 1.34.1 '@zag-js/types': - specifier: ^1.34.0 - version: 1.34.0 + specifier: ^1.34.1 + version: 1.34.1 '@zag-js/vanilla': - specifier: ^1.34.0 - version: 1.34.0 + specifier: ^1.34.1 + version: 1.34.1 eslint: specifier: ^9.39.2 version: 9.39.2 @@ -279,144 +279,144 @@ packages: resolution: {integrity: sha512-VFlhGSl4opC0bprJiItPQ1RfUhGDIBokcPwaFH4yiBCaNPeld/9VeXbiPO1cLyorQi1G1vL+ecBk1x8o1axORA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@zag-js/accordion@1.34.0': - resolution: {integrity: sha512-4zK897tjM72D65dBZ+DKwxzJXV1Hy/QjDANDiLukpVE8oD+bO4jwh5i8Ee7U3n8YuJFluM7Ajn+xZ/HQdg9yJA==} + '@zag-js/accordion@1.34.1': + resolution: {integrity: sha512-FEk9iBwoCkbs96386MfKG7fKZ6uAnID/ppkYFs3FatTf79liJi6cQekWuOxhDPwpmbndCBNSO+eRJgsN1DWGxw==} - '@zag-js/anatomy@1.34.0': - resolution: {integrity: sha512-c13dPCG7Sa8Xqhz25AoZFWvMkhw5RlNgTOPD0Z6ZXpURNauLn1r5C9a/H0SAdO66yOsPHJoK8LZxoL4BiGE8KQ==} + '@zag-js/anatomy@1.34.1': + resolution: {integrity: sha512-FQuhlB5ggZvC2AKy52qITBe8Q8GCHdO+tsSY0A7kOe1C3wDnZNRCKtiNfkcIlEc3uP+alMhF/7xn09N7XXOTcQ==} - '@zag-js/angle-slider@1.34.0': - resolution: {integrity: sha512-WEQTxxiqqfh025Bq7mhfYQc0AMua8dWWjmCOrGxXQD2kVI58RMNNCBImcNVYyvyCd97Om5rcaVEzaJ248xBNxQ==} + '@zag-js/angle-slider@1.34.1': + resolution: {integrity: sha512-TQjzs9GZMWX4TKCmO164eWBnmw9R0EzQUkbEMSPgQ5Ue2vxxU4CCFlcn2HNlP0cz1PASk2xnMPRhcoPoRUTM+w==} - '@zag-js/aria-hidden@1.34.0': - resolution: {integrity: sha512-aloo68LsjiHpZdR+qzNNBuFP6F72gPfwpBmPmcVUQbHp0o5nhT+oxmcqg1A3xeuJkvJibQVqgaSep2YMcVZSQQ==} + '@zag-js/aria-hidden@1.34.1': + resolution: {integrity: sha512-3oO8T4ezzZI+DSDTaPZ4ZE6wdzHqywlz2Q6sqwjnsE46NcmlnZkaFgDPJCNPH56tXLsB7uE7A6KXLYxQrLf6fw==} - '@zag-js/avatar@1.34.0': - resolution: {integrity: sha512-rlJ8xpNLqSFxpyFtO2j+JRI0LTPgqzviEWSO/ejb+1w/VvB2SHPfslHfJsLqll9yUwnEvEn6+kEalcTPn/V5RQ==} + '@zag-js/avatar@1.34.1': + resolution: {integrity: sha512-O7Bt02kZiTozNsEaWWgGYyTOU3wFejQIQl1Y0bYEeFbBQ2y75pmffhK9792UW6oZ+nwQ+s/0/kAMkzZWSl3qww==} - '@zag-js/carousel@1.34.0': - resolution: {integrity: sha512-g9pmUfWQlII0glsltLJb12L6EXsw+ms69xELAF58pZxwxK4hss0M2ZZRRY/JOgU9nX6UtZTs8CdfaKI+bIyP5g==} + '@zag-js/carousel@1.34.1': + resolution: {integrity: sha512-aKOXJCslU8HoPp54sNv0tQraVG00fq9qL/ZwmZAzKw6G2kLtHaHtxBunQMRVrVGCYxXqsdoHWGikKT9PJWIICg==} - '@zag-js/checkbox@1.34.0': - resolution: {integrity: sha512-47rEtbHXUh5t8bgk3j18lDvhoMTkxTKLBRUKx/TlNYl0r6/RwUWp4yRUpOM0NQkV3wvFyMc6LqTrOKRK6KKngg==} + '@zag-js/checkbox@1.34.1': + resolution: {integrity: sha512-qj9paN6hcFwmQz+T7ABrlN8zVeA/11WLB2JDhno2xuJjdHDmOSWnWVxCEf8lv4nxcw3SUnJnMgFtAWLd0xJkFA==} - '@zag-js/clipboard@1.34.0': - resolution: {integrity: sha512-Wkm4sWwXcIvpeZEdaUQB7Oy91UMUm1Rfyb/UjWUv12gMlXTAmdUucaT2yR7cPanYzUzwOR3ZNepwOxC2CjYE6w==} + '@zag-js/clipboard@1.34.1': + resolution: {integrity: sha512-O1xH2e3E+GYXubA/vtBkuE3AD81hO3N5P1weA6iHOCyDl8CAtKMZZ1xMuCTwt+wujwdvM1DT4IF+II9fBMngAw==} - '@zag-js/collapsible@1.34.0': - resolution: {integrity: sha512-P2d4DLVpVbLP0Qfq/rWjyCSgj380LKcnU04xLQV4ujAESx6Ga+w6MbW09ywCZGi1JPWlRu6A06bhVSRvyHTGHg==} + '@zag-js/collapsible@1.34.1': + resolution: {integrity: sha512-1u32Zz/shcmxPbPsXuiIc5D9eIoZdpFoLuzlUzGlutBzq7zoavmMVIGmSevBEn12a6vhk/dPoiz2+OAKXi+2ww==} - '@zag-js/collection@1.34.0': - resolution: {integrity: sha512-2fU7l/iRpCSsngRYTfbnVOFLGhlylk4+IuJgWZPpBHafDv5hg14Ju5OZK0U+SCFSkHDvN/vnr/4QllIftBWsfQ==} + '@zag-js/collection@1.34.1': + resolution: {integrity: sha512-C61BL3cFyEfPCsPKeWshc8l7D3igqySPksVtmfzHhxcnlOxgVkBBpik7JF/cYk9yU6pHB6bCRzRgPDziiyOHLQ==} - '@zag-js/combobox@1.34.0': - resolution: {integrity: sha512-fsTfGCDBbyYaWAmsF7VsOh5LIpqhbd2IxMLl41I8GPeQIRkiqomuOjEmq4vNiNIBEGVPMREFWb67ADlivZcPOQ==} + '@zag-js/combobox@1.34.1': + resolution: {integrity: sha512-YzqOYbfh8vDmc/ewj3DetGHt0hwYuvDV/2BdCQJ5rLtLK8iLJuTB79x8l6QJBCi93BG64E7aY7Wa7jyK+1oILQ==} - '@zag-js/core@1.34.0': - resolution: {integrity: sha512-+w6zQ5PiBNbUd2xu91FUQruIAHcTYsueUNlXz3biAEAcU7pzre3Es4ApTepkbijYjcsiXzI/QwTtujot53eVyA==} + '@zag-js/core@1.34.1': + resolution: {integrity: sha512-GjOZOL/p0tsuH/koYR+mSoegs3JXpP0ZkFuidAf0R4zqklNSdTLzhb00CZHGZcdWCExhmG18YwpjbEx8eyfV5g==} - '@zag-js/date-picker@1.34.0': - resolution: {integrity: sha512-0jF2clkuwyK6fIIkHd8RsqWO+/zl6xwK5HHEJoLHWEbWaJ4DvOfjadQD/5bHGIbCulZb37yHKgidI3pcQ1R3uw==} + '@zag-js/date-picker@1.34.1': + resolution: {integrity: sha512-ubhpCRDf7vVXSvToJvnsnqjTB2hhbsYEaeIrwsHTFAFOETD15A5H/aC5PeiwCouLEbTQDiIoaDlOsNh57IO7XQ==} peerDependencies: '@internationalized/date': '>=3.0.0' - '@zag-js/date-utils@1.34.0': - resolution: {integrity: sha512-9LjsBA4oCq1+jq33CTw469lhNhCajAvN1QyNGLTfzRdUDfaJE1/egv3akM2Gn+FaKU1pOWB11uJiK+VR6Eui9w==} + '@zag-js/date-utils@1.34.1': + resolution: {integrity: sha512-UG0aooQXNWINlO8RWcBFv6aEHE1jAFKU4vNprh0nDOl61O0+j+lbV0qYushgyZ7O6aYSoI+tdb/Myv+BPqQxyg==} peerDependencies: '@internationalized/date': '>=3.0.0' - '@zag-js/dialog@1.34.0': - resolution: {integrity: sha512-w52x1jCgpmaOx5zmJ2878sX6yIjz7QN/ICT5eH22uoyZQ1YKl1XlFUYf3SOCzY3svs4gSRHLd+/KFA/u0YEGGQ==} + '@zag-js/dialog@1.34.1': + resolution: {integrity: sha512-2PfuUhC2e0ouQ2s2F9qmeThwTMGptVCAMDNMh1ZOPeVzQLvnsIQDvcYHeb62BuKgYDeoUiz3Tz/ns/sV/VI1kw==} - '@zag-js/dismissable@1.34.0': - resolution: {integrity: sha512-R+H2E1gasWhChnBBXgzWSnok5NIL4IG9XfVPFTOWNz5OldqOTWH6p0xZgVbO85/ifBDmF3C+663ZeaZ1EW49EA==} + '@zag-js/dismissable@1.34.1': + resolution: {integrity: sha512-7dj7G0Cl6Fu7IHENdLT1tFeePuq8BqxPT6xANdk66hnpVeyDKcrR3XjN+ChWpYxGIkeOfPGYsjRHx1kDiFv89g==} - '@zag-js/dom-query@1.34.0': - resolution: {integrity: sha512-bK3rEAsY130SPrRsWn+M4Dim2Ne92Sq+RsRQ4I9UTOa98OeSPVc8F77zPdoc4aKS+XAWWpzNASTfDjYhAy1gKQ==} + '@zag-js/dom-query@1.34.1': + resolution: {integrity: sha512-7GaxMaShtWsGgsz9jPfi+aJSQrx7aVFnCi5edGet76Tcytv0MRTqFKUcpSCWu3W3znBt8tQO07Mu1SBSNQk07Q==} - '@zag-js/editable@1.34.0': - resolution: {integrity: sha512-ABJl+HusXMFidoGClcuH8rOhkw/9StKKak9WSQbHRcgd4f3GQQt7h8ENMaxmaK2FwZKywcoNnVB4+OdssVAxWQ==} + '@zag-js/editable@1.34.1': + resolution: {integrity: sha512-IXR8TE/S7uuTOY/Mj3tk8zmXlpgym9OUYZGFjohVdXeUC9INIeO27ei8mfAxY3JLqnYIyrLGD0Q7OUUj2L/vTQ==} - '@zag-js/floating-panel@1.34.0': - resolution: {integrity: sha512-5bd/5sOGfHunfKFXxQm96QbS6RgezB72nHW6Ir/wD1/CxClcRKILYwznx6E3BxpqPaOq8s6JdsMnLdzSoASoAw==} + '@zag-js/floating-panel@1.34.1': + resolution: {integrity: sha512-2yreCW74MO047Yd3jjofFyJpbfd55E9TBw0Hh4W1kMPpWg8E3XnL4gWrYYKX6ycjeWNVNNvyAg1O/0qiM49fiA==} - '@zag-js/focus-trap@1.34.0': - resolution: {integrity: sha512-vejkVmkqQ5Uv3/bhFaFovC2ownIwN+3Bh2CGLhqTI+bijsGtbFpmcpmwVXxnWQdO9VKaesygChFLmgQaFJ9UWw==} + '@zag-js/focus-trap@1.34.1': + resolution: {integrity: sha512-vfIC3diaYbdxDmKmNASZYx0RrNj6bG0InrIgQF0Kk/qI81FcK/VXQDjpXnnWC+yG2u3hG8h+6kbfgHtgoCuDjQ==} - '@zag-js/focus-visible@1.34.0': - resolution: {integrity: sha512-tHtvqxiBsNfzDaZaD8qpSBl7PRndeDrVLgCX80M0bcYu6f1RHJY53qGriBFqocRuKlNtSASE39JAicKCojQzpw==} + '@zag-js/focus-visible@1.34.1': + resolution: {integrity: sha512-Wvl/w6JpPU8DwEfq+I82yzlBX9mEp2o2k6DZc+3Kb3x/3oP8Jr0fCXdk+qWTvKekfVvDpM2vS1fjNNLfiOaQww==} - '@zag-js/interact-outside@1.34.0': - resolution: {integrity: sha512-U7E3k2pkF7xh9cxU3za1BaiAo33kCdW/iulicPutTVEgSa09bXARa2/v/ISrpAKj/Crau69UAfxjjl4D56fxnw==} + '@zag-js/interact-outside@1.34.1': + resolution: {integrity: sha512-wQdOYVhPFyYPJUDPYXQc1tg8vdJGTG5xwTEdOGVG1DpVo87VNsdb0KjnWPZsfPQEUSkvJIXtbjopp/OLw4fFow==} - '@zag-js/listbox@1.34.0': - resolution: {integrity: sha512-T1KHdYpaN/iIL2w14ySG4wLLti/az62CIe1Tnx9szuo7nC38GeeSsWtQJxc+DRKYTPNC9PDvbImKS6CsdcaaSg==} + '@zag-js/listbox@1.34.1': + resolution: {integrity: sha512-0KcGH8lILd1fLO49VCl3nyalesm+Nvr6/oH6qflsbn+frM4JaDcF/S6gngtKXJ8Mzx+5Qua97uAvuk047Z0I/A==} - '@zag-js/live-region@1.34.0': - resolution: {integrity: sha512-GTlVqRLJaZpqSg6j6XufxrD2z0EIaTp1tIddU4USceNb38SDb4JbFf/DB6IATRS506W27kfxQW1J4uau5bJLEg==} + '@zag-js/live-region@1.34.1': + resolution: {integrity: sha512-0Heu0z0cBQQDA1289nm0wQM3+ybEuOSqpPffHQdRgaTtUWnG0xkw0jt8qq0A/tq2VIlwLnbNledE4dh1Y6KwhA==} - '@zag-js/menu@1.34.0': - resolution: {integrity: sha512-qruSZvJuLVBi0Af8YkB/7+fRAmLuAu6MSMLuIW2WT4FlcsqLk9AEnYXD5X/shUKJmNiIvZisyaZlpzMxkNHChQ==} + '@zag-js/menu@1.34.1': + resolution: {integrity: sha512-nhVM6dNf48QqeY0qzn6n5hTk8qbBM7FwnGAUJmGRx9nrrG1SpIpQ5sCxDcOwgI/Q2vMu+2ggmIiObvZKAqZkiA==} - '@zag-js/number-input@1.34.0': - resolution: {integrity: sha512-xuq7skBhR4zH7bf9quuADOTQwl2FVdv/Wpsetj4epN+ZLJk31UwRYYYusfEhXZ3B9N9m7qdgADEF1zJLjwtm+g==} + '@zag-js/number-input@1.34.1': + resolution: {integrity: sha512-ZcqsC7XRzutc3nDV1AJO/kwb6ycAeDGTMOL3jTQND0xPZfUb5u/YZcMxinWHLwXZZ8ZuXZeWvtiy3veCUJGCFg==} - '@zag-js/password-input@1.34.0': - resolution: {integrity: sha512-OAkJNz443Pm90GVBomfDEhI20ADZEGv/23MprP3x1+sV/Ppn9HOTWUWSyghVQzIfocDK0X5zPygIE/XaJVaxBQ==} + '@zag-js/password-input@1.34.1': + resolution: {integrity: sha512-0gay9u9NqN8b4QuIpABlCtP93TbQJ8uRGurUSQwgbbG6llf+EOLiZs5f0H19MVnhbcINatv9p34Je6GAP2SBbA==} - '@zag-js/pin-input@1.34.0': - resolution: {integrity: sha512-PpH0MGW67tnNupApXv5a0LzzkYgzVxgomcirCrWjqPGMa+ZHBy6T0ZdHBVR/zBfOwtB/s/wiS883n18TIJ0Ekg==} + '@zag-js/pin-input@1.34.1': + resolution: {integrity: sha512-xmXdDA2JahFD+I+avDw62WX9/tKH6ab6rkn46ciqHbGTTgSW23s/btiPRuMshdd4lnqXgQgKBE/wkVdRZzeRnA==} - '@zag-js/popper@1.34.0': - resolution: {integrity: sha512-JCcQUfe8aSkE1Pgfes30xx5Fea2O3eIOhMKr7pMAFByYw9otLEimFaA3T8OU11O9gTgy5qlStAljbxpJYFB4pg==} + '@zag-js/popper@1.34.1': + resolution: {integrity: sha512-e9xklP5YjfqS6sTSzezsRBdaD5ilMTQoEedAQecy16jstIAQsberctUYZVg8vRQubA9ukmv1JvaNbkVFclr13w==} - '@zag-js/radio-group@1.34.0': - resolution: {integrity: sha512-xqUVmMrFgN/9/oBa/rvuTnKVc1KAH1jpHNsIYhMIAAbyR96Kl8LxRC0tq+gnwLFGijHnxjnTfqqHSwul+k3Suw==} + '@zag-js/radio-group@1.34.1': + resolution: {integrity: sha512-dRy7Z1zE2qwyyjI68icLBkPzM+ZzL0aAklFVc7kd/ejYxZfk6SrPoMjg3u22a34J2scxHwh/LdSuQFmauj4sAA==} - '@zag-js/rect-utils@1.34.0': - resolution: {integrity: sha512-KUJrU0xb1L53PFDDP0y/VuOzKDIFAFvfWru6AWIR1HaDDcX62K9s51mg9E27LgHSsXn1/yUTR2Nktb+wXy1dOg==} + '@zag-js/rect-utils@1.34.1': + resolution: {integrity: sha512-ms81ltfYBsabzk6tqy+kheMAUkx9IB+AUt8sATWGgqSTemBuMv/dptCJt3+WbkQjc8NveuEWmq3S6w1KJ31gaQ==} - '@zag-js/remove-scroll@1.34.0': - resolution: {integrity: sha512-StGyXyFdKXU5P86M7Jqiwh5dy9UwguXva2WuO+eM4vCfI/GvS76JoznjJqjF08uKBRelJdm+JDO0+KJj2OGqUA==} + '@zag-js/remove-scroll@1.34.1': + resolution: {integrity: sha512-irnvF4cwoAidwdU4UsPQyhTivnY3V/ZBO9EW/zTJ78qm3sjkRDDZsKEVV2UstUEmvH1jcFFPp6eNoTVItsyJBg==} - '@zag-js/scroll-snap@1.34.0': - resolution: {integrity: sha512-WJ6u75xIlQvtglLon4HADDBjbj9frQsmIAoeTkhDw7RaHNwT4Z0jqilPtkP87wBkSTNRc5/xuifgPSpm+81o3Q==} + '@zag-js/scroll-snap@1.34.1': + resolution: {integrity: sha512-pvgbJ3tjKRM1LlF14QY5riV/KT2EYGfYY4Cl5QuONqjO0ergrRd0FxDzhiwqIthgXrDZOavMucr7yqCLrjVQQA==} - '@zag-js/select@1.34.0': - resolution: {integrity: sha512-Cg3aMo4cMF2UWoP5oy4N/iwKR3vMbmrP12azlvQ0ZIjwGoDVBzUKbVvTo6W4Br5+PJWhzlYKphkf3RoCwpYB/w==} + '@zag-js/select@1.34.1': + resolution: {integrity: sha512-4Pc062McPgdRYwOU6v8jaqP48nBeTuSbuToHWixTVUhDkAsLDd7A8od2sKPsGHRsPOce0D259vJS5L3c0vtfig==} - '@zag-js/signature-pad@1.34.0': - resolution: {integrity: sha512-nyBOhncvaGa+FpAPM54q5oPnu0UNSK3EedIZdEAB1GZi0sI7wYmcKnKGxxN1QIwT4kWXbDWoA0y1ivHZFZFgdg==} + '@zag-js/signature-pad@1.34.1': + resolution: {integrity: sha512-yeYi4Tl+IgPfGA5ejbYSpHU52MBchR0S2aqT9X4kRJniCcnKLV3ZI5VOz8cYXCpYMDiWSsQnzcIIohHNHVhK2g==} - '@zag-js/store@1.34.0': - resolution: {integrity: sha512-ITHPt9mZT7OQUicVGVI3ajRa3/VZvXGiIPXo3YsQtajM62N3RvlbjyDRqfy0ApmW1/uQzO+ftj+v6BbDWBe2IQ==} + '@zag-js/store@1.34.1': + resolution: {integrity: sha512-127OWtrFKuS9HsbZITancVrydA9CpDHxyS5V+gDA7bnsNdw8kKx1H82oMh8Ok6hkDXvilhEB+g+UIOVeQD7RwQ==} - '@zag-js/switch@1.34.0': - resolution: {integrity: sha512-g9ZJDHDSD57VPYmNpARHhEmZPn5M3l+SCcHIT73IeiCR0EEmvKtNzd7YruAUGZBeFDch4g3lLAPqD4FPMgIEyA==} + '@zag-js/switch@1.34.1': + resolution: {integrity: sha512-yOibKwJ+/WZBxVBpiK7mFEFjz88rLCzAhYEr5Om52fsWPc7BbxZKBZw058e0yx5mEBXJUTXNebgp1fkmth7U9g==} - '@zag-js/tabs@1.34.0': - resolution: {integrity: sha512-UW4uTdutIl5tgkUOsmRH68K7zhar2MNg5usYGeykQu0Gk/GeEir+R1VvU6NFBu9Tc5nSq4VvEGytwjlNkNDU5A==} + '@zag-js/tabs@1.34.1': + resolution: {integrity: sha512-5qAz3jyawmfwpIjd5rNe10yae3YdZSaUu9bNk8ZzJXkXu+Sf69BO6M/k32+ZtAS1i+cku7ZXluza95CxA/Oshg==} - '@zag-js/timer@1.34.0': - resolution: {integrity: sha512-ppH1XRvp83y68ww1qLiKkzwUOgWpb5g47nHU3Q20zq3l/gfWIhjfkTcqp+ak/0C/Vk4ZvmP5Jhfn+oFFpAbTFg==} + '@zag-js/timer@1.34.1': + resolution: {integrity: sha512-awQXcKCHAKS+XBJCV+TpZhSI+zRmQCtYUqVjXMv6Mj1jmB7PS/I7gAt1D7wGmMKtkBZ4vbRxe2p2qO3StlpxRA==} - '@zag-js/toast@1.34.0': - resolution: {integrity: sha512-v9mWi5dBpUGXJLMvSVMOR/mmujENKhE2v2M6VJ/+0bilBYwcX3VW05OywSYaTlxi5vt9n2cOpvFDstMAnuRJHQ==} + '@zag-js/toast@1.34.1': + resolution: {integrity: sha512-Ad1QQ2aSbJUO+wDOLRAONrhhTsAm6YHwSxKnp4wMOvTw6UcBgRr2MeebrvYx/kwHa4UK/Y9L5rxoyRoTrk2mGg==} - '@zag-js/toggle-group@1.34.0': - resolution: {integrity: sha512-zXw9A5jTygdWgjsQi+gasvXaxekIbDTCbB0vrbLVZE/10aSO+v70INZmhlbffl6MjykqMD9HeV2eKvdpg0Uudw==} + '@zag-js/toggle-group@1.34.1': + resolution: {integrity: sha512-QDgABKDhz8EShqZoIK4NR0Ca2qDpOYAaClHlq4loyWqBhKSt1Xtok7GaD4iwer+U/5WpkzJqIlwV2nC09dsRpA==} - '@zag-js/tree-view@1.34.0': - resolution: {integrity: sha512-10WbUg/w9OlohHabJWqwFx3PSyqUdxyF7dHWJQKLxZo6J1YUrp1KXIC6+/NOSKtJ0F0olZbRJnrTAvgkejo97Q==} + '@zag-js/tree-view@1.34.1': + resolution: {integrity: sha512-ExoUFplWtTyVEGITgAFR2kIaJax/zznPuWVBiWL+Avf7qn0+CCs1b1JOPeGQtqUXk9Fso67uw9BwNQFOxbQelA==} - '@zag-js/types@1.34.0': - resolution: {integrity: sha512-0DRMFFfq77ofAQIXQrqieGAOvFezKx6VDrdgMsV7zhQRbTPBXxA7VjDdd0mSSk8sHFiA1AL7Jxzc5zFrQcOpow==} + '@zag-js/types@1.34.1': + resolution: {integrity: sha512-DAg2cD5g0PvYGIC08GWa71aSyO2IEGlQCIIdOtmUSN2PdLyRi3IllNHlnDYFCkTGbb0GUunCro8FLvE9Aj2BOA==} - '@zag-js/utils@1.34.0': - resolution: {integrity: sha512-rdk7T3M8Rj+Wew7jtQuaWzxT/htbk2PCoLuF2yj3ldef+4txVE2uVQWodeM5mp7vArvMJxXvipnJJtkTXEPNkw==} + '@zag-js/utils@1.34.1': + resolution: {integrity: sha512-XgNTbcxPgNkPEuH/s/WUSU3M1mnO7453P53Wvym3dte+zPmqjwf5VT1FmEw8t6YQDBiBkIEKpUnNwR2x/pmZgg==} - '@zag-js/vanilla@1.34.0': - resolution: {integrity: sha512-svVl2KrUNgB4O4l2LQu2f6crb+Qw5kfRL54WJUaVGeJZrrD3kjoXtcCMUP4nLNqX/YVnY4d2kPn/qskU+JUc1A==} + '@zag-js/vanilla@1.34.1': + resolution: {integrity: sha512-hMiIUhbAwB8TuRLGSd9vFeVjA13S5yqrDlgDbA3kOeDdqKilEhHaXuSFS1iid6QXUbGTPOlrdh0VLJRtA8OnVQ==} acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} @@ -983,327 +983,327 @@ snapshots: '@typescript-eslint/types': 8.54.0 eslint-visitor-keys: 4.2.1 - '@zag-js/accordion@1.34.0': + '@zag-js/accordion@1.34.1': dependencies: - '@zag-js/anatomy': 1.34.0 - '@zag-js/core': 1.34.0 - '@zag-js/dom-query': 1.34.0 - '@zag-js/types': 1.34.0 - '@zag-js/utils': 1.34.0 + '@zag-js/anatomy': 1.34.1 + '@zag-js/core': 1.34.1 + '@zag-js/dom-query': 1.34.1 + '@zag-js/types': 1.34.1 + '@zag-js/utils': 1.34.1 - '@zag-js/anatomy@1.34.0': {} + '@zag-js/anatomy@1.34.1': {} - '@zag-js/angle-slider@1.34.0': + '@zag-js/angle-slider@1.34.1': dependencies: - '@zag-js/anatomy': 1.34.0 - '@zag-js/core': 1.34.0 - '@zag-js/dom-query': 1.34.0 - '@zag-js/rect-utils': 1.34.0 - '@zag-js/types': 1.34.0 - '@zag-js/utils': 1.34.0 + '@zag-js/anatomy': 1.34.1 + '@zag-js/core': 1.34.1 + '@zag-js/dom-query': 1.34.1 + '@zag-js/rect-utils': 1.34.1 + '@zag-js/types': 1.34.1 + '@zag-js/utils': 1.34.1 - '@zag-js/aria-hidden@1.34.0': + '@zag-js/aria-hidden@1.34.1': dependencies: - '@zag-js/dom-query': 1.34.0 + '@zag-js/dom-query': 1.34.1 - '@zag-js/avatar@1.34.0': + '@zag-js/avatar@1.34.1': dependencies: - '@zag-js/anatomy': 1.34.0 - '@zag-js/core': 1.34.0 - '@zag-js/dom-query': 1.34.0 - '@zag-js/types': 1.34.0 - '@zag-js/utils': 1.34.0 + '@zag-js/anatomy': 1.34.1 + '@zag-js/core': 1.34.1 + '@zag-js/dom-query': 1.34.1 + '@zag-js/types': 1.34.1 + '@zag-js/utils': 1.34.1 - '@zag-js/carousel@1.34.0': + '@zag-js/carousel@1.34.1': dependencies: - '@zag-js/anatomy': 1.34.0 - '@zag-js/core': 1.34.0 - '@zag-js/dom-query': 1.34.0 - '@zag-js/scroll-snap': 1.34.0 - '@zag-js/types': 1.34.0 - '@zag-js/utils': 1.34.0 + '@zag-js/anatomy': 1.34.1 + '@zag-js/core': 1.34.1 + '@zag-js/dom-query': 1.34.1 + '@zag-js/scroll-snap': 1.34.1 + '@zag-js/types': 1.34.1 + '@zag-js/utils': 1.34.1 - '@zag-js/checkbox@1.34.0': + '@zag-js/checkbox@1.34.1': dependencies: - '@zag-js/anatomy': 1.34.0 - '@zag-js/core': 1.34.0 - '@zag-js/dom-query': 1.34.0 - '@zag-js/focus-visible': 1.34.0 - '@zag-js/types': 1.34.0 - '@zag-js/utils': 1.34.0 + '@zag-js/anatomy': 1.34.1 + '@zag-js/core': 1.34.1 + '@zag-js/dom-query': 1.34.1 + '@zag-js/focus-visible': 1.34.1 + '@zag-js/types': 1.34.1 + '@zag-js/utils': 1.34.1 - '@zag-js/clipboard@1.34.0': + '@zag-js/clipboard@1.34.1': dependencies: - '@zag-js/anatomy': 1.34.0 - '@zag-js/core': 1.34.0 - '@zag-js/dom-query': 1.34.0 - '@zag-js/types': 1.34.0 - '@zag-js/utils': 1.34.0 + '@zag-js/anatomy': 1.34.1 + '@zag-js/core': 1.34.1 + '@zag-js/dom-query': 1.34.1 + '@zag-js/types': 1.34.1 + '@zag-js/utils': 1.34.1 - '@zag-js/collapsible@1.34.0': + '@zag-js/collapsible@1.34.1': dependencies: - '@zag-js/anatomy': 1.34.0 - '@zag-js/core': 1.34.0 - '@zag-js/dom-query': 1.34.0 - '@zag-js/types': 1.34.0 - '@zag-js/utils': 1.34.0 + '@zag-js/anatomy': 1.34.1 + '@zag-js/core': 1.34.1 + '@zag-js/dom-query': 1.34.1 + '@zag-js/types': 1.34.1 + '@zag-js/utils': 1.34.1 - '@zag-js/collection@1.34.0': + '@zag-js/collection@1.34.1': dependencies: - '@zag-js/utils': 1.34.0 + '@zag-js/utils': 1.34.1 - '@zag-js/combobox@1.34.0': + '@zag-js/combobox@1.34.1': dependencies: - '@zag-js/anatomy': 1.34.0 - '@zag-js/aria-hidden': 1.34.0 - '@zag-js/collection': 1.34.0 - '@zag-js/core': 1.34.0 - '@zag-js/dismissable': 1.34.0 - '@zag-js/dom-query': 1.34.0 - '@zag-js/focus-visible': 1.34.0 - '@zag-js/popper': 1.34.0 - '@zag-js/types': 1.34.0 - '@zag-js/utils': 1.34.0 + '@zag-js/anatomy': 1.34.1 + '@zag-js/aria-hidden': 1.34.1 + '@zag-js/collection': 1.34.1 + '@zag-js/core': 1.34.1 + '@zag-js/dismissable': 1.34.1 + '@zag-js/dom-query': 1.34.1 + '@zag-js/focus-visible': 1.34.1 + '@zag-js/popper': 1.34.1 + '@zag-js/types': 1.34.1 + '@zag-js/utils': 1.34.1 - '@zag-js/core@1.34.0': + '@zag-js/core@1.34.1': dependencies: - '@zag-js/dom-query': 1.34.0 - '@zag-js/utils': 1.34.0 + '@zag-js/dom-query': 1.34.1 + '@zag-js/utils': 1.34.1 - '@zag-js/date-picker@1.34.0(@internationalized/date@3.11.0)': + '@zag-js/date-picker@1.34.1(@internationalized/date@3.11.0)': dependencies: '@internationalized/date': 3.11.0 - '@zag-js/anatomy': 1.34.0 - '@zag-js/core': 1.34.0 - '@zag-js/date-utils': 1.34.0(@internationalized/date@3.11.0) - '@zag-js/dismissable': 1.34.0 - '@zag-js/dom-query': 1.34.0 - '@zag-js/live-region': 1.34.0 - '@zag-js/popper': 1.34.0 - '@zag-js/types': 1.34.0 - '@zag-js/utils': 1.34.0 - - '@zag-js/date-utils@1.34.0(@internationalized/date@3.11.0)': + '@zag-js/anatomy': 1.34.1 + '@zag-js/core': 1.34.1 + '@zag-js/date-utils': 1.34.1(@internationalized/date@3.11.0) + '@zag-js/dismissable': 1.34.1 + '@zag-js/dom-query': 1.34.1 + '@zag-js/live-region': 1.34.1 + '@zag-js/popper': 1.34.1 + '@zag-js/types': 1.34.1 + '@zag-js/utils': 1.34.1 + + '@zag-js/date-utils@1.34.1(@internationalized/date@3.11.0)': dependencies: '@internationalized/date': 3.11.0 - '@zag-js/dialog@1.34.0': + '@zag-js/dialog@1.34.1': dependencies: - '@zag-js/anatomy': 1.34.0 - '@zag-js/aria-hidden': 1.34.0 - '@zag-js/core': 1.34.0 - '@zag-js/dismissable': 1.34.0 - '@zag-js/dom-query': 1.34.0 - '@zag-js/focus-trap': 1.34.0 - '@zag-js/remove-scroll': 1.34.0 - '@zag-js/types': 1.34.0 - '@zag-js/utils': 1.34.0 + '@zag-js/anatomy': 1.34.1 + '@zag-js/aria-hidden': 1.34.1 + '@zag-js/core': 1.34.1 + '@zag-js/dismissable': 1.34.1 + '@zag-js/dom-query': 1.34.1 + '@zag-js/focus-trap': 1.34.1 + '@zag-js/remove-scroll': 1.34.1 + '@zag-js/types': 1.34.1 + '@zag-js/utils': 1.34.1 - '@zag-js/dismissable@1.34.0': + '@zag-js/dismissable@1.34.1': dependencies: - '@zag-js/dom-query': 1.34.0 - '@zag-js/interact-outside': 1.34.0 - '@zag-js/utils': 1.34.0 + '@zag-js/dom-query': 1.34.1 + '@zag-js/interact-outside': 1.34.1 + '@zag-js/utils': 1.34.1 - '@zag-js/dom-query@1.34.0': + '@zag-js/dom-query@1.34.1': dependencies: - '@zag-js/types': 1.34.0 + '@zag-js/types': 1.34.1 - '@zag-js/editable@1.34.0': + '@zag-js/editable@1.34.1': dependencies: - '@zag-js/anatomy': 1.34.0 - '@zag-js/core': 1.34.0 - '@zag-js/dom-query': 1.34.0 - '@zag-js/interact-outside': 1.34.0 - '@zag-js/types': 1.34.0 - '@zag-js/utils': 1.34.0 + '@zag-js/anatomy': 1.34.1 + '@zag-js/core': 1.34.1 + '@zag-js/dom-query': 1.34.1 + '@zag-js/interact-outside': 1.34.1 + '@zag-js/types': 1.34.1 + '@zag-js/utils': 1.34.1 - '@zag-js/floating-panel@1.34.0': + '@zag-js/floating-panel@1.34.1': dependencies: - '@zag-js/anatomy': 1.34.0 - '@zag-js/core': 1.34.0 - '@zag-js/dom-query': 1.34.0 - '@zag-js/popper': 1.34.0 - '@zag-js/rect-utils': 1.34.0 - '@zag-js/store': 1.34.0 - '@zag-js/types': 1.34.0 - '@zag-js/utils': 1.34.0 + '@zag-js/anatomy': 1.34.1 + '@zag-js/core': 1.34.1 + '@zag-js/dom-query': 1.34.1 + '@zag-js/popper': 1.34.1 + '@zag-js/rect-utils': 1.34.1 + '@zag-js/store': 1.34.1 + '@zag-js/types': 1.34.1 + '@zag-js/utils': 1.34.1 - '@zag-js/focus-trap@1.34.0': + '@zag-js/focus-trap@1.34.1': dependencies: - '@zag-js/dom-query': 1.34.0 + '@zag-js/dom-query': 1.34.1 - '@zag-js/focus-visible@1.34.0': + '@zag-js/focus-visible@1.34.1': dependencies: - '@zag-js/dom-query': 1.34.0 + '@zag-js/dom-query': 1.34.1 - '@zag-js/interact-outside@1.34.0': + '@zag-js/interact-outside@1.34.1': dependencies: - '@zag-js/dom-query': 1.34.0 - '@zag-js/utils': 1.34.0 + '@zag-js/dom-query': 1.34.1 + '@zag-js/utils': 1.34.1 - '@zag-js/listbox@1.34.0': + '@zag-js/listbox@1.34.1': dependencies: - '@zag-js/anatomy': 1.34.0 - '@zag-js/collection': 1.34.0 - '@zag-js/core': 1.34.0 - '@zag-js/dom-query': 1.34.0 - '@zag-js/focus-visible': 1.34.0 - '@zag-js/types': 1.34.0 - '@zag-js/utils': 1.34.0 + '@zag-js/anatomy': 1.34.1 + '@zag-js/collection': 1.34.1 + '@zag-js/core': 1.34.1 + '@zag-js/dom-query': 1.34.1 + '@zag-js/focus-visible': 1.34.1 + '@zag-js/types': 1.34.1 + '@zag-js/utils': 1.34.1 - '@zag-js/live-region@1.34.0': {} + '@zag-js/live-region@1.34.1': {} - '@zag-js/menu@1.34.0': + '@zag-js/menu@1.34.1': dependencies: - '@zag-js/anatomy': 1.34.0 - '@zag-js/core': 1.34.0 - '@zag-js/dismissable': 1.34.0 - '@zag-js/dom-query': 1.34.0 - '@zag-js/focus-visible': 1.34.0 - '@zag-js/popper': 1.34.0 - '@zag-js/rect-utils': 1.34.0 - '@zag-js/types': 1.34.0 - '@zag-js/utils': 1.34.0 + '@zag-js/anatomy': 1.34.1 + '@zag-js/core': 1.34.1 + '@zag-js/dismissable': 1.34.1 + '@zag-js/dom-query': 1.34.1 + '@zag-js/focus-visible': 1.34.1 + '@zag-js/popper': 1.34.1 + '@zag-js/rect-utils': 1.34.1 + '@zag-js/types': 1.34.1 + '@zag-js/utils': 1.34.1 - '@zag-js/number-input@1.34.0': + '@zag-js/number-input@1.34.1': dependencies: '@internationalized/number': 3.6.5 - '@zag-js/anatomy': 1.34.0 - '@zag-js/core': 1.34.0 - '@zag-js/dom-query': 1.34.0 - '@zag-js/types': 1.34.0 - '@zag-js/utils': 1.34.0 + '@zag-js/anatomy': 1.34.1 + '@zag-js/core': 1.34.1 + '@zag-js/dom-query': 1.34.1 + '@zag-js/types': 1.34.1 + '@zag-js/utils': 1.34.1 - '@zag-js/password-input@1.34.0': + '@zag-js/password-input@1.34.1': dependencies: - '@zag-js/anatomy': 1.34.0 - '@zag-js/core': 1.34.0 - '@zag-js/dom-query': 1.34.0 - '@zag-js/types': 1.34.0 - '@zag-js/utils': 1.34.0 + '@zag-js/anatomy': 1.34.1 + '@zag-js/core': 1.34.1 + '@zag-js/dom-query': 1.34.1 + '@zag-js/types': 1.34.1 + '@zag-js/utils': 1.34.1 - '@zag-js/pin-input@1.34.0': + '@zag-js/pin-input@1.34.1': dependencies: - '@zag-js/anatomy': 1.34.0 - '@zag-js/core': 1.34.0 - '@zag-js/dom-query': 1.34.0 - '@zag-js/types': 1.34.0 - '@zag-js/utils': 1.34.0 + '@zag-js/anatomy': 1.34.1 + '@zag-js/core': 1.34.1 + '@zag-js/dom-query': 1.34.1 + '@zag-js/types': 1.34.1 + '@zag-js/utils': 1.34.1 - '@zag-js/popper@1.34.0': + '@zag-js/popper@1.34.1': dependencies: '@floating-ui/dom': 1.7.5 - '@zag-js/dom-query': 1.34.0 - '@zag-js/utils': 1.34.0 + '@zag-js/dom-query': 1.34.1 + '@zag-js/utils': 1.34.1 - '@zag-js/radio-group@1.34.0': + '@zag-js/radio-group@1.34.1': dependencies: - '@zag-js/anatomy': 1.34.0 - '@zag-js/core': 1.34.0 - '@zag-js/dom-query': 1.34.0 - '@zag-js/focus-visible': 1.34.0 - '@zag-js/types': 1.34.0 - '@zag-js/utils': 1.34.0 + '@zag-js/anatomy': 1.34.1 + '@zag-js/core': 1.34.1 + '@zag-js/dom-query': 1.34.1 + '@zag-js/focus-visible': 1.34.1 + '@zag-js/types': 1.34.1 + '@zag-js/utils': 1.34.1 - '@zag-js/rect-utils@1.34.0': {} + '@zag-js/rect-utils@1.34.1': {} - '@zag-js/remove-scroll@1.34.0': + '@zag-js/remove-scroll@1.34.1': dependencies: - '@zag-js/dom-query': 1.34.0 + '@zag-js/dom-query': 1.34.1 - '@zag-js/scroll-snap@1.34.0': + '@zag-js/scroll-snap@1.34.1': dependencies: - '@zag-js/dom-query': 1.34.0 + '@zag-js/dom-query': 1.34.1 - '@zag-js/select@1.34.0': + '@zag-js/select@1.34.1': dependencies: - '@zag-js/anatomy': 1.34.0 - '@zag-js/collection': 1.34.0 - '@zag-js/core': 1.34.0 - '@zag-js/dismissable': 1.34.0 - '@zag-js/dom-query': 1.34.0 - '@zag-js/focus-visible': 1.34.0 - '@zag-js/popper': 1.34.0 - '@zag-js/types': 1.34.0 - '@zag-js/utils': 1.34.0 + '@zag-js/anatomy': 1.34.1 + '@zag-js/collection': 1.34.1 + '@zag-js/core': 1.34.1 + '@zag-js/dismissable': 1.34.1 + '@zag-js/dom-query': 1.34.1 + '@zag-js/focus-visible': 1.34.1 + '@zag-js/popper': 1.34.1 + '@zag-js/types': 1.34.1 + '@zag-js/utils': 1.34.1 - '@zag-js/signature-pad@1.34.0': + '@zag-js/signature-pad@1.34.1': dependencies: - '@zag-js/anatomy': 1.34.0 - '@zag-js/core': 1.34.0 - '@zag-js/dom-query': 1.34.0 - '@zag-js/types': 1.34.0 - '@zag-js/utils': 1.34.0 + '@zag-js/anatomy': 1.34.1 + '@zag-js/core': 1.34.1 + '@zag-js/dom-query': 1.34.1 + '@zag-js/types': 1.34.1 + '@zag-js/utils': 1.34.1 perfect-freehand: 1.2.3 - '@zag-js/store@1.34.0': + '@zag-js/store@1.34.1': dependencies: proxy-compare: 3.0.1 - '@zag-js/switch@1.34.0': + '@zag-js/switch@1.34.1': dependencies: - '@zag-js/anatomy': 1.34.0 - '@zag-js/core': 1.34.0 - '@zag-js/dom-query': 1.34.0 - '@zag-js/focus-visible': 1.34.0 - '@zag-js/types': 1.34.0 - '@zag-js/utils': 1.34.0 + '@zag-js/anatomy': 1.34.1 + '@zag-js/core': 1.34.1 + '@zag-js/dom-query': 1.34.1 + '@zag-js/focus-visible': 1.34.1 + '@zag-js/types': 1.34.1 + '@zag-js/utils': 1.34.1 - '@zag-js/tabs@1.34.0': + '@zag-js/tabs@1.34.1': dependencies: - '@zag-js/anatomy': 1.34.0 - '@zag-js/core': 1.34.0 - '@zag-js/dom-query': 1.34.0 - '@zag-js/types': 1.34.0 - '@zag-js/utils': 1.34.0 + '@zag-js/anatomy': 1.34.1 + '@zag-js/core': 1.34.1 + '@zag-js/dom-query': 1.34.1 + '@zag-js/types': 1.34.1 + '@zag-js/utils': 1.34.1 - '@zag-js/timer@1.34.0': + '@zag-js/timer@1.34.1': dependencies: - '@zag-js/anatomy': 1.34.0 - '@zag-js/core': 1.34.0 - '@zag-js/dom-query': 1.34.0 - '@zag-js/types': 1.34.0 - '@zag-js/utils': 1.34.0 + '@zag-js/anatomy': 1.34.1 + '@zag-js/core': 1.34.1 + '@zag-js/dom-query': 1.34.1 + '@zag-js/types': 1.34.1 + '@zag-js/utils': 1.34.1 - '@zag-js/toast@1.34.0': + '@zag-js/toast@1.34.1': dependencies: - '@zag-js/anatomy': 1.34.0 - '@zag-js/core': 1.34.0 - '@zag-js/dismissable': 1.34.0 - '@zag-js/dom-query': 1.34.0 - '@zag-js/types': 1.34.0 - '@zag-js/utils': 1.34.0 + '@zag-js/anatomy': 1.34.1 + '@zag-js/core': 1.34.1 + '@zag-js/dismissable': 1.34.1 + '@zag-js/dom-query': 1.34.1 + '@zag-js/types': 1.34.1 + '@zag-js/utils': 1.34.1 - '@zag-js/toggle-group@1.34.0': + '@zag-js/toggle-group@1.34.1': dependencies: - '@zag-js/anatomy': 1.34.0 - '@zag-js/core': 1.34.0 - '@zag-js/dom-query': 1.34.0 - '@zag-js/types': 1.34.0 - '@zag-js/utils': 1.34.0 + '@zag-js/anatomy': 1.34.1 + '@zag-js/core': 1.34.1 + '@zag-js/dom-query': 1.34.1 + '@zag-js/types': 1.34.1 + '@zag-js/utils': 1.34.1 - '@zag-js/tree-view@1.34.0': + '@zag-js/tree-view@1.34.1': dependencies: - '@zag-js/anatomy': 1.34.0 - '@zag-js/collection': 1.34.0 - '@zag-js/core': 1.34.0 - '@zag-js/dom-query': 1.34.0 - '@zag-js/types': 1.34.0 - '@zag-js/utils': 1.34.0 + '@zag-js/anatomy': 1.34.1 + '@zag-js/collection': 1.34.1 + '@zag-js/core': 1.34.1 + '@zag-js/dom-query': 1.34.1 + '@zag-js/types': 1.34.1 + '@zag-js/utils': 1.34.1 - '@zag-js/types@1.34.0': + '@zag-js/types@1.34.1': dependencies: csstype: 3.2.3 - '@zag-js/utils@1.34.0': {} + '@zag-js/utils@1.34.1': {} - '@zag-js/vanilla@1.34.0': + '@zag-js/vanilla@1.34.1': dependencies: - '@zag-js/core': 1.34.0 - '@zag-js/store': 1.34.0 - '@zag-js/types': 1.34.0 - '@zag-js/utils': 1.34.0 + '@zag-js/core': 1.34.1 + '@zag-js/store': 1.34.1 + '@zag-js/types': 1.34.1 + '@zag-js/utils': 1.34.1 acorn-jsx@5.3.2(acorn@8.15.0): dependencies: diff --git a/priv/design/components/clipboard.css b/priv/design/components/clipboard.css index eba8c2f..0407fb4 100644 --- a/priv/design/components/clipboard.css +++ b/priv/design/components/clipboard.css @@ -61,3 +61,29 @@ display: inline-block !important; } } + +@utility clipboard--* { + [data-scope="clipboard"][data-part="root"] { + max-width: --value(--container-ui-*, [length]); + gap: --value(--spacing-ui-gap-*, [length]); + } + + [data-scope="clipboard"][data-part="control"] { + gap: --value(--spacing-ui-gap-*, [length]); + } + + [data-scope="clipboard"][data-part="trigger"] { + font-size: --value(--text-ui-*, [length]); + line-height: --value(--text-ui-* --line-height, [length]); + min-height: --value(--spacing-ui-*, [length]); + } + + [data-scope="clipboard"][data-part="label"] { + font-size: --value(--text-ui-*, [length]); + line-height: --value(--text-ui-* --line-height, [length]); + } + + [data-scope="clipboard"][data-part="input"] { + max-width: --value(--container-ui-*, [length]); + } +} diff --git a/priv/design/components/code.css b/priv/design/components/code.css new file mode 100644 index 0000000..f6dec12 --- /dev/null +++ b/priv/design/components/code.css @@ -0,0 +1,126 @@ +@import "../main.css"; + +@layer components { + .code[data-scope="code"][data-part="root"] { + @apply ui-root; + } + + .code [data-scope="code"][data-part="content"] .highlight { + @apply ui-content scrollbar scrollbar--sm; + font-family: var(--font-code); + font-size: var(--text-ui-sm); + line-height: var(--text-ui-sm--line-height); + padding: var(--spacing-ui-padding); + background-color: var(--color-ui); + color: var(--color-ui--text); + border: 1px solid var(--color-ui--border); + overflow: auto; + background-image: linear-gradient( + var(--color-root) 50%, + var(--color-layer) 50% + ); + background-size: 100% calc(var(--text-ui) * var(--text-ui--line-height)); + background-origin: content-box; + background-attachment: local; + background-position: 0 calc(var(--spacing-ui-padding) / 2);; + + + & code { + width: fit-content; + padding-inline-end: var(--spacing-ui); + } + } + + + .code .highlight .unselectable { + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + } + + + [data-mode="light"] .code .highlight .hll { background-color: #fff9c4; } + [data-mode="light"] .code .highlight .bp { color: #0070c1; } + [data-mode="light"] .code .highlight .c, [data-mode="light"] .code .highlight .c1, [data-mode="light"] .code .highlight .ch, [data-mode="light"] .code .highlight .cm, [data-mode="light"] .code .highlight .cs { color: #008000; font-style: italic; } + [data-mode="light"] .code .highlight .cp, [data-mode="light"] .code .highlight .cpf { color: #0000ff; font-style: normal; } + [data-mode="light"] .code .highlight .dl { color: #a31515; } + [data-mode="light"] .code .highlight .err { border-color: #cd3131; color: #cd3131; } + [data-mode="light"] .code .highlight .fm { color: #795e26; } + [data-mode="light"] .code .highlight .gd { color: #a31515; } + [data-mode="light"] .code .highlight .ge { font-style: italic; } + [data-mode="light"] .code .highlight .gh { color: #000080; font-weight: bold; } + [data-mode="light"] .code .highlight .gi { color: #098658; } + [data-mode="light"] .code .highlight .go { color: #6e7681; } + [data-mode="light"] .code .highlight .gp { color: #000080; font-weight: bold; } + [data-mode="light"] .code .highlight .gr { color: #cd3131; } + [data-mode="light"] .code .highlight .gs { font-weight: bold; } + [data-mode="light"] .code .highlight .gt { color: #0451a5; } + [data-mode="light"] .code .highlight .gu { color: #800080; font-weight: bold; } + [data-mode="light"] .code .highlight .il { color: #098658; } + [data-mode="light"] .code .highlight .k, [data-mode="light"] .code .highlight .kd, [data-mode="light"] .code .highlight .kr, [data-mode="light"] .code .highlight .kc, [data-mode="light"] .code .highlight .kn, [data-mode="light"] .code .highlight .kp { color: #0000ff; font-weight: bold; } + [data-mode="light"] .code .highlight .kt { color: #267f99; } + [data-mode="light"] .code .highlight .m, [data-mode="light"] .code .highlight .mb, [data-mode="light"] .code .highlight .mf, [data-mode="light"] .code .highlight .mh, [data-mode="light"] .code .highlight .mi, [data-mode="light"] .code .highlight .mo { color: #098658; } + [data-mode="light"] .code .highlight .na { color: #e50000; } + [data-mode="light"] .code .highlight .nb { color: #267f99; } + [data-mode="light"] .code .highlight .nc, [data-mode="light"] .code .highlight .nn { color: #267f99; font-weight: bold; } + [data-mode="light"] .code .highlight .nd { color: #af00db; } + [data-mode="light"] .code .highlight .ne { color: #cd3131; font-weight: bold; } + [data-mode="light"] .code .highlight .nf { color: #795e26; } + [data-mode="light"] .code .highlight .ni { color: #6e7681; font-weight: bold; } + [data-mode="light"] .code .highlight .nl { color: #000000; } + [data-mode="light"] .code .highlight .no { color: #0070c1; } + [data-mode="light"] .code .highlight .nt { color: #800000; font-weight: bold; } + [data-mode="light"] .code .highlight .nv, [data-mode="light"] .code .highlight .vc, [data-mode="light"] .code .highlight .vg, [data-mode="light"] .code .highlight .vi, [data-mode="light"] .code .highlight .vm { color: #001080; } + [data-mode="light"] .code .highlight .o { color: #000000; } + [data-mode="light"] .code .highlight .ow { color: #0000ff; font-weight: bold; } + [data-mode="light"] .code .highlight .s, [data-mode="light"] .code .highlight .s1, [data-mode="light"] .code .highlight .s2, [data-mode="light"] .code .highlight .sa, [data-mode="light"] .code .highlight .sb, [data-mode="light"] .code .highlight .sc, [data-mode="light"] .code .highlight .sh, [data-mode="light"] .code .highlight .sx { color: #a31515; } + [data-mode="light"] .code .highlight .sd { color: #a31515; font-style: italic; } + [data-mode="light"] .code .highlight .se { color: #ee0000; font-weight: bold; } + [data-mode="light"] .code .highlight .si { color: #811f3f; font-weight: bold; } + [data-mode="light"] .code .highlight .sr { color: #811f3f; } + [data-mode="light"] .code .highlight .ss { color: #001080; } + + [data-mode="dark"] .code .highlight .hll { background-color: #264f78; } + [data-mode="dark"] .code .highlight .bp { color: #4ec9b0; } + [data-mode="dark"] .code .highlight .c, [data-mode="dark"] .code .highlight .c1, [data-mode="dark"] .code .highlight .ch, [data-mode="dark"] .code .highlight .cm, [data-mode="dark"] .code .highlight .cs { color: #6a9955; font-style: italic; } + [data-mode="dark"] .code .highlight .cp, [data-mode="dark"] .code .highlight .cpf { color: #569cd6; font-style: normal; } + [data-mode="dark"] .code .highlight .dl { color: #ce9178; } + [data-mode="dark"] .code .highlight .err { border-color: #f44747; color: #f44747; } + [data-mode="dark"] .code .highlight .fm { color: #dcdcaa; } + [data-mode="dark"] .code .highlight .gd { color: #ce9178; } + [data-mode="dark"] .code .highlight .ge { font-style: italic; } + [data-mode="dark"] .code .highlight .gh { color: #569cd6; font-weight: bold; } + [data-mode="dark"] .code .highlight .gi { color: #b5cea8; } + [data-mode="dark"] .code .highlight .go { color: #808080; } + [data-mode="dark"] .code .highlight .gp { color: #569cd6; font-weight: bold; } + [data-mode="dark"] .code .highlight .gr { color: #f44747; } + [data-mode="dark"] .code .highlight .gs { font-weight: bold; } + [data-mode="dark"] .code .highlight .gt { color: #4ec9b0; } + [data-mode="dark"] .code .highlight .gu { color: #c586c0; font-weight: bold; } + [data-mode="dark"] .code .highlight .il { color: #b5cea8; } + [data-mode="dark"] .code .highlight .k, [data-mode="dark"] .code .highlight .kd, [data-mode="dark"] .code .highlight .kr, [data-mode="dark"] .code .highlight .kc, [data-mode="dark"] .code .highlight .kn, [data-mode="dark"] .code .highlight .kp { color: #569cd6; font-weight: bold; } + [data-mode="dark"] .code .highlight .kt { color: #4ec9b0; } + [data-mode="dark"] .code .highlight .m, [data-mode="dark"] .code .highlight .mb, [data-mode="dark"] .code .highlight .mf, [data-mode="dark"] .code .highlight .mh, [data-mode="dark"] .code .highlight .mi, [data-mode="dark"] .code .highlight .mo { color: #b5cea8; } + [data-mode="dark"] .code .highlight .na { color: #9cdcfe; } + [data-mode="dark"] .code .highlight .nb { color: #4ec9b0; } + [data-mode="dark"] .code .highlight .nc, [data-mode="dark"] .code .highlight .nn { color: #4ec9b0; font-weight: bold; } + [data-mode="dark"] .code .highlight .nd { color: #c586c0; } + [data-mode="dark"] .code .highlight .ne { color: #f44747; font-weight: bold; } + [data-mode="dark"] .code .highlight .nf { color: #dcdcaa; } + [data-mode="dark"] .code .highlight .ni { color: #808080; font-weight: bold; } + [data-mode="dark"] .code .highlight .nl { color: #c8c8c8; } + [data-mode="dark"] .code .highlight .no { color: #4fc1ff; } + [data-mode="dark"] .code .highlight .nt { color: #569cd6; font-weight: bold; } + [data-mode="dark"] .code .highlight .nv, [data-mode="dark"] .code .highlight .vc, [data-mode="dark"] .code .highlight .vg, [data-mode="dark"] .code .highlight .vi, [data-mode="dark"] .code .highlight .vm { color: #9cdcfe; } + [data-mode="dark"] .code .highlight .o { color: #d4d4d4; } + [data-mode="dark"] .code .highlight .ow { color: #569cd6; font-weight: bold; } + [data-mode="dark"] .code .highlight .s, [data-mode="dark"] .code .highlight .s1, [data-mode="dark"] .code .highlight .s2, [data-mode="dark"] .code .highlight .sa, [data-mode="dark"] .code .highlight .sb, [data-mode="dark"] .code .highlight .sc, [data-mode="dark"] .code .highlight .sh, [data-mode="dark"] .code .highlight .sx { color: #ce9178; } + [data-mode="dark"] .code .highlight .sd { color: #ce9178; font-style: italic; } + [data-mode="dark"] .code .highlight .se { color: #d7ba7d; font-weight: bold; } + [data-mode="dark"] .code .highlight .si { color: #d7ba7d; font-weight: bold; } + [data-mode="dark"] .code .highlight .sr { color: #d16969; } + [data-mode="dark"] .code .highlight .ss { color: #9cdcfe; } +} diff --git a/priv/design/components/typo.css b/priv/design/components/typo.css index 2729c4b..947ee9d 100644 --- a/priv/design/components/typo.css +++ b/priv/design/components/typo.css @@ -285,17 +285,4 @@ flex-direction: column; gap: var(--spacing-ui-gap-lg); } - - .typo pre { - @apply scrollbar scrollbar--sm; - - font-family: var(--font-code); - font-size: var(--text-ui-sm); - line-height: var(--text-ui-sm--line-height); - padding: var(--spacing-ui-padding-sm); - background-color: var(--color-ui); - color: var(--color-ui--text); - border: 1px solid var(--color-ui--border); - overflow: auto; - } } diff --git a/priv/static/accordion.mjs b/priv/static/accordion.mjs index 2c6373d..939c673 100644 --- a/priv/static/accordion.mjs +++ b/priv/static/accordion.mjs @@ -22,9 +22,9 @@ import { queryAll, remove, warn -} from "./chunk-TZXIWZZ7.mjs"; +} from "./chunk-RUWIVFVB.mjs"; -// ../node_modules/.pnpm/@zag-js+accordion@1.34.0/node_modules/@zag-js/accordion/dist/index.mjs +// ../node_modules/.pnpm/@zag-js+accordion@1.34.1/node_modules/@zag-js/accordion/dist/index.mjs var anatomy = createAnatomy("accordion").parts("root", "item", "itemTrigger", "itemContent", "itemIndicator"); var parts = anatomy.build(); var getRootId = (ctx) => ctx.ids?.root ?? `accordion:${ctx.id}`; diff --git a/priv/static/angle-slider.mjs b/priv/static/angle-slider.mjs index 5a38f0b..7bcb190 100644 --- a/priv/static/angle-slider.mjs +++ b/priv/static/angle-slider.mjs @@ -1,7 +1,7 @@ import { createRect, getPointAngle -} from "./chunk-FEZIYMNT.mjs"; +} from "./chunk-QHOSSHQC.mjs"; import { Component, VanillaMachine, @@ -11,6 +11,7 @@ import { createSplitProps, dataAttr, getBoolean, + getEventKey, getEventPoint, getEventStep, getNativeEvent, @@ -22,9 +23,9 @@ import { setElementValue, snapValueToStep, trackPointerMove -} from "./chunk-TZXIWZZ7.mjs"; +} from "./chunk-RUWIVFVB.mjs"; -// ../node_modules/.pnpm/@zag-js+angle-slider@1.34.0/node_modules/@zag-js/angle-slider/dist/index.mjs +// ../node_modules/.pnpm/@zag-js+angle-slider@1.34.1/node_modules/@zag-js/angle-slider/dist/index.mjs var anatomy = createAnatomy("angle-slider").parts( "root", "label", @@ -47,14 +48,31 @@ var getControlEl = (ctx) => ctx.getById(getControlId(ctx)); var getThumbEl = (ctx) => ctx.getById(getThumbId(ctx)); var MIN_VALUE = 0; var MAX_VALUE = 359; -function getAngle(controlEl, point, angularOffset) { +function mirrorAngle(angle) { + return (360 - angle) % 360; +} +function getAngle(controlEl, point, angularOffset, dir) { const rect = createRect(controlEl.getBoundingClientRect()); - const angle = getPointAngle(rect, point); + let angle = getPointAngle(rect, point); if (angularOffset != null) { return angle - angularOffset; } + if (dir === "rtl") { + angle = mirrorAngle(angle); + } return angle; } +function getPointerValue(controlEl, point, angularOffset, value, dir) { + if (angularOffset == null) { + return getAngle(controlEl, point, null, dir); + } + const angle = getAngle(controlEl, point); + const clickAngle = value + angularOffset; + return dir === "rtl" ? value + clickAngle - angle : angle - angularOffset; +} +function getDisplayAngle(value, dir) { + return dir === "rtl" ? mirrorAngle(value) : value; +} function clampAngle(degree) { return Math.min(Math.max(degree, MIN_VALUE), MAX_VALUE); } @@ -72,6 +90,8 @@ function connect(service, normalize) { const dragging = state.matches("dragging"); const value = context.get("value"); const valueAsDegree = computed("valueAsDegree"); + const dir = prop("dir"); + const displayAngle = getDisplayAngle(value, dir); const disabled = prop("disabled"); const invalid = prop("invalid"); const readOnly = prop("readOnly"); @@ -89,12 +109,13 @@ function connect(service, normalize) { return normalize.element({ ...parts.root.attrs, id: getRootId(scope), + dir: prop("dir"), "data-disabled": dataAttr(disabled), "data-invalid": dataAttr(invalid), "data-readonly": dataAttr(readOnly), style: { "--value": value, - "--angle": valueAsDegree + "--angle": `${displayAngle}deg` } }); }, @@ -103,6 +124,7 @@ function connect(service, normalize) { ...parts.label.attrs, id: getLabelId(scope), htmlFor: getHiddenInputId(scope), + dir: prop("dir"), "data-disabled": dataAttr(disabled), "data-invalid": dataAttr(invalid), "data-readonly": dataAttr(readOnly), @@ -118,7 +140,8 @@ function connect(service, normalize) { type: "hidden", value, name: prop("name"), - id: getHiddenInputId(scope) + id: getHiddenInputId(scope), + dir: prop("dir") }); }, getControlProps() { @@ -126,6 +149,7 @@ function connect(service, normalize) { ...parts.control.attrs, role: "presentation", id: getControlId(scope), + dir: prop("dir"), "data-disabled": dataAttr(disabled), "data-invalid": dataAttr(invalid), "data-readonly": dataAttr(readOnly), @@ -157,6 +181,7 @@ function connect(service, normalize) { ...parts.thumb.attrs, id: getThumbId(scope), role: "slider", + dir: prop("dir"), "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy ?? getLabelId(scope), "aria-valuemax": 360, @@ -175,25 +200,34 @@ function connect(service, normalize) { onKeyDown(event) { if (!interactive) return; const step = getEventStep(event) * prop("step"); - switch (event.key) { - case "ArrowLeft": - case "ArrowUp": - event.preventDefault(); + const keyMap = { + ArrowLeft() { + send({ type: "THUMB.ARROW_DEC", step }); + }, + ArrowUp() { send({ type: "THUMB.ARROW_DEC", step }); - break; - case "ArrowRight": - case "ArrowDown": - event.preventDefault(); + }, + ArrowRight() { send({ type: "THUMB.ARROW_INC", step }); - break; - case "Home": - event.preventDefault(); + }, + ArrowDown() { + send({ type: "THUMB.ARROW_INC", step }); + }, + Home() { send({ type: "THUMB.HOME" }); - break; - case "End": - event.preventDefault(); + }, + End() { send({ type: "THUMB.END" }); - break; + } + }; + const key = getEventKey(event, { + dir: prop("dir"), + orientation: "horizontal" + }); + const exec = keyMap[key]; + if (exec) { + exec(event); + event.preventDefault(); } }, style: { @@ -204,12 +238,14 @@ function connect(service, normalize) { getValueTextProps() { return normalize.element({ ...parts.valueText.attrs, - id: getValueTextId(scope) + id: getValueTextId(scope), + dir: prop("dir") }); }, getMarkerGroupProps() { return normalize.element({ - ...parts.markerGroup.attrs + ...parts.markerGroup.attrs, + dir: prop("dir") }); }, getMarkerProps(props2) { @@ -221,14 +257,17 @@ function connect(service, normalize) { } else { markerState = "at-value"; } + const markerDisplayAngle = getDisplayAngle(props2.value, dir); return normalize.element({ ...parts.marker.attrs, + dir: prop("dir"), "data-value": props2.value, "data-state": markerState, "data-disabled": dataAttr(disabled), style: { "--marker-value": props2.value, - rotate: `calc(var(--marker-value) * 1deg)` + "--marker-display-value": markerDisplayAngle, + rotate: `calc(var(--marker-display-value) * 1deg)` } }); } @@ -352,7 +391,8 @@ var machine = createMachine({ const controlEl = getControlEl(scope); if (!controlEl) return; const angularOffset = refs.get("thumbDragOffset"); - const deg = getAngle(controlEl, event.point, angularOffset); + const value = context.get("value"); + const deg = getPointerValue(controlEl, event.point, angularOffset, value, prop("dir")); context.set("value", constrainAngle(deg, prop("step"))); }, setValueToMin({ context }) { diff --git a/priv/static/avatar.mjs b/priv/static/avatar.mjs index f90feee..68a9bf9 100644 --- a/priv/static/avatar.mjs +++ b/priv/static/avatar.mjs @@ -9,9 +9,9 @@ import { normalizeProps, observeAttributes, observeChildren -} from "./chunk-TZXIWZZ7.mjs"; +} from "./chunk-RUWIVFVB.mjs"; -// ../node_modules/.pnpm/@zag-js+avatar@1.34.0/node_modules/@zag-js/avatar/dist/index.mjs +// ../node_modules/.pnpm/@zag-js+avatar@1.34.1/node_modules/@zag-js/avatar/dist/index.mjs var anatomy = createAnatomy("avatar").parts("root", "image", "fallback"); var parts = anatomy.build(); var getRootId = (ctx) => ctx.ids?.root ?? `avatar:${ctx.id}`; diff --git a/priv/static/carousel.mjs b/priv/static/carousel.mjs index 3a8e2b9..940ea44 100644 --- a/priv/static/carousel.mjs +++ b/priv/static/carousel.mjs @@ -34,9 +34,9 @@ import { throttle, trackPointerMove, uniq -} from "./chunk-TZXIWZZ7.mjs"; +} from "./chunk-RUWIVFVB.mjs"; -// ../node_modules/.pnpm/@zag-js+scroll-snap@1.34.0/node_modules/@zag-js/scroll-snap/dist/index.mjs +// ../node_modules/.pnpm/@zag-js+scroll-snap@1.34.1/node_modules/@zag-js/scroll-snap/dist/index.mjs var getDirection = (element) => getComputedStyle2(element).direction; function getScrollPadding(element) { const style = getComputedStyle2(element); @@ -198,7 +198,7 @@ function findSnapPoint(parent, axis, predicate) { var uniq2 = (arr) => [...new Set(arr)]; var clamp = (min, max) => (value) => Math.max(min, Math.min(max, value)); -// ../node_modules/.pnpm/@zag-js+carousel@1.34.0/node_modules/@zag-js/carousel/dist/index.mjs +// ../node_modules/.pnpm/@zag-js+carousel@1.34.1/node_modules/@zag-js/carousel/dist/index.mjs var anatomy = createAnatomy("carousel").parts( "root", "itemGroup", diff --git a/priv/static/checkbox.mjs b/priv/static/checkbox.mjs index 90a7341..015541c 100644 --- a/priv/static/checkbox.mjs +++ b/priv/static/checkbox.mjs @@ -1,7 +1,7 @@ import { isFocusVisible, trackFocusVisible -} from "./chunk-GXGJDSCU.mjs"; +} from "./chunk-2QLEEEGG.mjs"; import { Component, VanillaMachine, @@ -21,9 +21,9 @@ import { trackFormControl, trackPress, visuallyHiddenStyle -} from "./chunk-TZXIWZZ7.mjs"; +} from "./chunk-RUWIVFVB.mjs"; -// ../node_modules/.pnpm/@zag-js+checkbox@1.34.0/node_modules/@zag-js/checkbox/dist/index.mjs +// ../node_modules/.pnpm/@zag-js+checkbox@1.34.1/node_modules/@zag-js/checkbox/dist/index.mjs var anatomy = createAnatomy("checkbox").parts("root", "label", "control", "indicator"); var parts = anatomy.build(); var getRootId = (ctx) => ctx.ids?.root ?? `checkbox:${ctx.id}`; diff --git a/priv/static/chunk-2QLEEEGG.mjs b/priv/static/chunk-2QLEEEGG.mjs new file mode 100644 index 0000000..e99849d --- /dev/null +++ b/priv/static/chunk-2QLEEEGG.mjs @@ -0,0 +1,174 @@ +import { + getActiveElement, + getDocument, + getEventTarget, + getWindow, + isMac, + isVirtualClick +} from "./chunk-RUWIVFVB.mjs"; + +// ../node_modules/.pnpm/@zag-js+focus-visible@1.34.1/node_modules/@zag-js/focus-visible/dist/index.mjs +function isValidKey(e) { + return !(e.metaKey || !isMac() && e.altKey || e.ctrlKey || e.key === "Control" || e.key === "Shift" || e.key === "Meta"); +} +var nonTextInputTypes = /* @__PURE__ */ new Set(["checkbox", "radio", "range", "color", "file", "image", "button", "submit", "reset"]); +function isKeyboardFocusEvent(isTextInput, modality, e) { + const eventTarget = e ? getEventTarget(e) : null; + const doc = getDocument(eventTarget); + const win = getWindow(eventTarget); + const activeElement = getActiveElement(doc); + isTextInput = isTextInput || activeElement instanceof win.HTMLInputElement && !nonTextInputTypes.has(activeElement?.type) || activeElement instanceof win.HTMLTextAreaElement || activeElement instanceof win.HTMLElement && activeElement.isContentEditable; + return !(isTextInput && modality === "keyboard" && e instanceof win.KeyboardEvent && !Reflect.has(FOCUS_VISIBLE_INPUT_KEYS, e.key)); +} +var currentModality = null; +var changeHandlers = /* @__PURE__ */ new Set(); +var listenerMap = /* @__PURE__ */ new Map(); +var hasEventBeforeFocus = false; +var hasBlurredWindowRecently = false; +var ignoreFocusEvent = false; +var FOCUS_VISIBLE_INPUT_KEYS = { + Tab: true, + Escape: true +}; +function triggerChangeHandlers(modality, e) { + for (let handler of changeHandlers) { + handler(modality, e); + } +} +function handleKeyboardEvent(e) { + hasEventBeforeFocus = true; + if (isValidKey(e)) { + currentModality = "keyboard"; + triggerChangeHandlers("keyboard", e); + } +} +function handlePointerEvent(e) { + currentModality = "pointer"; + if (e.type === "mousedown" || e.type === "pointerdown") { + hasEventBeforeFocus = true; + triggerChangeHandlers("pointer", e); + } +} +function handleClickEvent(e) { + if (isVirtualClick(e)) { + hasEventBeforeFocus = true; + currentModality = "virtual"; + } +} +function handleFocusEvent(e) { + const target = getEventTarget(e); + if (target === getWindow(target) || target === getDocument(target) || ignoreFocusEvent || !e.isTrusted) { + return; + } + if (!hasEventBeforeFocus && !hasBlurredWindowRecently) { + currentModality = "virtual"; + triggerChangeHandlers("virtual", e); + } + hasEventBeforeFocus = false; + hasBlurredWindowRecently = false; +} +function handleWindowBlur() { + hasEventBeforeFocus = false; + hasBlurredWindowRecently = true; +} +function setupGlobalFocusEvents(root) { + if (typeof window === "undefined" || listenerMap.get(getWindow(root))) { + return; + } + const win = getWindow(root); + const doc = getDocument(root); + let focus = win.HTMLElement.prototype.focus; + function patchedFocus() { + hasEventBeforeFocus = true; + focus.apply(this, arguments); + } + try { + Object.defineProperty(win.HTMLElement.prototype, "focus", { + configurable: true, + value: patchedFocus + }); + } catch { + } + doc.addEventListener("keydown", handleKeyboardEvent, true); + doc.addEventListener("keyup", handleKeyboardEvent, true); + doc.addEventListener("click", handleClickEvent, true); + win.addEventListener("focus", handleFocusEvent, true); + win.addEventListener("blur", handleWindowBlur, false); + if (typeof win.PointerEvent !== "undefined") { + doc.addEventListener("pointerdown", handlePointerEvent, true); + doc.addEventListener("pointermove", handlePointerEvent, true); + doc.addEventListener("pointerup", handlePointerEvent, true); + } else { + doc.addEventListener("mousedown", handlePointerEvent, true); + doc.addEventListener("mousemove", handlePointerEvent, true); + doc.addEventListener("mouseup", handlePointerEvent, true); + } + win.addEventListener( + "beforeunload", + () => { + tearDownWindowFocusTracking(root); + }, + { once: true } + ); + listenerMap.set(win, { focus }); +} +var tearDownWindowFocusTracking = (root, loadListener) => { + const win = getWindow(root); + const doc = getDocument(root); + const listenerData = listenerMap.get(win); + if (!listenerData) { + return; + } + try { + Object.defineProperty(win.HTMLElement.prototype, "focus", { + configurable: true, + value: listenerData.focus + }); + } catch { + } + doc.removeEventListener("keydown", handleKeyboardEvent, true); + doc.removeEventListener("keyup", handleKeyboardEvent, true); + doc.removeEventListener("click", handleClickEvent, true); + win.removeEventListener("focus", handleFocusEvent, true); + win.removeEventListener("blur", handleWindowBlur, false); + if (typeof win.PointerEvent !== "undefined") { + doc.removeEventListener("pointerdown", handlePointerEvent, true); + doc.removeEventListener("pointermove", handlePointerEvent, true); + doc.removeEventListener("pointerup", handlePointerEvent, true); + } else { + doc.removeEventListener("mousedown", handlePointerEvent, true); + doc.removeEventListener("mousemove", handlePointerEvent, true); + doc.removeEventListener("mouseup", handlePointerEvent, true); + } + listenerMap.delete(win); +}; +function getInteractionModality() { + return currentModality; +} +function setInteractionModality(modality) { + currentModality = modality; + triggerChangeHandlers(modality, null); +} +function isFocusVisible() { + return currentModality === "keyboard" || currentModality === "virtual"; +} +function trackFocusVisible(props = {}) { + const { isTextInput, autoFocus, onChange, root } = props; + setupGlobalFocusEvents(root); + onChange?.({ isFocusVisible: autoFocus || isFocusVisible(), modality: currentModality }); + const handler = (modality, e) => { + if (!isKeyboardFocusEvent(!!isTextInput, modality, e)) return; + onChange?.({ isFocusVisible: isFocusVisible(), modality }); + }; + changeHandlers.add(handler); + return () => { + changeHandlers.delete(handler); + }; +} + +export { + getInteractionModality, + setInteractionModality, + isFocusVisible, + trackFocusVisible +}; diff --git a/priv/static/chunk-EBQGC3XC.mjs b/priv/static/chunk-EBQGC3XC.mjs new file mode 100644 index 0000000..ee7255b --- /dev/null +++ b/priv/static/chunk-EBQGC3XC.mjs @@ -0,0 +1,237 @@ +import { + addDomEvent, + callAll, + contains, + getDocument, + getEventTarget, + getNearestOverflowAncestor, + getWindow, + isContextMenuEvent, + isControlledElement, + isFocusable, + isHTMLElement, + isShadowRoot, + isTouchDevice, + raf +} from "./chunk-RUWIVFVB.mjs"; + +// ../node_modules/.pnpm/@zag-js+interact-outside@1.34.1/node_modules/@zag-js/interact-outside/dist/index.mjs +function getWindowFrames(win) { + const frames = { + each(cb) { + for (let i = 0; i < win.frames?.length; i += 1) { + const frame = win.frames[i]; + if (frame) cb(frame); + } + }, + addEventListener(event, listener, options) { + frames.each((frame) => { + try { + frame.document.addEventListener(event, listener, options); + } catch { + } + }); + return () => { + try { + frames.removeEventListener(event, listener, options); + } catch { + } + }; + }, + removeEventListener(event, listener, options) { + frames.each((frame) => { + try { + frame.document.removeEventListener(event, listener, options); + } catch { + } + }); + } + }; + return frames; +} +function getParentWindow(win) { + const parent = win.frameElement != null ? win.parent : null; + return { + addEventListener: (event, listener, options) => { + try { + parent?.addEventListener(event, listener, options); + } catch { + } + return () => { + try { + parent?.removeEventListener(event, listener, options); + } catch { + } + }; + }, + removeEventListener: (event, listener, options) => { + try { + parent?.removeEventListener(event, listener, options); + } catch { + } + } + }; +} +var POINTER_OUTSIDE_EVENT = "pointerdown.outside"; +var FOCUS_OUTSIDE_EVENT = "focus.outside"; +function isComposedPathFocusable(composedPath) { + for (const node of composedPath) { + if (isHTMLElement(node) && isFocusable(node)) return true; + } + return false; +} +var isPointerEvent = (event) => "clientY" in event; +function isEventPointWithin(node, event) { + if (!isPointerEvent(event) || !node) return false; + const rect = node.getBoundingClientRect(); + if (rect.width === 0 || rect.height === 0) return false; + return rect.top <= event.clientY && event.clientY <= rect.top + rect.height && rect.left <= event.clientX && event.clientX <= rect.left + rect.width; +} +function isPointInRect(rect, point) { + return rect.y <= point.y && point.y <= rect.y + rect.height && rect.x <= point.x && point.x <= rect.x + rect.width; +} +function isEventWithinScrollbar(event, ancestor) { + if (!ancestor || !isPointerEvent(event)) return false; + const isScrollableY = ancestor.scrollHeight > ancestor.clientHeight; + const onScrollbarY = isScrollableY && event.clientX > ancestor.offsetLeft + ancestor.clientWidth; + const isScrollableX = ancestor.scrollWidth > ancestor.clientWidth; + const onScrollbarX = isScrollableX && event.clientY > ancestor.offsetTop + ancestor.clientHeight; + const rect = { + x: ancestor.offsetLeft, + y: ancestor.offsetTop, + width: ancestor.clientWidth + (isScrollableY ? 16 : 0), + height: ancestor.clientHeight + (isScrollableX ? 16 : 0) + }; + const point = { + x: event.clientX, + y: event.clientY + }; + if (!isPointInRect(rect, point)) return false; + return onScrollbarY || onScrollbarX; +} +function trackInteractOutsideImpl(node, options) { + const { + exclude, + onFocusOutside, + onPointerDownOutside, + onInteractOutside, + defer, + followControlledElements = true + } = options; + if (!node) return; + const doc = getDocument(node); + const win = getWindow(node); + const frames = getWindowFrames(win); + const parentWin = getParentWindow(win); + function isEventOutside(event, target) { + if (!isHTMLElement(target)) return false; + if (!target.isConnected) return false; + if (contains(node, target)) return false; + if (isEventPointWithin(node, event)) return false; + if (followControlledElements && isControlledElement(node, target)) return false; + const triggerEl = doc.querySelector(`[aria-controls="${node.id}"]`); + if (triggerEl) { + const triggerAncestor = getNearestOverflowAncestor(triggerEl); + if (isEventWithinScrollbar(event, triggerAncestor)) return false; + } + const nodeAncestor = getNearestOverflowAncestor(node); + if (isEventWithinScrollbar(event, nodeAncestor)) return false; + return !exclude?.(target); + } + const pointerdownCleanups = /* @__PURE__ */ new Set(); + const isInShadowRoot = isShadowRoot(node?.getRootNode()); + function onPointerDown(event) { + function handler(clickEvent) { + const func = defer && !isTouchDevice() ? raf : (v) => v(); + const evt = clickEvent ?? event; + const composedPath = evt?.composedPath?.() ?? [evt?.target]; + func(() => { + const target = isInShadowRoot ? composedPath[0] : getEventTarget(event); + if (!node || !isEventOutside(event, target)) return; + if (onPointerDownOutside || onInteractOutside) { + const handler2 = callAll(onPointerDownOutside, onInteractOutside); + node.addEventListener(POINTER_OUTSIDE_EVENT, handler2, { once: true }); + } + fireCustomEvent(node, POINTER_OUTSIDE_EVENT, { + bubbles: false, + cancelable: true, + detail: { + originalEvent: evt, + contextmenu: isContextMenuEvent(evt), + focusable: isComposedPathFocusable(composedPath), + target + } + }); + }); + } + if (event.pointerType === "touch") { + pointerdownCleanups.forEach((fn) => fn()); + pointerdownCleanups.add(addDomEvent(doc, "click", handler, { once: true })); + pointerdownCleanups.add(parentWin.addEventListener("click", handler, { once: true })); + pointerdownCleanups.add(frames.addEventListener("click", handler, { once: true })); + } else { + handler(); + } + } + const cleanups = /* @__PURE__ */ new Set(); + const timer = setTimeout(() => { + cleanups.add(addDomEvent(doc, "pointerdown", onPointerDown, true)); + cleanups.add(parentWin.addEventListener("pointerdown", onPointerDown, true)); + cleanups.add(frames.addEventListener("pointerdown", onPointerDown, true)); + }, 0); + function onFocusin(event) { + const func = defer ? raf : (v) => v(); + func(() => { + const composedPath = event?.composedPath?.() ?? [event?.target]; + const target = isInShadowRoot ? composedPath[0] : getEventTarget(event); + if (!node || !isEventOutside(event, target)) return; + if (onFocusOutside || onInteractOutside) { + const handler = callAll(onFocusOutside, onInteractOutside); + node.addEventListener(FOCUS_OUTSIDE_EVENT, handler, { once: true }); + } + fireCustomEvent(node, FOCUS_OUTSIDE_EVENT, { + bubbles: false, + cancelable: true, + detail: { + originalEvent: event, + contextmenu: false, + focusable: isFocusable(target), + target + } + }); + }); + } + if (!isTouchDevice()) { + cleanups.add(addDomEvent(doc, "focusin", onFocusin, true)); + cleanups.add(parentWin.addEventListener("focusin", onFocusin, true)); + cleanups.add(frames.addEventListener("focusin", onFocusin, true)); + } + return () => { + clearTimeout(timer); + pointerdownCleanups.forEach((fn) => fn()); + cleanups.forEach((fn) => fn()); + }; +} +function trackInteractOutside(nodeOrFn, options) { + const { defer } = options; + const func = defer ? raf : (v) => v(); + const cleanups = []; + cleanups.push( + func(() => { + const node = typeof nodeOrFn === "function" ? nodeOrFn() : nodeOrFn; + cleanups.push(trackInteractOutsideImpl(node, options)); + }) + ); + return () => { + cleanups.forEach((fn) => fn?.()); + }; +} +function fireCustomEvent(el, type, init) { + const win = el.ownerDocument.defaultView || window; + const event = new win.CustomEvent(type, init); + return el.dispatchEvent(event); +} + +export { + trackInteractOutside +}; diff --git a/priv/static/chunk-GN3NEUUU.mjs b/priv/static/chunk-GN3NEUUU.mjs new file mode 100644 index 0000000..f7ec740 --- /dev/null +++ b/priv/static/chunk-GN3NEUUU.mjs @@ -0,0 +1,1988 @@ +import { + compact, + getComputedStyle as getComputedStyle2, + getWindow, + isHTMLElement, + isNull, + noop, + raf +} from "./chunk-RUWIVFVB.mjs"; + +// ../node_modules/.pnpm/@floating-ui+utils@0.2.10/node_modules/@floating-ui/utils/dist/floating-ui.utils.mjs +var sides = ["top", "right", "bottom", "left"]; +var min = Math.min; +var max = Math.max; +var round = Math.round; +var floor = Math.floor; +var createCoords = (v) => ({ + x: v, + y: v +}); +var oppositeSideMap = { + left: "right", + right: "left", + bottom: "top", + top: "bottom" +}; +var oppositeAlignmentMap = { + start: "end", + end: "start" +}; +function clamp(start, value, end) { + return max(start, min(value, end)); +} +function evaluate(value, param) { + return typeof value === "function" ? value(param) : value; +} +function getSide(placement) { + return placement.split("-")[0]; +} +function getAlignment(placement) { + return placement.split("-")[1]; +} +function getOppositeAxis(axis) { + return axis === "x" ? "y" : "x"; +} +function getAxisLength(axis) { + return axis === "y" ? "height" : "width"; +} +var yAxisSides = /* @__PURE__ */ new Set(["top", "bottom"]); +function getSideAxis(placement) { + return yAxisSides.has(getSide(placement)) ? "y" : "x"; +} +function getAlignmentAxis(placement) { + return getOppositeAxis(getSideAxis(placement)); +} +function getAlignmentSides(placement, rects, rtl) { + if (rtl === void 0) { + rtl = false; + } + const alignment = getAlignment(placement); + const alignmentAxis = getAlignmentAxis(placement); + const length = getAxisLength(alignmentAxis); + let mainAlignmentSide = alignmentAxis === "x" ? alignment === (rtl ? "end" : "start") ? "right" : "left" : alignment === "start" ? "bottom" : "top"; + if (rects.reference[length] > rects.floating[length]) { + mainAlignmentSide = getOppositePlacement(mainAlignmentSide); + } + return [mainAlignmentSide, getOppositePlacement(mainAlignmentSide)]; +} +function getExpandedPlacements(placement) { + const oppositePlacement = getOppositePlacement(placement); + return [getOppositeAlignmentPlacement(placement), oppositePlacement, getOppositeAlignmentPlacement(oppositePlacement)]; +} +function getOppositeAlignmentPlacement(placement) { + return placement.replace(/start|end/g, (alignment) => oppositeAlignmentMap[alignment]); +} +var lrPlacement = ["left", "right"]; +var rlPlacement = ["right", "left"]; +var tbPlacement = ["top", "bottom"]; +var btPlacement = ["bottom", "top"]; +function getSideList(side, isStart, rtl) { + switch (side) { + case "top": + case "bottom": + if (rtl) return isStart ? rlPlacement : lrPlacement; + return isStart ? lrPlacement : rlPlacement; + case "left": + case "right": + return isStart ? tbPlacement : btPlacement; + default: + return []; + } +} +function getOppositeAxisPlacements(placement, flipAlignment, direction, rtl) { + const alignment = getAlignment(placement); + let list = getSideList(getSide(placement), direction === "start", rtl); + if (alignment) { + list = list.map((side) => side + "-" + alignment); + if (flipAlignment) { + list = list.concat(list.map(getOppositeAlignmentPlacement)); + } + } + return list; +} +function getOppositePlacement(placement) { + return placement.replace(/left|right|bottom|top/g, (side) => oppositeSideMap[side]); +} +function expandPaddingObject(padding) { + return { + top: 0, + right: 0, + bottom: 0, + left: 0, + ...padding + }; +} +function getPaddingObject(padding) { + return typeof padding !== "number" ? expandPaddingObject(padding) : { + top: padding, + right: padding, + bottom: padding, + left: padding + }; +} +function rectToClientRect(rect) { + const { + x, + y, + width, + height + } = rect; + return { + width, + height, + top: y, + left: x, + right: x + width, + bottom: y + height, + x, + y + }; +} + +// ../node_modules/.pnpm/@floating-ui+core@1.7.4/node_modules/@floating-ui/core/dist/floating-ui.core.mjs +function computeCoordsFromPlacement(_ref, placement, rtl) { + let { + reference, + floating + } = _ref; + const sideAxis = getSideAxis(placement); + const alignmentAxis = getAlignmentAxis(placement); + const alignLength = getAxisLength(alignmentAxis); + const side = getSide(placement); + const isVertical = sideAxis === "y"; + const commonX = reference.x + reference.width / 2 - floating.width / 2; + const commonY = reference.y + reference.height / 2 - floating.height / 2; + const commonAlign = reference[alignLength] / 2 - floating[alignLength] / 2; + let coords; + switch (side) { + case "top": + coords = { + x: commonX, + y: reference.y - floating.height + }; + break; + case "bottom": + coords = { + x: commonX, + y: reference.y + reference.height + }; + break; + case "right": + coords = { + x: reference.x + reference.width, + y: commonY + }; + break; + case "left": + coords = { + x: reference.x - floating.width, + y: commonY + }; + break; + default: + coords = { + x: reference.x, + y: reference.y + }; + } + switch (getAlignment(placement)) { + case "start": + coords[alignmentAxis] -= commonAlign * (rtl && isVertical ? -1 : 1); + break; + case "end": + coords[alignmentAxis] += commonAlign * (rtl && isVertical ? -1 : 1); + break; + } + return coords; +} +async function detectOverflow(state, options) { + var _await$platform$isEle; + if (options === void 0) { + options = {}; + } + const { + x, + y, + platform: platform2, + rects, + elements, + strategy + } = state; + const { + boundary = "clippingAncestors", + rootBoundary = "viewport", + elementContext = "floating", + altBoundary = false, + padding = 0 + } = evaluate(options, state); + const paddingObject = getPaddingObject(padding); + const altContext = elementContext === "floating" ? "reference" : "floating"; + const element = elements[altBoundary ? altContext : elementContext]; + const clippingClientRect = rectToClientRect(await platform2.getClippingRect({ + element: ((_await$platform$isEle = await (platform2.isElement == null ? void 0 : platform2.isElement(element))) != null ? _await$platform$isEle : true) ? element : element.contextElement || await (platform2.getDocumentElement == null ? void 0 : platform2.getDocumentElement(elements.floating)), + boundary, + rootBoundary, + strategy + })); + const rect = elementContext === "floating" ? { + x, + y, + width: rects.floating.width, + height: rects.floating.height + } : rects.reference; + const offsetParent = await (platform2.getOffsetParent == null ? void 0 : platform2.getOffsetParent(elements.floating)); + const offsetScale = await (platform2.isElement == null ? void 0 : platform2.isElement(offsetParent)) ? await (platform2.getScale == null ? void 0 : platform2.getScale(offsetParent)) || { + x: 1, + y: 1 + } : { + x: 1, + y: 1 + }; + const elementClientRect = rectToClientRect(platform2.convertOffsetParentRelativeRectToViewportRelativeRect ? await platform2.convertOffsetParentRelativeRectToViewportRelativeRect({ + elements, + rect, + offsetParent, + strategy + }) : rect); + return { + top: (clippingClientRect.top - elementClientRect.top + paddingObject.top) / offsetScale.y, + bottom: (elementClientRect.bottom - clippingClientRect.bottom + paddingObject.bottom) / offsetScale.y, + left: (clippingClientRect.left - elementClientRect.left + paddingObject.left) / offsetScale.x, + right: (elementClientRect.right - clippingClientRect.right + paddingObject.right) / offsetScale.x + }; +} +var computePosition = async (reference, floating, config) => { + const { + placement = "bottom", + strategy = "absolute", + middleware = [], + platform: platform2 + } = config; + const validMiddleware = middleware.filter(Boolean); + const rtl = await (platform2.isRTL == null ? void 0 : platform2.isRTL(floating)); + let rects = await platform2.getElementRects({ + reference, + floating, + strategy + }); + let { + x, + y + } = computeCoordsFromPlacement(rects, placement, rtl); + let statefulPlacement = placement; + let middlewareData = {}; + let resetCount = 0; + for (let i = 0; i < validMiddleware.length; i++) { + var _platform$detectOverf; + const { + name, + fn + } = validMiddleware[i]; + const { + x: nextX, + y: nextY, + data, + reset + } = await fn({ + x, + y, + initialPlacement: placement, + placement: statefulPlacement, + strategy, + middlewareData, + rects, + platform: { + ...platform2, + detectOverflow: (_platform$detectOverf = platform2.detectOverflow) != null ? _platform$detectOverf : detectOverflow + }, + elements: { + reference, + floating + } + }); + x = nextX != null ? nextX : x; + y = nextY != null ? nextY : y; + middlewareData = { + ...middlewareData, + [name]: { + ...middlewareData[name], + ...data + } + }; + if (reset && resetCount <= 50) { + resetCount++; + if (typeof reset === "object") { + if (reset.placement) { + statefulPlacement = reset.placement; + } + if (reset.rects) { + rects = reset.rects === true ? await platform2.getElementRects({ + reference, + floating, + strategy + }) : reset.rects; + } + ({ + x, + y + } = computeCoordsFromPlacement(rects, statefulPlacement, rtl)); + } + i = -1; + } + } + return { + x, + y, + placement: statefulPlacement, + strategy, + middlewareData + }; +}; +var arrow = (options) => ({ + name: "arrow", + options, + async fn(state) { + const { + x, + y, + placement, + rects, + platform: platform2, + elements, + middlewareData + } = state; + const { + element, + padding = 0 + } = evaluate(options, state) || {}; + if (element == null) { + return {}; + } + const paddingObject = getPaddingObject(padding); + const coords = { + x, + y + }; + const axis = getAlignmentAxis(placement); + const length = getAxisLength(axis); + const arrowDimensions = await platform2.getDimensions(element); + const isYAxis = axis === "y"; + const minProp = isYAxis ? "top" : "left"; + const maxProp = isYAxis ? "bottom" : "right"; + const clientProp = isYAxis ? "clientHeight" : "clientWidth"; + const endDiff = rects.reference[length] + rects.reference[axis] - coords[axis] - rects.floating[length]; + const startDiff = coords[axis] - rects.reference[axis]; + const arrowOffsetParent = await (platform2.getOffsetParent == null ? void 0 : platform2.getOffsetParent(element)); + let clientSize = arrowOffsetParent ? arrowOffsetParent[clientProp] : 0; + if (!clientSize || !await (platform2.isElement == null ? void 0 : platform2.isElement(arrowOffsetParent))) { + clientSize = elements.floating[clientProp] || rects.floating[length]; + } + const centerToReference = endDiff / 2 - startDiff / 2; + const largestPossiblePadding = clientSize / 2 - arrowDimensions[length] / 2 - 1; + const minPadding = min(paddingObject[minProp], largestPossiblePadding); + const maxPadding = min(paddingObject[maxProp], largestPossiblePadding); + const min$1 = minPadding; + const max2 = clientSize - arrowDimensions[length] - maxPadding; + const center = clientSize / 2 - arrowDimensions[length] / 2 + centerToReference; + const offset3 = clamp(min$1, center, max2); + const shouldAddOffset = !middlewareData.arrow && getAlignment(placement) != null && center !== offset3 && rects.reference[length] / 2 - (center < min$1 ? minPadding : maxPadding) - arrowDimensions[length] / 2 < 0; + const alignmentOffset = shouldAddOffset ? center < min$1 ? center - min$1 : center - max2 : 0; + return { + [axis]: coords[axis] + alignmentOffset, + data: { + [axis]: offset3, + centerOffset: center - offset3 - alignmentOffset, + ...shouldAddOffset && { + alignmentOffset + } + }, + reset: shouldAddOffset + }; + } +}); +var flip = function(options) { + if (options === void 0) { + options = {}; + } + return { + name: "flip", + options, + async fn(state) { + var _middlewareData$arrow, _middlewareData$flip; + const { + placement, + middlewareData, + rects, + initialPlacement, + platform: platform2, + elements + } = state; + const { + mainAxis: checkMainAxis = true, + crossAxis: checkCrossAxis = true, + fallbackPlacements: specifiedFallbackPlacements, + fallbackStrategy = "bestFit", + fallbackAxisSideDirection = "none", + flipAlignment = true, + ...detectOverflowOptions + } = evaluate(options, state); + if ((_middlewareData$arrow = middlewareData.arrow) != null && _middlewareData$arrow.alignmentOffset) { + return {}; + } + const side = getSide(placement); + const initialSideAxis = getSideAxis(initialPlacement); + const isBasePlacement = getSide(initialPlacement) === initialPlacement; + const rtl = await (platform2.isRTL == null ? void 0 : platform2.isRTL(elements.floating)); + const fallbackPlacements = specifiedFallbackPlacements || (isBasePlacement || !flipAlignment ? [getOppositePlacement(initialPlacement)] : getExpandedPlacements(initialPlacement)); + const hasFallbackAxisSideDirection = fallbackAxisSideDirection !== "none"; + if (!specifiedFallbackPlacements && hasFallbackAxisSideDirection) { + fallbackPlacements.push(...getOppositeAxisPlacements(initialPlacement, flipAlignment, fallbackAxisSideDirection, rtl)); + } + const placements2 = [initialPlacement, ...fallbackPlacements]; + const overflow = await platform2.detectOverflow(state, detectOverflowOptions); + const overflows = []; + let overflowsData = ((_middlewareData$flip = middlewareData.flip) == null ? void 0 : _middlewareData$flip.overflows) || []; + if (checkMainAxis) { + overflows.push(overflow[side]); + } + if (checkCrossAxis) { + const sides2 = getAlignmentSides(placement, rects, rtl); + overflows.push(overflow[sides2[0]], overflow[sides2[1]]); + } + overflowsData = [...overflowsData, { + placement, + overflows + }]; + if (!overflows.every((side2) => side2 <= 0)) { + var _middlewareData$flip2, _overflowsData$filter; + const nextIndex = (((_middlewareData$flip2 = middlewareData.flip) == null ? void 0 : _middlewareData$flip2.index) || 0) + 1; + const nextPlacement = placements2[nextIndex]; + if (nextPlacement) { + const ignoreCrossAxisOverflow = checkCrossAxis === "alignment" ? initialSideAxis !== getSideAxis(nextPlacement) : false; + if (!ignoreCrossAxisOverflow || // We leave the current main axis only if every placement on that axis + // overflows the main axis. + overflowsData.every((d) => getSideAxis(d.placement) === initialSideAxis ? d.overflows[0] > 0 : true)) { + return { + data: { + index: nextIndex, + overflows: overflowsData + }, + reset: { + placement: nextPlacement + } + }; + } + } + let resetPlacement = (_overflowsData$filter = overflowsData.filter((d) => d.overflows[0] <= 0).sort((a, b) => a.overflows[1] - b.overflows[1])[0]) == null ? void 0 : _overflowsData$filter.placement; + if (!resetPlacement) { + switch (fallbackStrategy) { + case "bestFit": { + var _overflowsData$filter2; + const placement2 = (_overflowsData$filter2 = overflowsData.filter((d) => { + if (hasFallbackAxisSideDirection) { + const currentSideAxis = getSideAxis(d.placement); + return currentSideAxis === initialSideAxis || // Create a bias to the `y` side axis due to horizontal + // reading directions favoring greater width. + currentSideAxis === "y"; + } + return true; + }).map((d) => [d.placement, d.overflows.filter((overflow2) => overflow2 > 0).reduce((acc, overflow2) => acc + overflow2, 0)]).sort((a, b) => a[1] - b[1])[0]) == null ? void 0 : _overflowsData$filter2[0]; + if (placement2) { + resetPlacement = placement2; + } + break; + } + case "initialPlacement": + resetPlacement = initialPlacement; + break; + } + } + if (placement !== resetPlacement) { + return { + reset: { + placement: resetPlacement + } + }; + } + } + return {}; + } + }; +}; +function getSideOffsets(overflow, rect) { + return { + top: overflow.top - rect.height, + right: overflow.right - rect.width, + bottom: overflow.bottom - rect.height, + left: overflow.left - rect.width + }; +} +function isAnySideFullyClipped(overflow) { + return sides.some((side) => overflow[side] >= 0); +} +var hide = function(options) { + if (options === void 0) { + options = {}; + } + return { + name: "hide", + options, + async fn(state) { + const { + rects, + platform: platform2 + } = state; + const { + strategy = "referenceHidden", + ...detectOverflowOptions + } = evaluate(options, state); + switch (strategy) { + case "referenceHidden": { + const overflow = await platform2.detectOverflow(state, { + ...detectOverflowOptions, + elementContext: "reference" + }); + const offsets = getSideOffsets(overflow, rects.reference); + return { + data: { + referenceHiddenOffsets: offsets, + referenceHidden: isAnySideFullyClipped(offsets) + } + }; + } + case "escaped": { + const overflow = await platform2.detectOverflow(state, { + ...detectOverflowOptions, + altBoundary: true + }); + const offsets = getSideOffsets(overflow, rects.floating); + return { + data: { + escapedOffsets: offsets, + escaped: isAnySideFullyClipped(offsets) + } + }; + } + default: { + return {}; + } + } + } + }; +}; +var originSides = /* @__PURE__ */ new Set(["left", "top"]); +async function convertValueToCoords(state, options) { + const { + placement, + platform: platform2, + elements + } = state; + const rtl = await (platform2.isRTL == null ? void 0 : platform2.isRTL(elements.floating)); + const side = getSide(placement); + const alignment = getAlignment(placement); + const isVertical = getSideAxis(placement) === "y"; + const mainAxisMulti = originSides.has(side) ? -1 : 1; + const crossAxisMulti = rtl && isVertical ? -1 : 1; + const rawValue = evaluate(options, state); + let { + mainAxis, + crossAxis, + alignmentAxis + } = typeof rawValue === "number" ? { + mainAxis: rawValue, + crossAxis: 0, + alignmentAxis: null + } : { + mainAxis: rawValue.mainAxis || 0, + crossAxis: rawValue.crossAxis || 0, + alignmentAxis: rawValue.alignmentAxis + }; + if (alignment && typeof alignmentAxis === "number") { + crossAxis = alignment === "end" ? alignmentAxis * -1 : alignmentAxis; + } + return isVertical ? { + x: crossAxis * crossAxisMulti, + y: mainAxis * mainAxisMulti + } : { + x: mainAxis * mainAxisMulti, + y: crossAxis * crossAxisMulti + }; +} +var offset = function(options) { + if (options === void 0) { + options = 0; + } + return { + name: "offset", + options, + async fn(state) { + var _middlewareData$offse, _middlewareData$arrow; + const { + x, + y, + placement, + middlewareData + } = state; + const diffCoords = await convertValueToCoords(state, options); + if (placement === ((_middlewareData$offse = middlewareData.offset) == null ? void 0 : _middlewareData$offse.placement) && (_middlewareData$arrow = middlewareData.arrow) != null && _middlewareData$arrow.alignmentOffset) { + return {}; + } + return { + x: x + diffCoords.x, + y: y + diffCoords.y, + data: { + ...diffCoords, + placement + } + }; + } + }; +}; +var shift = function(options) { + if (options === void 0) { + options = {}; + } + return { + name: "shift", + options, + async fn(state) { + const { + x, + y, + placement, + platform: platform2 + } = state; + const { + mainAxis: checkMainAxis = true, + crossAxis: checkCrossAxis = false, + limiter = { + fn: (_ref) => { + let { + x: x2, + y: y2 + } = _ref; + return { + x: x2, + y: y2 + }; + } + }, + ...detectOverflowOptions + } = evaluate(options, state); + const coords = { + x, + y + }; + const overflow = await platform2.detectOverflow(state, detectOverflowOptions); + const crossAxis = getSideAxis(getSide(placement)); + const mainAxis = getOppositeAxis(crossAxis); + let mainAxisCoord = coords[mainAxis]; + let crossAxisCoord = coords[crossAxis]; + if (checkMainAxis) { + const minSide = mainAxis === "y" ? "top" : "left"; + const maxSide = mainAxis === "y" ? "bottom" : "right"; + const min2 = mainAxisCoord + overflow[minSide]; + const max2 = mainAxisCoord - overflow[maxSide]; + mainAxisCoord = clamp(min2, mainAxisCoord, max2); + } + if (checkCrossAxis) { + const minSide = crossAxis === "y" ? "top" : "left"; + const maxSide = crossAxis === "y" ? "bottom" : "right"; + const min2 = crossAxisCoord + overflow[minSide]; + const max2 = crossAxisCoord - overflow[maxSide]; + crossAxisCoord = clamp(min2, crossAxisCoord, max2); + } + const limitedCoords = limiter.fn({ + ...state, + [mainAxis]: mainAxisCoord, + [crossAxis]: crossAxisCoord + }); + return { + ...limitedCoords, + data: { + x: limitedCoords.x - x, + y: limitedCoords.y - y, + enabled: { + [mainAxis]: checkMainAxis, + [crossAxis]: checkCrossAxis + } + } + }; + } + }; +}; +var limitShift = function(options) { + if (options === void 0) { + options = {}; + } + return { + options, + fn(state) { + const { + x, + y, + placement, + rects, + middlewareData + } = state; + const { + offset: offset3 = 0, + mainAxis: checkMainAxis = true, + crossAxis: checkCrossAxis = true + } = evaluate(options, state); + const coords = { + x, + y + }; + const crossAxis = getSideAxis(placement); + const mainAxis = getOppositeAxis(crossAxis); + let mainAxisCoord = coords[mainAxis]; + let crossAxisCoord = coords[crossAxis]; + const rawOffset = evaluate(offset3, state); + const computedOffset = typeof rawOffset === "number" ? { + mainAxis: rawOffset, + crossAxis: 0 + } : { + mainAxis: 0, + crossAxis: 0, + ...rawOffset + }; + if (checkMainAxis) { + const len = mainAxis === "y" ? "height" : "width"; + const limitMin = rects.reference[mainAxis] - rects.floating[len] + computedOffset.mainAxis; + const limitMax = rects.reference[mainAxis] + rects.reference[len] - computedOffset.mainAxis; + if (mainAxisCoord < limitMin) { + mainAxisCoord = limitMin; + } else if (mainAxisCoord > limitMax) { + mainAxisCoord = limitMax; + } + } + if (checkCrossAxis) { + var _middlewareData$offse, _middlewareData$offse2; + const len = mainAxis === "y" ? "width" : "height"; + const isOriginSide = originSides.has(getSide(placement)); + const limitMin = rects.reference[crossAxis] - rects.floating[len] + (isOriginSide ? ((_middlewareData$offse = middlewareData.offset) == null ? void 0 : _middlewareData$offse[crossAxis]) || 0 : 0) + (isOriginSide ? 0 : computedOffset.crossAxis); + const limitMax = rects.reference[crossAxis] + rects.reference[len] + (isOriginSide ? 0 : ((_middlewareData$offse2 = middlewareData.offset) == null ? void 0 : _middlewareData$offse2[crossAxis]) || 0) - (isOriginSide ? computedOffset.crossAxis : 0); + if (crossAxisCoord < limitMin) { + crossAxisCoord = limitMin; + } else if (crossAxisCoord > limitMax) { + crossAxisCoord = limitMax; + } + } + return { + [mainAxis]: mainAxisCoord, + [crossAxis]: crossAxisCoord + }; + } + }; +}; +var size = function(options) { + if (options === void 0) { + options = {}; + } + return { + name: "size", + options, + async fn(state) { + var _state$middlewareData, _state$middlewareData2; + const { + placement, + rects, + platform: platform2, + elements + } = state; + const { + apply = () => { + }, + ...detectOverflowOptions + } = evaluate(options, state); + const overflow = await platform2.detectOverflow(state, detectOverflowOptions); + const side = getSide(placement); + const alignment = getAlignment(placement); + const isYAxis = getSideAxis(placement) === "y"; + const { + width, + height + } = rects.floating; + let heightSide; + let widthSide; + if (side === "top" || side === "bottom") { + heightSide = side; + widthSide = alignment === (await (platform2.isRTL == null ? void 0 : platform2.isRTL(elements.floating)) ? "start" : "end") ? "left" : "right"; + } else { + widthSide = side; + heightSide = alignment === "end" ? "top" : "bottom"; + } + const maximumClippingHeight = height - overflow.top - overflow.bottom; + const maximumClippingWidth = width - overflow.left - overflow.right; + const overflowAvailableHeight = min(height - overflow[heightSide], maximumClippingHeight); + const overflowAvailableWidth = min(width - overflow[widthSide], maximumClippingWidth); + const noShift = !state.middlewareData.shift; + let availableHeight = overflowAvailableHeight; + let availableWidth = overflowAvailableWidth; + if ((_state$middlewareData = state.middlewareData.shift) != null && _state$middlewareData.enabled.x) { + availableWidth = maximumClippingWidth; + } + if ((_state$middlewareData2 = state.middlewareData.shift) != null && _state$middlewareData2.enabled.y) { + availableHeight = maximumClippingHeight; + } + if (noShift && !alignment) { + const xMin = max(overflow.left, 0); + const xMax = max(overflow.right, 0); + const yMin = max(overflow.top, 0); + const yMax = max(overflow.bottom, 0); + if (isYAxis) { + availableWidth = width - 2 * (xMin !== 0 || xMax !== 0 ? xMin + xMax : max(overflow.left, overflow.right)); + } else { + availableHeight = height - 2 * (yMin !== 0 || yMax !== 0 ? yMin + yMax : max(overflow.top, overflow.bottom)); + } + } + await apply({ + ...state, + availableWidth, + availableHeight + }); + const nextDimensions = await platform2.getDimensions(elements.floating); + if (width !== nextDimensions.width || height !== nextDimensions.height) { + return { + reset: { + rects: true + } + }; + } + return {}; + } + }; +}; + +// ../node_modules/.pnpm/@floating-ui+utils@0.2.10/node_modules/@floating-ui/utils/dist/floating-ui.utils.dom.mjs +function hasWindow() { + return typeof window !== "undefined"; +} +function getNodeName(node) { + if (isNode(node)) { + return (node.nodeName || "").toLowerCase(); + } + return "#document"; +} +function getWindow2(node) { + var _node$ownerDocument; + return (node == null || (_node$ownerDocument = node.ownerDocument) == null ? void 0 : _node$ownerDocument.defaultView) || window; +} +function getDocumentElement(node) { + var _ref; + return (_ref = (isNode(node) ? node.ownerDocument : node.document) || window.document) == null ? void 0 : _ref.documentElement; +} +function isNode(value) { + if (!hasWindow()) { + return false; + } + return value instanceof Node || value instanceof getWindow2(value).Node; +} +function isElement(value) { + if (!hasWindow()) { + return false; + } + return value instanceof Element || value instanceof getWindow2(value).Element; +} +function isHTMLElement2(value) { + if (!hasWindow()) { + return false; + } + return value instanceof HTMLElement || value instanceof getWindow2(value).HTMLElement; +} +function isShadowRoot(value) { + if (!hasWindow() || typeof ShadowRoot === "undefined") { + return false; + } + return value instanceof ShadowRoot || value instanceof getWindow2(value).ShadowRoot; +} +var invalidOverflowDisplayValues = /* @__PURE__ */ new Set(["inline", "contents"]); +function isOverflowElement(element) { + const { + overflow, + overflowX, + overflowY, + display + } = getComputedStyle3(element); + return /auto|scroll|overlay|hidden|clip/.test(overflow + overflowY + overflowX) && !invalidOverflowDisplayValues.has(display); +} +var tableElements = /* @__PURE__ */ new Set(["table", "td", "th"]); +function isTableElement(element) { + return tableElements.has(getNodeName(element)); +} +var topLayerSelectors = [":popover-open", ":modal"]; +function isTopLayer(element) { + return topLayerSelectors.some((selector) => { + try { + return element.matches(selector); + } catch (_e) { + return false; + } + }); +} +var transformProperties = ["transform", "translate", "scale", "rotate", "perspective"]; +var willChangeValues = ["transform", "translate", "scale", "rotate", "perspective", "filter"]; +var containValues = ["paint", "layout", "strict", "content"]; +function isContainingBlock(elementOrCss) { + const webkit = isWebKit(); + const css = isElement(elementOrCss) ? getComputedStyle3(elementOrCss) : elementOrCss; + return transformProperties.some((value) => css[value] ? css[value] !== "none" : false) || (css.containerType ? css.containerType !== "normal" : false) || !webkit && (css.backdropFilter ? css.backdropFilter !== "none" : false) || !webkit && (css.filter ? css.filter !== "none" : false) || willChangeValues.some((value) => (css.willChange || "").includes(value)) || containValues.some((value) => (css.contain || "").includes(value)); +} +function getContainingBlock(element) { + let currentNode = getParentNode(element); + while (isHTMLElement2(currentNode) && !isLastTraversableNode(currentNode)) { + if (isContainingBlock(currentNode)) { + return currentNode; + } else if (isTopLayer(currentNode)) { + return null; + } + currentNode = getParentNode(currentNode); + } + return null; +} +function isWebKit() { + if (typeof CSS === "undefined" || !CSS.supports) return false; + return CSS.supports("-webkit-backdrop-filter", "none"); +} +var lastTraversableNodeNames = /* @__PURE__ */ new Set(["html", "body", "#document"]); +function isLastTraversableNode(node) { + return lastTraversableNodeNames.has(getNodeName(node)); +} +function getComputedStyle3(element) { + return getWindow2(element).getComputedStyle(element); +} +function getNodeScroll(element) { + if (isElement(element)) { + return { + scrollLeft: element.scrollLeft, + scrollTop: element.scrollTop + }; + } + return { + scrollLeft: element.scrollX, + scrollTop: element.scrollY + }; +} +function getParentNode(node) { + if (getNodeName(node) === "html") { + return node; + } + const result = ( + // Step into the shadow DOM of the parent of a slotted node. + node.assignedSlot || // DOM Element detected. + node.parentNode || // ShadowRoot detected. + isShadowRoot(node) && node.host || // Fallback. + getDocumentElement(node) + ); + return isShadowRoot(result) ? result.host : result; +} +function getNearestOverflowAncestor(node) { + const parentNode = getParentNode(node); + if (isLastTraversableNode(parentNode)) { + return node.ownerDocument ? node.ownerDocument.body : node.body; + } + if (isHTMLElement2(parentNode) && isOverflowElement(parentNode)) { + return parentNode; + } + return getNearestOverflowAncestor(parentNode); +} +function getOverflowAncestors(node, list, traverseIframes) { + var _node$ownerDocument2; + if (list === void 0) { + list = []; + } + if (traverseIframes === void 0) { + traverseIframes = true; + } + const scrollableAncestor = getNearestOverflowAncestor(node); + const isBody = scrollableAncestor === ((_node$ownerDocument2 = node.ownerDocument) == null ? void 0 : _node$ownerDocument2.body); + const win = getWindow2(scrollableAncestor); + if (isBody) { + const frameElement = getFrameElement(win); + return list.concat(win, win.visualViewport || [], isOverflowElement(scrollableAncestor) ? scrollableAncestor : [], frameElement && traverseIframes ? getOverflowAncestors(frameElement) : []); + } + return list.concat(scrollableAncestor, getOverflowAncestors(scrollableAncestor, [], traverseIframes)); +} +function getFrameElement(win) { + return win.parent && Object.getPrototypeOf(win.parent) ? win.frameElement : null; +} + +// ../node_modules/.pnpm/@floating-ui+dom@1.7.5/node_modules/@floating-ui/dom/dist/floating-ui.dom.mjs +function getCssDimensions(element) { + const css = getComputedStyle3(element); + let width = parseFloat(css.width) || 0; + let height = parseFloat(css.height) || 0; + const hasOffset = isHTMLElement2(element); + const offsetWidth = hasOffset ? element.offsetWidth : width; + const offsetHeight = hasOffset ? element.offsetHeight : height; + const shouldFallback = round(width) !== offsetWidth || round(height) !== offsetHeight; + if (shouldFallback) { + width = offsetWidth; + height = offsetHeight; + } + return { + width, + height, + $: shouldFallback + }; +} +function unwrapElement(element) { + return !isElement(element) ? element.contextElement : element; +} +function getScale(element) { + const domElement = unwrapElement(element); + if (!isHTMLElement2(domElement)) { + return createCoords(1); + } + const rect = domElement.getBoundingClientRect(); + const { + width, + height, + $ + } = getCssDimensions(domElement); + let x = ($ ? round(rect.width) : rect.width) / width; + let y = ($ ? round(rect.height) : rect.height) / height; + if (!x || !Number.isFinite(x)) { + x = 1; + } + if (!y || !Number.isFinite(y)) { + y = 1; + } + return { + x, + y + }; +} +var noOffsets = /* @__PURE__ */ createCoords(0); +function getVisualOffsets(element) { + const win = getWindow2(element); + if (!isWebKit() || !win.visualViewport) { + return noOffsets; + } + return { + x: win.visualViewport.offsetLeft, + y: win.visualViewport.offsetTop + }; +} +function shouldAddVisualOffsets(element, isFixed, floatingOffsetParent) { + if (isFixed === void 0) { + isFixed = false; + } + if (!floatingOffsetParent || isFixed && floatingOffsetParent !== getWindow2(element)) { + return false; + } + return isFixed; +} +function getBoundingClientRect(element, includeScale, isFixedStrategy, offsetParent) { + if (includeScale === void 0) { + includeScale = false; + } + if (isFixedStrategy === void 0) { + isFixedStrategy = false; + } + const clientRect = element.getBoundingClientRect(); + const domElement = unwrapElement(element); + let scale = createCoords(1); + if (includeScale) { + if (offsetParent) { + if (isElement(offsetParent)) { + scale = getScale(offsetParent); + } + } else { + scale = getScale(element); + } + } + const visualOffsets = shouldAddVisualOffsets(domElement, isFixedStrategy, offsetParent) ? getVisualOffsets(domElement) : createCoords(0); + let x = (clientRect.left + visualOffsets.x) / scale.x; + let y = (clientRect.top + visualOffsets.y) / scale.y; + let width = clientRect.width / scale.x; + let height = clientRect.height / scale.y; + if (domElement) { + const win = getWindow2(domElement); + const offsetWin = offsetParent && isElement(offsetParent) ? getWindow2(offsetParent) : offsetParent; + let currentWin = win; + let currentIFrame = getFrameElement(currentWin); + while (currentIFrame && offsetParent && offsetWin !== currentWin) { + const iframeScale = getScale(currentIFrame); + const iframeRect = currentIFrame.getBoundingClientRect(); + const css = getComputedStyle3(currentIFrame); + const left = iframeRect.left + (currentIFrame.clientLeft + parseFloat(css.paddingLeft)) * iframeScale.x; + const top = iframeRect.top + (currentIFrame.clientTop + parseFloat(css.paddingTop)) * iframeScale.y; + x *= iframeScale.x; + y *= iframeScale.y; + width *= iframeScale.x; + height *= iframeScale.y; + x += left; + y += top; + currentWin = getWindow2(currentIFrame); + currentIFrame = getFrameElement(currentWin); + } + } + return rectToClientRect({ + width, + height, + x, + y + }); +} +function getWindowScrollBarX(element, rect) { + const leftScroll = getNodeScroll(element).scrollLeft; + if (!rect) { + return getBoundingClientRect(getDocumentElement(element)).left + leftScroll; + } + return rect.left + leftScroll; +} +function getHTMLOffset(documentElement, scroll) { + const htmlRect = documentElement.getBoundingClientRect(); + const x = htmlRect.left + scroll.scrollLeft - getWindowScrollBarX(documentElement, htmlRect); + const y = htmlRect.top + scroll.scrollTop; + return { + x, + y + }; +} +function convertOffsetParentRelativeRectToViewportRelativeRect(_ref) { + let { + elements, + rect, + offsetParent, + strategy + } = _ref; + const isFixed = strategy === "fixed"; + const documentElement = getDocumentElement(offsetParent); + const topLayer = elements ? isTopLayer(elements.floating) : false; + if (offsetParent === documentElement || topLayer && isFixed) { + return rect; + } + let scroll = { + scrollLeft: 0, + scrollTop: 0 + }; + let scale = createCoords(1); + const offsets = createCoords(0); + const isOffsetParentAnElement = isHTMLElement2(offsetParent); + if (isOffsetParentAnElement || !isOffsetParentAnElement && !isFixed) { + if (getNodeName(offsetParent) !== "body" || isOverflowElement(documentElement)) { + scroll = getNodeScroll(offsetParent); + } + if (isHTMLElement2(offsetParent)) { + const offsetRect = getBoundingClientRect(offsetParent); + scale = getScale(offsetParent); + offsets.x = offsetRect.x + offsetParent.clientLeft; + offsets.y = offsetRect.y + offsetParent.clientTop; + } + } + const htmlOffset = documentElement && !isOffsetParentAnElement && !isFixed ? getHTMLOffset(documentElement, scroll) : createCoords(0); + return { + width: rect.width * scale.x, + height: rect.height * scale.y, + x: rect.x * scale.x - scroll.scrollLeft * scale.x + offsets.x + htmlOffset.x, + y: rect.y * scale.y - scroll.scrollTop * scale.y + offsets.y + htmlOffset.y + }; +} +function getClientRects(element) { + return Array.from(element.getClientRects()); +} +function getDocumentRect(element) { + const html = getDocumentElement(element); + const scroll = getNodeScroll(element); + const body = element.ownerDocument.body; + const width = max(html.scrollWidth, html.clientWidth, body.scrollWidth, body.clientWidth); + const height = max(html.scrollHeight, html.clientHeight, body.scrollHeight, body.clientHeight); + let x = -scroll.scrollLeft + getWindowScrollBarX(element); + const y = -scroll.scrollTop; + if (getComputedStyle3(body).direction === "rtl") { + x += max(html.clientWidth, body.clientWidth) - width; + } + return { + width, + height, + x, + y + }; +} +var SCROLLBAR_MAX = 25; +function getViewportRect(element, strategy) { + const win = getWindow2(element); + const html = getDocumentElement(element); + const visualViewport = win.visualViewport; + let width = html.clientWidth; + let height = html.clientHeight; + let x = 0; + let y = 0; + if (visualViewport) { + width = visualViewport.width; + height = visualViewport.height; + const visualViewportBased = isWebKit(); + if (!visualViewportBased || visualViewportBased && strategy === "fixed") { + x = visualViewport.offsetLeft; + y = visualViewport.offsetTop; + } + } + const windowScrollbarX = getWindowScrollBarX(html); + if (windowScrollbarX <= 0) { + const doc = html.ownerDocument; + const body = doc.body; + const bodyStyles = getComputedStyle(body); + const bodyMarginInline = doc.compatMode === "CSS1Compat" ? parseFloat(bodyStyles.marginLeft) + parseFloat(bodyStyles.marginRight) || 0 : 0; + const clippingStableScrollbarWidth = Math.abs(html.clientWidth - body.clientWidth - bodyMarginInline); + if (clippingStableScrollbarWidth <= SCROLLBAR_MAX) { + width -= clippingStableScrollbarWidth; + } + } else if (windowScrollbarX <= SCROLLBAR_MAX) { + width += windowScrollbarX; + } + return { + width, + height, + x, + y + }; +} +var absoluteOrFixed = /* @__PURE__ */ new Set(["absolute", "fixed"]); +function getInnerBoundingClientRect(element, strategy) { + const clientRect = getBoundingClientRect(element, true, strategy === "fixed"); + const top = clientRect.top + element.clientTop; + const left = clientRect.left + element.clientLeft; + const scale = isHTMLElement2(element) ? getScale(element) : createCoords(1); + const width = element.clientWidth * scale.x; + const height = element.clientHeight * scale.y; + const x = left * scale.x; + const y = top * scale.y; + return { + width, + height, + x, + y + }; +} +function getClientRectFromClippingAncestor(element, clippingAncestor, strategy) { + let rect; + if (clippingAncestor === "viewport") { + rect = getViewportRect(element, strategy); + } else if (clippingAncestor === "document") { + rect = getDocumentRect(getDocumentElement(element)); + } else if (isElement(clippingAncestor)) { + rect = getInnerBoundingClientRect(clippingAncestor, strategy); + } else { + const visualOffsets = getVisualOffsets(element); + rect = { + x: clippingAncestor.x - visualOffsets.x, + y: clippingAncestor.y - visualOffsets.y, + width: clippingAncestor.width, + height: clippingAncestor.height + }; + } + return rectToClientRect(rect); +} +function hasFixedPositionAncestor(element, stopNode) { + const parentNode = getParentNode(element); + if (parentNode === stopNode || !isElement(parentNode) || isLastTraversableNode(parentNode)) { + return false; + } + return getComputedStyle3(parentNode).position === "fixed" || hasFixedPositionAncestor(parentNode, stopNode); +} +function getClippingElementAncestors(element, cache) { + const cachedResult = cache.get(element); + if (cachedResult) { + return cachedResult; + } + let result = getOverflowAncestors(element, [], false).filter((el) => isElement(el) && getNodeName(el) !== "body"); + let currentContainingBlockComputedStyle = null; + const elementIsFixed = getComputedStyle3(element).position === "fixed"; + let currentNode = elementIsFixed ? getParentNode(element) : element; + while (isElement(currentNode) && !isLastTraversableNode(currentNode)) { + const computedStyle = getComputedStyle3(currentNode); + const currentNodeIsContaining = isContainingBlock(currentNode); + if (!currentNodeIsContaining && computedStyle.position === "fixed") { + currentContainingBlockComputedStyle = null; + } + const shouldDropCurrentNode = elementIsFixed ? !currentNodeIsContaining && !currentContainingBlockComputedStyle : !currentNodeIsContaining && computedStyle.position === "static" && !!currentContainingBlockComputedStyle && absoluteOrFixed.has(currentContainingBlockComputedStyle.position) || isOverflowElement(currentNode) && !currentNodeIsContaining && hasFixedPositionAncestor(element, currentNode); + if (shouldDropCurrentNode) { + result = result.filter((ancestor) => ancestor !== currentNode); + } else { + currentContainingBlockComputedStyle = computedStyle; + } + currentNode = getParentNode(currentNode); + } + cache.set(element, result); + return result; +} +function getClippingRect(_ref) { + let { + element, + boundary, + rootBoundary, + strategy + } = _ref; + const elementClippingAncestors = boundary === "clippingAncestors" ? isTopLayer(element) ? [] : getClippingElementAncestors(element, this._c) : [].concat(boundary); + const clippingAncestors = [...elementClippingAncestors, rootBoundary]; + const firstClippingAncestor = clippingAncestors[0]; + const clippingRect = clippingAncestors.reduce((accRect, clippingAncestor) => { + const rect = getClientRectFromClippingAncestor(element, clippingAncestor, strategy); + accRect.top = max(rect.top, accRect.top); + accRect.right = min(rect.right, accRect.right); + accRect.bottom = min(rect.bottom, accRect.bottom); + accRect.left = max(rect.left, accRect.left); + return accRect; + }, getClientRectFromClippingAncestor(element, firstClippingAncestor, strategy)); + return { + width: clippingRect.right - clippingRect.left, + height: clippingRect.bottom - clippingRect.top, + x: clippingRect.left, + y: clippingRect.top + }; +} +function getDimensions(element) { + const { + width, + height + } = getCssDimensions(element); + return { + width, + height + }; +} +function getRectRelativeToOffsetParent(element, offsetParent, strategy) { + const isOffsetParentAnElement = isHTMLElement2(offsetParent); + const documentElement = getDocumentElement(offsetParent); + const isFixed = strategy === "fixed"; + const rect = getBoundingClientRect(element, true, isFixed, offsetParent); + let scroll = { + scrollLeft: 0, + scrollTop: 0 + }; + const offsets = createCoords(0); + function setLeftRTLScrollbarOffset() { + offsets.x = getWindowScrollBarX(documentElement); + } + if (isOffsetParentAnElement || !isOffsetParentAnElement && !isFixed) { + if (getNodeName(offsetParent) !== "body" || isOverflowElement(documentElement)) { + scroll = getNodeScroll(offsetParent); + } + if (isOffsetParentAnElement) { + const offsetRect = getBoundingClientRect(offsetParent, true, isFixed, offsetParent); + offsets.x = offsetRect.x + offsetParent.clientLeft; + offsets.y = offsetRect.y + offsetParent.clientTop; + } else if (documentElement) { + setLeftRTLScrollbarOffset(); + } + } + if (isFixed && !isOffsetParentAnElement && documentElement) { + setLeftRTLScrollbarOffset(); + } + const htmlOffset = documentElement && !isOffsetParentAnElement && !isFixed ? getHTMLOffset(documentElement, scroll) : createCoords(0); + const x = rect.left + scroll.scrollLeft - offsets.x - htmlOffset.x; + const y = rect.top + scroll.scrollTop - offsets.y - htmlOffset.y; + return { + x, + y, + width: rect.width, + height: rect.height + }; +} +function isStaticPositioned(element) { + return getComputedStyle3(element).position === "static"; +} +function getTrueOffsetParent(element, polyfill) { + if (!isHTMLElement2(element) || getComputedStyle3(element).position === "fixed") { + return null; + } + if (polyfill) { + return polyfill(element); + } + let rawOffsetParent = element.offsetParent; + if (getDocumentElement(element) === rawOffsetParent) { + rawOffsetParent = rawOffsetParent.ownerDocument.body; + } + return rawOffsetParent; +} +function getOffsetParent(element, polyfill) { + const win = getWindow2(element); + if (isTopLayer(element)) { + return win; + } + if (!isHTMLElement2(element)) { + let svgOffsetParent = getParentNode(element); + while (svgOffsetParent && !isLastTraversableNode(svgOffsetParent)) { + if (isElement(svgOffsetParent) && !isStaticPositioned(svgOffsetParent)) { + return svgOffsetParent; + } + svgOffsetParent = getParentNode(svgOffsetParent); + } + return win; + } + let offsetParent = getTrueOffsetParent(element, polyfill); + while (offsetParent && isTableElement(offsetParent) && isStaticPositioned(offsetParent)) { + offsetParent = getTrueOffsetParent(offsetParent, polyfill); + } + if (offsetParent && isLastTraversableNode(offsetParent) && isStaticPositioned(offsetParent) && !isContainingBlock(offsetParent)) { + return win; + } + return offsetParent || getContainingBlock(element) || win; +} +var getElementRects = async function(data) { + const getOffsetParentFn = this.getOffsetParent || getOffsetParent; + const getDimensionsFn = this.getDimensions; + const floatingDimensions = await getDimensionsFn(data.floating); + return { + reference: getRectRelativeToOffsetParent(data.reference, await getOffsetParentFn(data.floating), data.strategy), + floating: { + x: 0, + y: 0, + width: floatingDimensions.width, + height: floatingDimensions.height + } + }; +}; +function isRTL(element) { + return getComputedStyle3(element).direction === "rtl"; +} +var platform = { + convertOffsetParentRelativeRectToViewportRelativeRect, + getDocumentElement, + getClippingRect, + getOffsetParent, + getElementRects, + getClientRects, + getDimensions, + getScale, + isElement, + isRTL +}; +function rectsAreEqual(a, b) { + return a.x === b.x && a.y === b.y && a.width === b.width && a.height === b.height; +} +function observeMove(element, onMove) { + let io = null; + let timeoutId; + const root = getDocumentElement(element); + function cleanup() { + var _io; + clearTimeout(timeoutId); + (_io = io) == null || _io.disconnect(); + io = null; + } + function refresh(skip, threshold) { + if (skip === void 0) { + skip = false; + } + if (threshold === void 0) { + threshold = 1; + } + cleanup(); + const elementRectForRootMargin = element.getBoundingClientRect(); + const { + left, + top, + width, + height + } = elementRectForRootMargin; + if (!skip) { + onMove(); + } + if (!width || !height) { + return; + } + const insetTop = floor(top); + const insetRight = floor(root.clientWidth - (left + width)); + const insetBottom = floor(root.clientHeight - (top + height)); + const insetLeft = floor(left); + const rootMargin = -insetTop + "px " + -insetRight + "px " + -insetBottom + "px " + -insetLeft + "px"; + const options = { + rootMargin, + threshold: max(0, min(1, threshold)) || 1 + }; + let isFirstUpdate = true; + function handleObserve(entries) { + const ratio = entries[0].intersectionRatio; + if (ratio !== threshold) { + if (!isFirstUpdate) { + return refresh(); + } + if (!ratio) { + timeoutId = setTimeout(() => { + refresh(false, 1e-7); + }, 1e3); + } else { + refresh(false, ratio); + } + } + if (ratio === 1 && !rectsAreEqual(elementRectForRootMargin, element.getBoundingClientRect())) { + refresh(); + } + isFirstUpdate = false; + } + try { + io = new IntersectionObserver(handleObserve, { + ...options, + // Handle