hallo-kit 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +168 -0
- package/bin/cli.mjs +136 -0
- package/fonts/Manrope/Manrope-VariableFont_wght.woff2 +0 -0
- package/fonts/Manrope/OFL.txt +93 -0
- package/fonts/index.ts +30 -0
- package/package.json +77 -0
- package/src/config.ts +11 -0
- package/src/hooks/index.ts +14 -0
- package/src/hooks/useAutoHideOnScroll.ts +26 -0
- package/src/hooks/useCountdown.ts +44 -0
- package/src/hooks/useDevice.ts +63 -0
- package/src/hooks/useIsTelegramMiniApp.ts +17 -0
- package/src/hooks/useIsTouch.ts +23 -0
- package/src/hooks/useKeyboardOpen.ts +35 -0
- package/src/hooks/useMediaQuery.ts +24 -0
- package/src/hooks/usePlatform.ts +102 -0
- package/src/hooks/useQueryParams.tsx +70 -0
- package/src/hooks/useRevealOnce.ts +21 -0
- package/src/hooks/useScrolledPast.ts +19 -0
- package/src/hooks/useSettleOnce.ts +26 -0
- package/src/hooks/useTelegram.tsx +57 -0
- package/src/i18n/index.tsx +92 -0
- package/src/index.ts +35 -0
- package/src/lib/device.ts +28 -0
- package/src/lib/platform.ts +19 -0
- package/src/lib/utils.ts +36 -0
- package/src/logo/LogoFull.tsx +59 -0
- package/src/logo/LogoMark.tsx +31 -0
- package/src/logo/index.ts +2 -0
- package/src/next.ts +13 -0
- package/src/server.ts +4 -0
- package/src/theme-color-sync.tsx +43 -0
- package/src/theme-provider.tsx +31 -0
- package/src/types/css.d.ts +6 -0
- package/src/types/globals.d.ts +47 -0
- package/src/ui/Icon/Icon.module.css +25 -0
- package/src/ui/Icon/Icon.tsx +45 -0
- package/src/ui/Icon/Icon.types.ts +60 -0
- package/src/ui/Icon/data/brand.data.tsx +75 -0
- package/src/ui/Icon/data/index.ts +2 -0
- package/src/ui/Icon/data/ui.data.tsx +780 -0
- package/src/ui/Icon/index.ts +3 -0
- package/src/ui/badge/badge.tsx +185 -0
- package/src/ui/badge/index.ts +1 -0
- package/src/ui/breadcrumbs/breadcrumbs.tsx +63 -0
- package/src/ui/breadcrumbs/index.ts +1 -0
- package/src/ui/burger-icon/burger-icon.tsx +22 -0
- package/src/ui/burger-icon/index.ts +1 -0
- package/src/ui/button/button.tsx +179 -0
- package/src/ui/button/index.ts +1 -0
- package/src/ui/card/card.tsx +100 -0
- package/src/ui/card/index.ts +1 -0
- package/src/ui/confirm/confirm-dialog-host.tsx +89 -0
- package/src/ui/confirm/index.ts +1 -0
- package/src/ui/count-up/count-up.tsx +96 -0
- package/src/ui/count-up/index.ts +1 -0
- package/src/ui/data-list/data-list.tsx +331 -0
- package/src/ui/data-list/index.ts +1 -0
- package/src/ui/drawer/drawer.tsx +166 -0
- package/src/ui/drawer/index.ts +1 -0
- package/src/ui/flag/flag.data.ts +240 -0
- package/src/ui/flag/flag.tsx +57 -0
- package/src/ui/flag/index.ts +1 -0
- package/src/ui/image/image.tsx +67 -0
- package/src/ui/image/index.ts +1 -0
- package/src/ui/input/amount-input.tsx +55 -0
- package/src/ui/input/index.ts +2 -0
- package/src/ui/input/input.tsx +391 -0
- package/src/ui/label/index.ts +1 -0
- package/src/ui/label/label.tsx +34 -0
- package/src/ui/method-card/index.ts +1 -0
- package/src/ui/method-card/method-card.tsx +101 -0
- package/src/ui/modal/index.ts +1 -0
- package/src/ui/modal/modal.tsx +128 -0
- package/src/ui/otp-input/index.ts +1 -0
- package/src/ui/otp-input/otp-input.tsx +78 -0
- package/src/ui/pagination/index.ts +1 -0
- package/src/ui/pagination/pagination.tsx +138 -0
- package/src/ui/radio-group/index.ts +1 -0
- package/src/ui/radio-group/radio-group.tsx +81 -0
- package/src/ui/select/index.ts +1 -0
- package/src/ui/select/select.tsx +215 -0
- package/src/ui/separator/index.ts +1 -0
- package/src/ui/separator/separator.tsx +54 -0
- package/src/ui/skeleton/index.ts +1 -0
- package/src/ui/skeleton/skeleton.tsx +44 -0
- package/src/ui/spinner/index.ts +1 -0
- package/src/ui/spinner/spinner.tsx +33 -0
- package/src/ui/switch/index.ts +1 -0
- package/src/ui/switch/switch.tsx +48 -0
- package/src/ui/tabs/index.ts +1 -0
- package/src/ui/tabs/tabs.tsx +190 -0
- package/src/ui/textarea/index.ts +1 -0
- package/src/ui/textarea/textarea.tsx +151 -0
- package/src/ui/theme-switch/index.ts +1 -0
- package/src/ui/theme-switch/theme-switch.tsx +161 -0
- package/src/ui/toast/index.ts +1 -0
- package/src/ui/toast/toast.tsx +159 -0
- package/src/utils/api/stringFormat.ts +5 -0
- package/src/utils/clearAllCookies.ts +7 -0
- package/src/utils/clsx.ts +18 -0
- package/src/utils/confirm.ts +48 -0
- package/src/utils/copy-to-clipboard.ts +12 -0
- package/src/utils/date/convertTimestamp.ts +44 -0
- package/src/utils/date/formatCountdown.ts +9 -0
- package/src/utils/date/formatDayLabel.ts +50 -0
- package/src/utils/date/formatRelativeDateTime.ts +34 -0
- package/src/utils/date/locales.ts +9 -0
- package/src/utils/form/validationSchema.ts +46 -0
- package/src/utils/getByKeyString.ts +15 -0
- package/src/utils/getCssVar.ts +2 -0
- package/src/utils/getDeepType.ts +3 -0
- package/src/utils/getQueryParams.ts +21 -0
- package/src/utils/inAppBrowser.ts +151 -0
- package/src/utils/index.ts +28 -0
- package/src/utils/isRTL.ts +29 -0
- package/src/utils/notify.ts +46 -0
- package/src/utils/openExternal.ts +27 -0
- package/src/utils/system/getMillisecondsPeriod.ts +31 -0
- package/src/utils/validation/authValidators.ts +23 -0
- package/src/utils/validation/refCode.ts +30 -0
- package/styles/base.css +97 -0
- package/styles/brand.css +36 -0
- package/styles/index.css +19 -0
- package/styles/motion.css +207 -0
- package/styles/semantic.css +151 -0
- package/styles/theme.css +143 -0
- package/styles/utilities.css +289 -0
package/src/lib/utils.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type ClassValue, clsx } from 'clsx'
|
|
2
|
+
import { extendTailwindMerge } from 'tailwind-merge'
|
|
3
|
+
|
|
4
|
+
// Регистрируем наши кастомные text-*-утилиты типографики как font-size. Иначе
|
|
5
|
+
// tailwind-merge принимает их за text-{color} и при слиянии выкидывает реальный
|
|
6
|
+
// цвет (напр. text-primary-foreground на кнопке), из-за чего текст теряет цвет.
|
|
7
|
+
export const twMerge = extendTailwindMerge({
|
|
8
|
+
extend: {
|
|
9
|
+
classGroups: {
|
|
10
|
+
'font-size': [
|
|
11
|
+
{
|
|
12
|
+
text: [
|
|
13
|
+
'h1',
|
|
14
|
+
'h2',
|
|
15
|
+
'h3',
|
|
16
|
+
'h4',
|
|
17
|
+
'h5',
|
|
18
|
+
'body-l',
|
|
19
|
+
'body-m',
|
|
20
|
+
'body-s',
|
|
21
|
+
'caption',
|
|
22
|
+
'button-l',
|
|
23
|
+
'button-m',
|
|
24
|
+
'button-s',
|
|
25
|
+
],
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
// Слияние классов: clsx (условные/массивы) + tailwind-merge (разруливает конфликты
|
|
33
|
+
// Tailwind-классов). Используется компонентами ui-кита.
|
|
34
|
+
export function cn(...inputs: ClassValue[]) {
|
|
35
|
+
return twMerge(clsx(inputs))
|
|
36
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { ComponentProps } from 'react'
|
|
2
|
+
|
|
3
|
+
// Полный логотип: знак-капля + словесная часть «halloproxy». Цвета через токены →
|
|
4
|
+
// автоадаптация к теме:
|
|
5
|
+
// знак (капля + хвостик) и «proxy» = --color-primary,
|
|
6
|
+
// «h» внутри знака = --color-primary-foreground,
|
|
7
|
+
// «hallo» = --color-foreground (navy в light, светлый в dark).
|
|
8
|
+
// Размер — через className (напр. `h-10 w-auto`).
|
|
9
|
+
export function LogoFull({ className, ...props }: ComponentProps<'svg'>) {
|
|
10
|
+
return (
|
|
11
|
+
<svg
|
|
12
|
+
viewBox="0 0 646 112"
|
|
13
|
+
fill="none"
|
|
14
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
15
|
+
role="img"
|
|
16
|
+
aria-label="Hallo Proxy"
|
|
17
|
+
className={className}
|
|
18
|
+
{...props}
|
|
19
|
+
>
|
|
20
|
+
{/* знак: капля — primary */}
|
|
21
|
+
<path
|
|
22
|
+
d="M0 49.7337C0 28.3923 0 17.753 5.66586 10.4504C7.05085 8.68765 8.6247 7.1138 10.3874 5.72881C17.753 0 28.3923 0 49.7337 0H54.2663C75.6077 0 86.3099 0 93.6126 5.66586C95.3753 7.05085 96.9492 8.6247 98.3341 10.3874C104 17.6901 104 28.3923 104 49.7337C104 71.0751 104 81.7772 98.3341 89.0799C96.9492 90.8426 95.3753 92.4165 93.6126 93.8015C86.3099 99.4673 75.6077 99.4673 54.2663 99.4673H49.7337C28.3923 99.4673 17.6901 99.4673 10.3874 93.8015C8.6247 92.4165 7.05085 90.8426 5.66586 89.0799C0 81.7772 0 71.0751 0 49.7337Z"
|
|
23
|
+
fill="var(--color-primary)"
|
|
24
|
+
/>
|
|
25
|
+
{/* знак: хвостик капли — primary */}
|
|
26
|
+
<path
|
|
27
|
+
d="M51.5593 111.681C47.5303 111.681 39.1574 99.0898 39.1574 99.0898H64.402C64.402 99.0898 55.6514 111.681 51.5593 111.681Z"
|
|
28
|
+
fill="var(--color-primary)"
|
|
29
|
+
/>
|
|
30
|
+
{/* «h» внутри знака — primary-foreground (белая) */}
|
|
31
|
+
<path
|
|
32
|
+
d="M55.4625 37.9613C58.4843 37.9613 61.1913 38.6538 63.5835 39.9758C65.9758 41.2978 67.8015 43.2494 69.1235 45.8305C70.4455 48.4116 71.138 51.4964 71.138 55.1477V73.7192C71.138 76.5521 68.8717 78.8184 66.0387 78.8184C63.2058 78.8184 60.9395 76.5521 60.9395 73.7192V56.5327C60.9395 53.3221 60.1211 50.8669 58.5472 49.1671C56.9734 47.4044 54.77 46.523 52 46.523C49.23 46.523 46.9637 47.4044 45.3269 49.1671C43.753 50.8669 42.9346 53.3221 42.9346 56.5327V73.7192C42.9346 76.5521 40.6683 78.8184 37.8354 78.8184C35.0024 78.8184 32.7361 76.5521 32.7361 73.7192V24.8669C32.7361 22.0339 35.0024 19.7676 37.8354 19.7676C40.6683 19.7676 42.9346 22.0339 42.9346 24.8669V37.9613C42.9346 41.4238 45.0121 40.8572 48.1598 39.4722C50.3632 38.4649 52.7555 37.9613 55.3995 37.9613H55.4625Z"
|
|
33
|
+
fill="var(--color-primary-foreground)"
|
|
34
|
+
/>
|
|
35
|
+
<g clipPath="url(#logo-full-clip)">
|
|
36
|
+
{/* «hallo» — foreground */}
|
|
37
|
+
<path
|
|
38
|
+
d="M182.925 86.5421V60.3336C182.925 59.066 182.857 57.4558 182.72 55.503C182.583 53.5159 182.154 51.5289 181.435 49.5418C180.715 47.5549 179.534 45.8932 177.889 44.5571C176.279 43.221 173.983 42.5529 171.003 42.5529C169.804 42.5529 168.519 42.7413 167.149 43.1183C165.778 43.4951 164.494 44.2316 163.295 45.328C162.095 46.39 161.102 47.966 160.314 50.0558C159.56 52.1456 159.183 54.9207 159.183 58.3808L151.167 54.5781C151.167 50.1928 152.057 46.0817 153.839 42.2447C155.62 38.4076 158.293 35.307 161.856 32.9432C165.453 30.5793 169.975 29.3973 175.422 29.3973C179.773 29.3973 183.319 30.1339 186.06 31.607C188.801 33.0803 190.925 34.9474 192.432 37.2084C193.974 39.4696 195.07 41.8335 195.721 44.3002C196.372 46.7326 196.766 48.9595 196.903 50.9808C197.04 53.0021 197.109 54.4752 197.109 55.4002V86.5421H182.925ZM145 86.5421V12.5417H157.436V50.5696H159.183V86.5421H145ZM222.777 88.0838C218.803 88.0838 215.428 87.3301 212.653 85.8226C209.912 84.2809 207.822 82.2425 206.384 79.7073C204.979 77.1379 204.277 74.3115 204.277 71.2281C204.277 68.6587 204.671 66.3119 205.459 64.1878C206.247 62.0638 207.514 60.1965 209.261 58.5863C211.043 56.942 213.424 55.5716 216.405 54.4752C218.46 53.7215 220.91 53.0534 223.753 52.4711C226.597 51.8887 229.817 51.3405 233.414 50.8266C237.012 50.2784 240.968 49.6789 245.285 49.028L240.249 51.803C240.249 48.5142 239.461 46.0987 237.885 44.5571C236.309 43.0154 233.671 42.2447 229.971 42.2447C227.916 42.2447 225.774 42.7413 223.548 43.7348C221.321 44.7285 219.762 46.4928 218.871 49.028L206.229 45.0196C207.634 40.4289 210.272 36.6946 214.143 33.8169C218.015 30.939 223.291 29.5002 229.971 29.5002C234.87 29.5002 239.221 30.2537 243.024 31.7612C246.827 33.2687 249.705 35.8723 251.657 39.5723C252.754 41.628 253.405 43.6835 253.61 45.7391C253.816 47.7946 253.919 50.09 253.919 52.6253V86.5421H241.688V75.1337L243.435 77.4976C240.729 81.2318 237.799 83.9383 234.648 85.617C231.53 87.2615 227.573 88.0838 222.777 88.0838ZM225.757 77.0865C228.327 77.0865 230.485 76.641 232.232 75.7504C234.014 74.8253 235.419 73.7805 236.446 72.6156C237.508 71.4507 238.228 70.4744 238.605 69.6865C239.324 68.179 239.735 66.4318 239.838 64.4447C239.975 62.4234 240.043 60.7447 240.043 59.4086L244.155 60.4364C240.009 61.1216 236.652 61.704 234.082 62.1836C231.513 62.629 229.44 63.0401 227.864 63.417C226.288 63.7938 224.901 64.2049 223.702 64.6503C222.331 65.1984 221.218 65.7979 220.361 66.449C219.539 67.0655 218.923 67.7507 218.511 68.5045C218.135 69.2582 217.946 70.0975 217.946 71.0226C217.946 72.2902 218.254 73.3865 218.871 74.3115C219.522 75.2022 220.43 75.8875 221.595 76.3671C222.76 76.8466 224.147 77.0865 225.757 77.0865ZM265.225 86.5421V11H279.203V86.5421H265.225ZM292.554 86.5421V11H306.531V86.5421H292.554ZM343.83 88.0838C338.245 88.0838 333.347 86.8333 329.132 84.3323C324.918 81.8313 321.63 78.3884 319.266 74.0032C316.937 69.5836 315.771 64.5132 315.771 58.7919C315.771 53.0021 316.97 47.9145 319.369 43.5294C321.767 39.1099 325.073 35.6668 329.287 33.2002C333.5 30.7334 338.348 29.5002 343.83 29.5002C349.414 29.5002 354.313 30.7506 358.527 33.2515C362.775 35.7525 366.081 39.2127 368.445 43.6321C370.809 48.0173 371.991 53.0706 371.991 58.7919C371.991 64.5475 370.792 69.635 368.393 74.0545C366.03 78.4398 362.724 81.8828 358.475 84.3838C354.262 86.8504 349.38 88.0838 343.83 88.0838ZM343.83 75.0309C348.318 75.0309 351.658 73.5235 353.85 70.5086C356.077 67.4596 357.191 63.5541 357.191 58.7919C357.191 53.8585 356.061 49.9188 353.798 46.9724C351.573 44.0261 348.248 42.5529 343.83 42.5529C340.781 42.5529 338.28 43.2382 336.326 44.6086C334.374 45.9789 332.918 47.8803 331.958 50.3127C331.034 52.7452 330.571 55.5715 330.571 58.7919C330.571 63.7595 331.685 67.7165 333.912 70.6628C336.173 73.5749 339.479 75.0309 343.83 75.0309Z"
|
|
39
|
+
fill="var(--color-foreground)"
|
|
40
|
+
/>
|
|
41
|
+
{/* «proxy» — primary */}
|
|
42
|
+
<path
|
|
43
|
+
d="M407.444 88.0836C401.929 88.0836 397.321 86.7989 393.62 84.2294C389.921 81.66 387.128 78.1655 385.244 73.746C383.395 69.3266 382.47 64.3418 382.47 58.7918C382.47 53.2417 383.395 48.2569 385.244 43.8375C387.095 39.4181 389.817 35.9235 393.415 33.3541C397.013 30.7847 401.449 29.5 406.726 29.5C412.036 29.5 416.661 30.7676 420.6 33.3027C424.54 35.838 427.589 39.3153 429.748 43.7347C431.94 48.1199 433.036 53.139 433.036 58.7918C433.036 64.3418 431.957 69.3266 429.798 73.746C427.674 78.1655 424.695 81.66 420.857 84.2294C417.021 86.7989 412.549 88.0836 407.444 88.0836ZM380.106 111.209V31.0417H392.438V69.0697H394.185V111.209H380.106ZM405.184 75.6474C408.198 75.6474 410.665 74.8937 412.583 73.3863C414.502 71.8789 415.924 69.8576 416.849 67.3224C417.774 64.753 418.236 61.9094 418.236 58.7918C418.236 55.7084 417.756 52.8992 416.797 50.3639C415.839 47.7945 414.348 45.7561 412.326 44.2487C410.339 42.707 407.788 41.9361 404.67 41.9361C401.758 41.9361 399.394 42.6385 397.578 44.0431C395.796 45.4477 394.494 47.4177 393.672 49.9528C392.85 52.4881 392.438 55.4344 392.438 58.7918C392.438 62.1491 392.85 65.0956 393.672 67.6307C394.494 70.1659 395.831 72.1358 397.681 73.5404C399.565 74.9452 402.066 75.6474 405.184 75.6474ZM441.261 86.542V31.0417H453.595V44.6084L452.258 42.8611C452.978 40.9426 453.937 39.1954 455.136 37.6194C456.334 36.0436 457.808 34.7417 459.555 33.7138C460.891 32.8916 462.348 32.2579 463.923 31.8124C465.499 31.3328 467.126 31.0417 468.805 30.9388C470.484 30.8018 472.163 30.8361 473.842 31.0417V44.0945C472.299 43.6148 470.502 43.4607 468.446 43.632C466.424 43.769 464.592 44.2315 462.947 45.0194C461.303 45.7731 459.915 46.7838 458.785 48.0514C457.655 49.2848 456.798 50.7579 456.216 52.4709C455.633 54.1496 455.342 56.051 455.342 58.1751V86.542H441.261ZM504.504 88.0836C498.921 88.0836 494.021 86.8331 489.808 84.3322C485.593 81.8312 482.304 78.3883 479.941 74.003C477.611 69.5835 476.447 64.5131 476.447 58.7918C476.447 53.0019 477.646 47.9143 480.043 43.5293C482.442 39.1097 485.748 35.6666 489.961 33.2C494.176 30.7332 499.023 29.5 504.504 29.5C510.089 29.5 514.988 30.7504 519.202 33.2514C523.45 35.7523 526.756 39.2126 529.12 43.632C531.484 48.0172 532.667 53.0704 532.667 58.7918C532.667 64.5474 531.468 69.6348 529.069 74.0544C526.705 78.4396 523.399 81.8827 519.151 84.3836C514.938 86.8503 510.055 88.0836 504.504 88.0836ZM504.504 75.0307C508.993 75.0307 512.333 73.5234 514.526 70.5085C516.753 67.4595 517.867 63.5539 517.867 58.7918C517.867 53.8584 516.735 49.9186 514.474 46.9722C512.247 44.0259 508.924 42.5528 504.504 42.5528C501.455 42.5528 498.954 43.238 497.002 44.6084C495.05 45.9787 493.593 47.8801 492.634 50.3126C491.709 52.745 491.247 55.5714 491.247 58.7918C491.247 63.7593 492.36 67.7164 494.586 70.6627C496.848 73.5747 500.155 75.0307 504.504 75.0307ZM532.71 86.542L552.546 58.4835L533.12 31.0417H549.565L560.665 47.4862L571.56 31.0417H588.004L568.579 58.4835L588.519 86.542H572.074L560.665 69.4808L549.153 86.542H532.71ZM600.798 111.209L611.487 81.8142L611.692 90.4476L587.539 31.0417H602.031L618.271 73.0779H614.981L631.118 31.0417H645.096L613.748 111.209H600.798Z"
|
|
44
|
+
fill="var(--color-primary)"
|
|
45
|
+
/>
|
|
46
|
+
</g>
|
|
47
|
+
<defs>
|
|
48
|
+
<clipPath id="logo-full-clip">
|
|
49
|
+
<rect
|
|
50
|
+
width="501"
|
|
51
|
+
height="100.479"
|
|
52
|
+
fill="white"
|
|
53
|
+
transform="translate(145 11)"
|
|
54
|
+
/>
|
|
55
|
+
</clipPath>
|
|
56
|
+
</defs>
|
|
57
|
+
</svg>
|
|
58
|
+
)
|
|
59
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { ComponentProps } from 'react'
|
|
2
|
+
|
|
3
|
+
// Короткий знак бренда (синяя капля с белой «h»).
|
|
4
|
+
// Цвета — через токены дизайн-системы, поэтому знак сам адаптируется к теме.
|
|
5
|
+
// Размер задаётся через className (напр. `h-8 w-auto`); пропорции держит viewBox.
|
|
6
|
+
export function LogoMark({ className, ...props }: ComponentProps<'svg'>) {
|
|
7
|
+
return (
|
|
8
|
+
<svg
|
|
9
|
+
viewBox='0 0 166 178'
|
|
10
|
+
fill='none'
|
|
11
|
+
xmlns='http://www.w3.org/2000/svg'
|
|
12
|
+
role='img'
|
|
13
|
+
aria-label='Hallo Proxy'
|
|
14
|
+
className={className}
|
|
15
|
+
{...props}
|
|
16
|
+
>
|
|
17
|
+
<path
|
|
18
|
+
d='M0 79C0 45.1 0 28.2 9 16.6C11.2 13.8 13.7 11.3 16.5 9.1C28.2 0 45.1 0 79 0H86.2C120.1 0 137.1 0 148.7 9C151.5 11.2 154 13.7 156.2 16.5C165.2 28.1 165.2 45.1 165.2 79C165.2 112.9 165.2 129.9 156.2 141.5C154 144.3 151.5 146.8 148.7 149C137.1 158 120.1 158 86.2 158H79C45.1 158 28.1 158 16.5 149C13.7 146.8 11.2 144.3 9 141.5C0 129.9 0 112.9 0 79Z'
|
|
19
|
+
fill='var(--color-primary)'
|
|
20
|
+
/>
|
|
21
|
+
<path
|
|
22
|
+
d='M81.9002 177.4C75.5002 177.4 62.2002 157.4 62.2002 157.4H102.3C102.3 157.4 88.4002 177.4 81.9002 177.4Z'
|
|
23
|
+
fill='var(--color-primary)'
|
|
24
|
+
/>
|
|
25
|
+
<path
|
|
26
|
+
d='M88.1 60.2999C92.9 60.2999 97.2 61.3999 101 63.4999C104.8 65.5999 107.7 68.6999 109.8 72.7999C111.9 76.8999 113 81.7999 113 87.5999V117.1C113 121.6 109.4 125.2 104.9 125.2C100.4 125.2 96.8 121.6 96.8 117.1V89.7999C96.8 84.6999 95.5 80.7999 93 78.0999C90.5 75.2999 87 73.8999 82.6 73.8999C78.2 73.8999 74.6 75.2999 72 78.0999C69.5 80.7999 68.2 84.6999 68.2 89.7999V117.1C68.2 121.6 64.6 125.2 60.1 125.2C55.6 125.2 52 121.6 52 117.1V39.4999C52 34.9999 55.6 31.3999 60.1 31.3999C64.6 31.3999 68.2 34.9999 68.2 39.4999V60.2999C68.2 65.7999 71.5 64.8999 76.5 62.6999C80 61.0999 83.8 60.2999 88 60.2999H88.1Z'
|
|
27
|
+
fill='var(--color-primary-foreground)'
|
|
28
|
+
/>
|
|
29
|
+
</svg>
|
|
30
|
+
)
|
|
31
|
+
}
|
package/src/next.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Next-специфичная часть кита. Всё остальное — обычный React и работает в любом
|
|
2
|
+
// сборщике (Vite, Remix, CRA): next-themes, вопреки названию, к Next не привязан.
|
|
3
|
+
//
|
|
4
|
+
// Здесь лежит то, что без Next не соберётся: next/link, next/image, next/navigation.
|
|
5
|
+
// Держим отдельным входом, чтобы не-Next проект мог пользоваться китом, не подтягивая
|
|
6
|
+
// фреймворк ради двух компонентов.
|
|
7
|
+
//
|
|
8
|
+
// Ещё два Next-входа по той же причине: 'hallo-kit/fonts' (next/font) и
|
|
9
|
+
// 'hallo-kit/server' (next/headers, только для Server Components).
|
|
10
|
+
|
|
11
|
+
export * from './ui/breadcrumbs'
|
|
12
|
+
export * from './ui/image'
|
|
13
|
+
export * from './hooks/useQueryParams'
|
package/src/server.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useEffect } from 'react'
|
|
4
|
+
|
|
5
|
+
import { useTheme } from 'next-themes'
|
|
6
|
+
|
|
7
|
+
import { getCssVar } from './utils/getCssVar'
|
|
8
|
+
|
|
9
|
+
// Красит «хром» браузера (meta theme-color: статус-бар/адресная строка Safari iOS и
|
|
10
|
+
// overscroll-«резинка» вокруг страницы) в фон приложения под ТЕКУЩУЮ тему. Дефолт до
|
|
11
|
+
// гидрации задаёт viewport-export в layout (по системной media); здесь — синк с
|
|
12
|
+
// фактической темой next-themes: она переключается классом на <html>, и системная
|
|
13
|
+
// media может не совпадать с выбором юзера. Аналог TelegramChromeSync, но для веба.
|
|
14
|
+
export function ThemeColorSync() {
|
|
15
|
+
const { resolvedTheme } = useTheme()
|
|
16
|
+
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
// rAF — дождаться применения класса темы к <html> перед чтением CSS-переменной.
|
|
19
|
+
const id = requestAnimationFrame(() => {
|
|
20
|
+
const color = getCssVar('--color-background')
|
|
21
|
+
if (!color) return
|
|
22
|
+
|
|
23
|
+
const metas = document.querySelectorAll<HTMLMetaElement>(
|
|
24
|
+
'meta[name="theme-color"]',
|
|
25
|
+
)
|
|
26
|
+
if (metas.length === 0) {
|
|
27
|
+
const meta = document.createElement('meta')
|
|
28
|
+
meta.name = 'theme-color'
|
|
29
|
+
meta.content = color
|
|
30
|
+
document.head.appendChild(meta)
|
|
31
|
+
return
|
|
32
|
+
}
|
|
33
|
+
// Next из viewport-экспорта рендерит ДВА тега с media (light/dark) — ставим
|
|
34
|
+
// актуальный цвет обоим, чтобы фактическая тема побеждала системную схему.
|
|
35
|
+
metas.forEach(meta => {
|
|
36
|
+
meta.content = color
|
|
37
|
+
})
|
|
38
|
+
})
|
|
39
|
+
return () => cancelAnimationFrame(id)
|
|
40
|
+
}, [resolvedTheme])
|
|
41
|
+
|
|
42
|
+
return null
|
|
43
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { ReactNode } from 'react'
|
|
4
|
+
|
|
5
|
+
import { ThemeProvider as NextThemesProvider } from 'next-themes'
|
|
6
|
+
|
|
7
|
+
// Управление темой через next-themes. attribute="class" → ставит класс на <html>
|
|
8
|
+
// (light/dark), на который завязан `@custom-variant dark` в globals.css.
|
|
9
|
+
//
|
|
10
|
+
// Включены light + dark (переключатель — ThemeSwitch). Тёмная палитра определена в
|
|
11
|
+
// `.dark { ... }` (globals.css) — переопределяет семантические токены. При желании
|
|
12
|
+
// позже можно добавить системную тему: enableSystem + defaultTheme="system".
|
|
13
|
+
//
|
|
14
|
+
// disableTransitionOnChange: на миг флипа темы next-themes вешает `* { transition: none }`
|
|
15
|
+
// на всю страницу → тема переключается МГНОВЕННО и синхронно (все элементы разом, без
|
|
16
|
+
// лагов и рассинхрона). Покомпонентные transition-colors при этом никуда не деваются —
|
|
17
|
+
// они глушатся ТОЛЬКО на этот кадр, а на ховерах/тумблере работают как обычно. Цена —
|
|
18
|
+
// «бегунок» тумблера в момент флипа не проезжает, а перескакивает (в тон мгновенной смене).
|
|
19
|
+
export function ThemeProvider({ children }: { children: ReactNode }) {
|
|
20
|
+
return (
|
|
21
|
+
<NextThemesProvider
|
|
22
|
+
attribute="class"
|
|
23
|
+
defaultTheme="light"
|
|
24
|
+
themes={['light', 'dark']}
|
|
25
|
+
enableSystem={false}
|
|
26
|
+
disableTransitionOnChange
|
|
27
|
+
>
|
|
28
|
+
{children}
|
|
29
|
+
</NextThemesProvider>
|
|
30
|
+
)
|
|
31
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// Глобальные типы окружения. Telegram WebApp SDK инжектится скриптом-лоадером
|
|
2
|
+
// (см. layout.tsx) и доступен как window.Telegram.WebApp.
|
|
3
|
+
declare global {
|
|
4
|
+
// Нативная кнопка «Назад» Telegram MiniApp (в шапке клиента).
|
|
5
|
+
interface TelegramBackButton {
|
|
6
|
+
show: () => void
|
|
7
|
+
hide: () => void
|
|
8
|
+
onClick: (cb: () => void) => void
|
|
9
|
+
offClick: (cb: () => void) => void
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface TelegramWebApp {
|
|
13
|
+
initData: string
|
|
14
|
+
initDataUnsafe?: {
|
|
15
|
+
user?: {
|
|
16
|
+
id?: number
|
|
17
|
+
username?: string
|
|
18
|
+
/** ISO-код языка клиента Telegram (напр. 'ru', 'en') — для LocaleSync. */
|
|
19
|
+
language_code?: string
|
|
20
|
+
[key: string]: unknown
|
|
21
|
+
}
|
|
22
|
+
[key: string]: unknown
|
|
23
|
+
}
|
|
24
|
+
ready: () => void
|
|
25
|
+
/** Развернуть MiniApp на всю высоту вьюпорта. */
|
|
26
|
+
expand?: () => void
|
|
27
|
+
/** Нативная кнопка «Назад». */
|
|
28
|
+
BackButton?: TelegramBackButton
|
|
29
|
+
openLink?: (url: string, options?: { try_instant_view?: boolean }) => void
|
|
30
|
+
/** Открыть t.me/ tg:// ссылку ВНУТРИ Telegram (бот/чат), не во внешнем браузере. */
|
|
31
|
+
openTelegramLink?: (url: string) => void
|
|
32
|
+
setHeaderColor?: (color: string) => void
|
|
33
|
+
setBackgroundColor?: (color: string) => void
|
|
34
|
+
setBottomBarColor?: (color: string) => void
|
|
35
|
+
headerColor?: string
|
|
36
|
+
backgroundColor?: string
|
|
37
|
+
bottomBarColor?: string
|
|
38
|
+
[key: string]: unknown
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
interface Window {
|
|
42
|
+
Telegram?: { WebApp?: TelegramWebApp }
|
|
43
|
+
TelegramWebviewProxy?: unknown
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export {}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/* Размер задаёт Icon.tsx: квадрат width=height=size. SVG вписывается в этот бокс с
|
|
2
|
+
сохранением пропорций (preserveAspectRatio="meet" по умолчанию) — поэтому `size`
|
|
3
|
+
предсказуемо задаёт размер любой иконки, включая неквадратные (шеврон, warning). */
|
|
4
|
+
.icon {
|
|
5
|
+
display: inline-flex;
|
|
6
|
+
flex-shrink: 0;
|
|
7
|
+
align-items: center;
|
|
8
|
+
justify-content: center;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.icon svg {
|
|
12
|
+
display: block;
|
|
13
|
+
width: 100%;
|
|
14
|
+
height: 100%;
|
|
15
|
+
overflow: visible;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/* Перекраска монохромных иконок в currentColor (наследует цвет текста). */
|
|
19
|
+
.currentColor svg [fill] {
|
|
20
|
+
fill: currentColor;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.currentColor svg [stroke] {
|
|
24
|
+
stroke: currentColor;
|
|
25
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { type CSSProperties, type FC } from 'react'
|
|
2
|
+
|
|
3
|
+
import { clsx } from '../../utils/clsx'
|
|
4
|
+
|
|
5
|
+
import styles from './Icon.module.css'
|
|
6
|
+
|
|
7
|
+
import { IconProps, UIIconNames } from './Icon.types'
|
|
8
|
+
import * as UIIcons from './data'
|
|
9
|
+
|
|
10
|
+
export const Icon: FC<IconProps> = ({
|
|
11
|
+
type,
|
|
12
|
+
size,
|
|
13
|
+
className,
|
|
14
|
+
title,
|
|
15
|
+
isCurrentColor,
|
|
16
|
+
}) => {
|
|
17
|
+
if (!type) return null
|
|
18
|
+
|
|
19
|
+
const iconKey = UIIconNames[type.toUpperCase() as keyof typeof UIIconNames]
|
|
20
|
+
const IconComponent = (UIIcons as Record<string, FC | undefined>)[iconKey]
|
|
21
|
+
|
|
22
|
+
if (!IconComponent) return null
|
|
23
|
+
|
|
24
|
+
// size: число → px; строка → CSS-длина как есть (напр. '1.25em' — масштабируется
|
|
25
|
+
// за font-size родителя, что даёт CSS-адаптив без JS). Квадратный бокс size×size.
|
|
26
|
+
const dim = size == null ? '24px' : typeof size === 'number' ? `${size}px` : size
|
|
27
|
+
const iconStyle: CSSProperties = {
|
|
28
|
+
width: dim,
|
|
29
|
+
height: dim,
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<div
|
|
34
|
+
className={clsx(
|
|
35
|
+
styles.icon,
|
|
36
|
+
{ [styles.currentColor]: isCurrentColor },
|
|
37
|
+
className,
|
|
38
|
+
)}
|
|
39
|
+
style={iconStyle}
|
|
40
|
+
title={title}
|
|
41
|
+
>
|
|
42
|
+
<IconComponent />
|
|
43
|
+
</div>
|
|
44
|
+
)
|
|
45
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
export enum UIIconNames {
|
|
2
|
+
CLOSE = 'Close',
|
|
3
|
+
COPY = 'Copy',
|
|
4
|
+
PASTE = 'Paste',
|
|
5
|
+
SEARCH = 'Search',
|
|
6
|
+
ALERT = 'Alert',
|
|
7
|
+
PLAY_CIRCLE = 'PlayCircle',
|
|
8
|
+
WARNING = 'Warning',
|
|
9
|
+
QR = 'QR',
|
|
10
|
+
PLUS = 'Plus',
|
|
11
|
+
ARROW_RIGHT = 'ArrowRight',
|
|
12
|
+
EXTERNAL = 'External',
|
|
13
|
+
LINK = 'Link',
|
|
14
|
+
DOWNLOAD = 'Download',
|
|
15
|
+
AT = 'At',
|
|
16
|
+
PLUS_CIRCLE = 'PlusCircle',
|
|
17
|
+
INFO = 'Info',
|
|
18
|
+
CLOCK = 'Clock',
|
|
19
|
+
SOON = 'Soon',
|
|
20
|
+
MINUS_CIRCLE = 'MinusCircle',
|
|
21
|
+
SUN = 'Sun',
|
|
22
|
+
MOON = 'Moon',
|
|
23
|
+
CHECK_CIRCLE = 'CheckCircle',
|
|
24
|
+
CHECK = 'Check',
|
|
25
|
+
CHEVRON_DOWN = 'ChevronDown',
|
|
26
|
+
CHEVRON_RIGHT = 'ChevronRight',
|
|
27
|
+
HOME = 'Home',
|
|
28
|
+
TARIFF = 'Tariff',
|
|
29
|
+
CONNECT = 'Connect',
|
|
30
|
+
DEVICES = 'Devices',
|
|
31
|
+
LOCATION = 'Location',
|
|
32
|
+
REFERRALS = 'Referrals',
|
|
33
|
+
SUPPORT = 'Support',
|
|
34
|
+
SETTINGS = 'Settings',
|
|
35
|
+
SIDEBAR = 'Sidebar',
|
|
36
|
+
LOGOUT = 'Logout',
|
|
37
|
+
MENU = 'Menu',
|
|
38
|
+
TRASH = 'Trash',
|
|
39
|
+
SEND = 'Send',
|
|
40
|
+
PAPERCLIP = 'Paperclip',
|
|
41
|
+
CARD = 'Card',
|
|
42
|
+
USER = 'User',
|
|
43
|
+
// Брендовые (цветные) иконки провайдеров OAuth — из brand.data, не currentColor.
|
|
44
|
+
TELEGRAM = 'Telegram',
|
|
45
|
+
GOOGLE = 'Google',
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface IconProps {
|
|
49
|
+
type: IconNameType
|
|
50
|
+
/**
|
|
51
|
+
* Размер: число → px (по умолчанию 24); строка → CSS-длина как есть
|
|
52
|
+
* (напр. '1.25em' для масштаба за font-size).
|
|
53
|
+
*/
|
|
54
|
+
size?: number | string
|
|
55
|
+
className?: string
|
|
56
|
+
title?: string
|
|
57
|
+
isCurrentColor?: boolean
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export type IconNameType = Lowercase<keyof typeof UIIconNames>
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// Брендовые (цветные) иконки провайдеров OAuth. В отличие от ui.data — НЕ
|
|
2
|
+
// currentColor: сохраняют фирменные градиенты/фильтры. Рендерятся тем же
|
|
3
|
+
// компонентом Icon, но без isCurrentColor. id градиентов/масок уникальны между
|
|
4
|
+
// иконками, поэтому коллизий на странице нет.
|
|
5
|
+
|
|
6
|
+
export const Telegram = () => (
|
|
7
|
+
<svg
|
|
8
|
+
width='28'
|
|
9
|
+
height='28'
|
|
10
|
+
viewBox='0 0 28 28'
|
|
11
|
+
fill='none'
|
|
12
|
+
xmlns='http://www.w3.org/2000/svg'
|
|
13
|
+
>
|
|
14
|
+
<g clipPath='url(#clip0_384_4248)'>
|
|
15
|
+
<path
|
|
16
|
+
d='M14 28C21.732 28 28 21.732 28 14C28 6.26801 21.732 0 14 0C6.26801 0 0 6.26801 0 14C0 21.732 6.26801 28 14 28Z'
|
|
17
|
+
fill='url(#paint0_linear_384_4248)'
|
|
18
|
+
/>
|
|
19
|
+
<path
|
|
20
|
+
fillRule='evenodd'
|
|
21
|
+
clipRule='evenodd'
|
|
22
|
+
d='M6.33719 13.8539C10.4185 12.0757 13.14 10.9035 14.5017 10.3371C18.3896 8.71997 19.1975 8.43906 19.7241 8.42979C19.8399 8.42775 20.0988 8.45645 20.2666 8.59255C20.4082 8.70748 20.4472 8.86273 20.4658 8.97169C20.4845 9.08065 20.5077 9.32887 20.4892 9.52282C20.2785 11.7366 19.3669 17.1087 18.9031 19.5881C18.7069 20.6372 18.3204 20.989 17.9463 21.0234C17.1333 21.0982 16.516 20.4861 15.7285 19.97C14.4963 19.1623 13.8002 18.6595 12.6042 17.8713C11.222 16.9604 12.118 16.4598 12.9057 15.6416C13.1119 15.4275 16.694 12.1693 16.7633 11.8737C16.772 11.8367 16.7801 11.6989 16.6982 11.6262C16.6163 11.5534 16.4955 11.5783 16.4083 11.5981C16.2848 11.6261 14.3163 12.9272 10.5031 15.5013C9.94433 15.8849 9.43825 16.0719 8.98482 16.0621C8.48494 16.0513 7.52339 15.7794 6.80857 15.5471C5.93182 15.2621 5.235 15.1114 5.29568 14.6274C5.32728 14.3753 5.67445 14.1174 6.33719 13.8539Z'
|
|
23
|
+
fill='white'
|
|
24
|
+
/>
|
|
25
|
+
</g>
|
|
26
|
+
<defs>
|
|
27
|
+
<linearGradient
|
|
28
|
+
id='paint0_linear_384_4248'
|
|
29
|
+
x1='14'
|
|
30
|
+
y1='0'
|
|
31
|
+
x2='14'
|
|
32
|
+
y2='27.7923'
|
|
33
|
+
gradientUnits='userSpaceOnUse'
|
|
34
|
+
>
|
|
35
|
+
<stop stopColor='#2AABEE' />
|
|
36
|
+
<stop offset='1' stopColor='#229ED9' />
|
|
37
|
+
</linearGradient>
|
|
38
|
+
<clipPath id='clip0_384_4248'>
|
|
39
|
+
<rect width='28' height='28' fill='white' />
|
|
40
|
+
</clipPath>
|
|
41
|
+
</defs>
|
|
42
|
+
</svg>
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
// Канонический Google «G» (плоские 4 цвета). Намеренно БЕЗ маски/фильтров/градиентов:
|
|
46
|
+
// исходный градиентный ассет строился на mask-type:luminance + 9 feGaussianBlur в
|
|
47
|
+
// userSpaceOnUse и в масштабированном контейнере внутри кнопки с transform пропадал на
|
|
48
|
+
// мобильном WebKit (iOS) — движок терял маскированный контент. Плоский логотип рендерится
|
|
49
|
+
// одинаково на всех платформах.
|
|
50
|
+
export const Google = () => (
|
|
51
|
+
<svg
|
|
52
|
+
width='28'
|
|
53
|
+
height='28'
|
|
54
|
+
viewBox='0 0 48 48'
|
|
55
|
+
fill='none'
|
|
56
|
+
xmlns='http://www.w3.org/2000/svg'
|
|
57
|
+
>
|
|
58
|
+
<path
|
|
59
|
+
fill='#4285F4'
|
|
60
|
+
d='M47.532 24.552c0-1.636-.147-3.209-.42-4.72H24.48v8.934h12.925c-.557 2.999-2.25 5.539-4.795 7.24v6.02h7.756c4.537-4.177 7.166-10.327 7.166-17.474z'
|
|
61
|
+
/>
|
|
62
|
+
<path
|
|
63
|
+
fill='#34A853'
|
|
64
|
+
d='M24.48 48c6.48 0 11.915-2.147 15.887-5.814l-7.756-6.02c-2.148 1.44-4.895 2.29-8.131 2.29-6.252 0-11.545-4.222-13.433-9.896H2.964v6.216C6.914 42.62 15.028 48 24.48 48z'
|
|
65
|
+
/>
|
|
66
|
+
<path
|
|
67
|
+
fill='#FBBC05'
|
|
68
|
+
d='M11.047 28.56A14.42 14.42 0 0 1 10.29 24c0-1.582.273-3.118.757-4.56v-6.216H2.964A23.9 23.9 0 0 0 .48 24c0 3.874.927 7.537 2.484 10.776l8.083-6.216z'
|
|
69
|
+
/>
|
|
70
|
+
<path
|
|
71
|
+
fill='#EA4335'
|
|
72
|
+
d='M24.48 9.544c3.527 0 6.693 1.213 9.184 3.593l6.888-6.888C36.389 2.377 30.955 0 24.48 0 15.028 0 6.914 5.38 2.964 13.224l8.083 6.216c1.888-5.674 7.181-9.896 13.433-9.896z'
|
|
73
|
+
/>
|
|
74
|
+
</svg>
|
|
75
|
+
)
|