@shohojdhara/atomix 0.5.0 → 0.5.2
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/atomix.config.ts +12 -0
- package/build-tools/webpack-loader.js +5 -4
- package/dist/atomix.css +230 -83
- package/dist/atomix.css.map +1 -1
- package/dist/atomix.min.css +1 -1
- package/dist/atomix.min.css.map +1 -1
- package/dist/build-tools/webpack-loader.js +5 -4
- package/dist/charts.d.ts +24 -23
- package/dist/charts.js +271 -369
- package/dist/charts.js.map +1 -1
- package/dist/config.d.ts +624 -0
- package/dist/config.js +59 -0
- package/dist/config.js.map +1 -0
- package/dist/core.d.ts +3 -2
- package/dist/core.js +342 -382
- package/dist/core.js.map +1 -1
- package/dist/forms.d.ts +4 -6
- package/dist/forms.js +233 -334
- package/dist/forms.js.map +1 -1
- package/dist/heavy.d.ts +11 -2
- package/dist/heavy.js +406 -445
- package/dist/heavy.js.map +1 -1
- package/dist/index.d.ts +109 -65
- package/dist/index.esm.js +654 -748
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +621 -717
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/dist/layout.js +59 -60
- package/dist/layout.js.map +1 -1
- package/dist/theme.js +4 -4
- package/dist/theme.js.map +1 -1
- package/package.json +24 -9
- package/scripts/atomix-cli.js +15 -1
- package/scripts/cli/__tests__/complexity-utils.test.js +24 -0
- package/scripts/cli/__tests__/detector.test.js +50 -0
- package/scripts/cli/__tests__/template-engine.test.js +23 -0
- package/scripts/cli/__tests__/test-setup.js +1 -133
- package/scripts/cli/commands/doctor.js +15 -3
- package/scripts/cli/commands/generate.js +113 -51
- package/scripts/cli/internal/ai-engine.js +30 -10
- package/scripts/cli/internal/complexity-utils.js +60 -0
- package/scripts/cli/internal/component-validator.js +49 -16
- package/scripts/cli/internal/generator.js +89 -36
- package/scripts/cli/internal/hook-generator.js +5 -2
- package/scripts/cli/internal/itcss-generator.js +16 -12
- package/scripts/cli/templates/next-templates.js +81 -30
- package/scripts/cli/templates/storybook-templates.js +12 -2
- package/scripts/cli/utils/detector.js +45 -7
- package/scripts/cli/utils/diagnostics.js +78 -0
- package/scripts/cli/utils/telemetry.js +13 -0
- package/src/components/Accordion/Accordion.stories.tsx +4 -0
- package/src/components/AtomixGlass/AtomixGlass.tsx +188 -128
- package/src/components/AtomixGlass/AtomixGlassContainer.tsx +63 -91
- package/src/components/AtomixGlass/PerformanceDashboard.tsx +153 -201
- package/src/components/AtomixGlass/__snapshots__/AtomixGlass.test.tsx.snap +9 -6
- package/src/components/AtomixGlass/glass-utils.ts +51 -1
- package/src/components/AtomixGlass/stories/AnimationFeatures.stories.tsx +52 -46
- package/src/components/AtomixGlass/stories/Examples.stories.tsx +573 -236
- package/src/components/AtomixGlass/stories/Playground.stories.tsx +88 -41
- package/src/components/AtomixGlass/stories/argTypes.ts +19 -19
- package/src/components/AtomixGlass/stories/shared-components.tsx +7 -12
- package/src/components/AtomixGlass/stories/types.ts +3 -3
- package/src/components/Button/Button.tsx +114 -57
- package/src/components/Callout/Callout.tsx +4 -4
- package/src/components/Chart/ChartRenderer.tsx +1 -1
- package/src/components/Chart/DonutChart.tsx +11 -8
- package/src/components/EdgePanel/EdgePanel.tsx +119 -115
- package/src/components/Form/Select.tsx +4 -4
- package/src/components/List/List.tsx +4 -4
- package/src/components/Navigation/SideMenu/SideMenu.tsx +6 -6
- package/src/components/PhotoViewer/PhotoViewerImage.tsx +1 -1
- package/src/components/ProductReview/ProductReview.tsx +4 -2
- package/src/components/Rating/Rating.tsx +4 -2
- package/src/components/SectionIntro/SectionIntro.tsx +4 -2
- package/src/components/Steps/Steps.tsx +1 -1
- package/src/components/Tabs/Tabs.tsx +5 -5
- package/src/components/Testimonial/Testimonial.tsx +4 -2
- package/src/components/VideoPlayer/VideoPlayer.tsx +4 -2
- package/src/layouts/CssGrid/CssGrid.stories.tsx +464 -0
- package/src/layouts/CssGrid/CssGrid.tsx +215 -0
- package/src/layouts/CssGrid/index.ts +8 -0
- package/src/layouts/CssGrid/scripts/CssGrid.js +284 -0
- package/src/layouts/CssGrid/scripts/index.js +43 -0
- package/src/layouts/Grid/scripts/Container.js +139 -0
- package/src/layouts/Grid/scripts/Grid.js +184 -0
- package/src/layouts/Grid/scripts/GridCol.js +273 -0
- package/src/layouts/Grid/scripts/Row.js +154 -0
- package/src/layouts/Grid/scripts/index.js +48 -0
- package/src/layouts/MasonryGrid/MasonryGrid.tsx +71 -59
- package/src/lib/composables/atomix-glass/useGlassSize.ts +1 -1
- package/src/lib/composables/useAccordion.ts +5 -5
- package/src/lib/composables/useAtomixGlass.ts +111 -74
- package/src/lib/composables/useAtomixGlassStyles.ts +0 -2
- package/src/lib/composables/useBarChart.ts +2 -2
- package/src/lib/composables/useChart.ts +3 -2
- package/src/lib/composables/useChartToolbar.ts +48 -66
- package/src/lib/composables/useDataTable.ts +1 -1
- package/src/lib/composables/useDatePicker.ts +2 -2
- package/src/lib/composables/useEdgePanel.ts +45 -54
- package/src/lib/composables/useHeroBackgroundSlider.ts +5 -5
- package/src/lib/composables/usePhotoViewer.ts +2 -3
- package/src/lib/composables/usePieChart.ts +1 -1
- package/src/lib/composables/usePopover.ts +151 -139
- package/src/lib/composables/useSideMenu.ts +28 -41
- package/src/lib/composables/useSlider.ts +2 -6
- package/src/lib/composables/useTooltip.ts +2 -2
- package/src/lib/config/index.ts +39 -0
- package/src/lib/constants/components.ts +1 -0
- package/src/lib/theme/devtools/Comparator.tsx +1 -1
- package/src/lib/theme/devtools/Inspector.tsx +1 -1
- package/src/lib/theme/devtools/LiveEditor.tsx +1 -1
- package/src/lib/theme/runtime/ThemeProvider.tsx +1 -1
- package/src/lib/types/components.ts +1 -0
- package/src/styles/01-settings/_index.scss +1 -0
- package/src/styles/01-settings/_settings.atomix-glass.scss +174 -0
- package/src/styles/01-settings/_settings.masonry-grid.scss +42 -6
- package/src/styles/02-tools/_tools.glass.scss +6 -0
- package/src/styles/05-objects/_objects.masonry-grid.scss +162 -24
- package/src/styles/06-components/_components.atomix-glass.scss +160 -99
- package/scripts/cli/__tests__/README.md +0 -81
- package/scripts/cli/__tests__/basic.test.js +0 -116
- package/scripts/cli/__tests__/clean.test.js +0 -278
- package/scripts/cli/__tests__/component-generator.test.js +0 -332
- package/scripts/cli/__tests__/component-validator.test.js +0 -433
- package/scripts/cli/__tests__/generator.test.js +0 -613
- package/scripts/cli/__tests__/glass-motion.test.js +0 -256
- package/scripts/cli/__tests__/integration.test.js +0 -938
- package/scripts/cli/__tests__/migrate.test.js +0 -74
- package/scripts/cli/__tests__/security.test.js +0 -206
- package/scripts/cli/__tests__/theme-bridge.test.js +0 -507
- package/scripts/cli/__tests__/token-manager.test.js +0 -251
- package/scripts/cli/__tests__/token-provider.test.js +0 -361
- package/scripts/cli/__tests__/utils.test.js +0 -165
- package/src/components/AtomixGlass/stories/AnimationTests.stories.tsx +0 -95
- package/src/components/AtomixGlass/stories/CardExamples.stories.tsx +0 -212
- package/src/components/AtomixGlass/stories/Customization.stories.tsx +0 -131
- package/src/components/AtomixGlass/stories/DashboardExamples.stories.tsx +0 -348
- package/src/components/AtomixGlass/stories/EcommerceExamples.stories.tsx +0 -410
- package/src/components/AtomixGlass/stories/FormExamples.stories.tsx +0 -436
- package/src/components/AtomixGlass/stories/HeroExamples.stories.tsx +0 -264
- package/src/components/AtomixGlass/stories/InteractivePlayground.stories.tsx +0 -247
- package/src/components/AtomixGlass/stories/MobileUIExamples.stories.tsx +0 -418
- package/src/components/AtomixGlass/stories/ModalExamples.stories.tsx +0 -402
- package/src/components/AtomixGlass/stories/Modes.stories.tsx +0 -1082
- package/src/components/AtomixGlass/stories/Overview.stories.tsx +0 -497
- package/src/components/AtomixGlass/stories/Performance.stories.tsx +0 -103
- package/src/components/AtomixGlass/stories/PresetGallery.stories.tsx +0 -335
- package/src/components/AtomixGlass/stories/Shaders.stories.tsx +0 -395
- package/src/components/AtomixGlass/stories/WidgetExamples.stories.tsx +0 -441
- package/src/components/TypedButton/TypedButton.stories.tsx +0 -59
- package/src/components/TypedButton/TypedButton.tsx +0 -39
- package/src/components/TypedButton/index.ts +0 -2
- package/src/lib/composables/useBreadcrumb.ts +0 -81
- package/src/lib/composables/useChartInteractions.ts +0 -123
- package/src/lib/composables/useChartPerformance.ts +0 -347
- package/src/lib/composables/useDropdown.ts +0 -338
- package/src/lib/composables/useModal.ts +0 -110
- package/src/lib/composables/useTypedButton.ts +0 -66
- package/src/lib/hooks/usePerformanceMonitor.ts +0 -148
- package/src/lib/utils/displacement-generator.ts +0 -92
- package/src/lib/utils/memoryMonitor.ts +0 -191
- package/src/styles/01-settings/_settings.testtypecheck.scss +0 -53
- package/src/styles/01-settings/_settings.typedbutton.scss +0 -53
- package/src/styles/06-components/_components.testbutton.scss +0 -212
- package/src/styles/06-components/_components.testtypecheck.scss +0 -212
- package/src/styles/06-components/_components.typedbutton.scss +0 -212
|
@@ -4,11 +4,11 @@ exports[`AtomixGlass Visual Regression > matches snapshot with default props 1`]
|
|
|
4
4
|
<div>
|
|
5
5
|
<div
|
|
6
6
|
class="c-atomix-glass"
|
|
7
|
-
style="--atomix-glass-radius: 16px; --atomix-glass-transform: scale(1); --atomix-glass-top: 0px; --atomix-glass-left: 0px; --atomix-glass-
|
|
7
|
+
style="--atomix-glass-radius: 16px; --atomix-glass-transform: translate(0px, 0px) scale(1); --atomix-glass-container-position: absolute; --atomix-glass-position: absolute; --atomix-glass-top: 0px; --atomix-glass-left: 0px; --atomix-glass-right: auto; --atomix-glass-bottom: auto; --atomix-glass-width: 100%; --atomix-glass-height: 100%; --atomix-glass-border-width: var(--atomix-spacing-0-5, 0.125rem); --atomix-glass-blend-mode: overlay; --atomix-glass-border-gradient-1: linear-gradient(135deg, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.08399999999999999) 33%, rgba(255, 255, 255, 0.27999999999999997) 66%, rgba(255, 255, 255, 0) 100%); --atomix-glass-border-gradient-2: linear-gradient(135deg, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.22399999999999998) 33%, rgba(255, 255, 255, 0.42) 66%, rgba(255, 255, 255, 0) 100%); --atomix-glass-hover-1-opacity: 0; --atomix-glass-hover-1-gradient: radial-gradient(circle at 50% 50%, rgba(255, 255, 255, 0.5) 0%, rgba(255, 255, 255, 0) 50%); --atomix-glass-hover-2-opacity: 0; --atomix-glass-hover-2-gradient: radial-gradient(circle at 50% 50%, rgba(255, 255, 255, 1) 0%, rgba(255, 255, 255, 0) 80%); --atomix-glass-hover-3-opacity: 0; --atomix-glass-hover-3-gradient: radial-gradient(circle at 50% 50%, rgba(255, 255, 255, 1) 0%, rgba(255, 255, 255, 0) 100%); --atomix-glass-base-opacity: 0; --atomix-glass-base-gradient: rgba(255, 255, 255, 0.1); --atomix-glass-overlay-opacity: 0; --atomix-glass-overlay-gradient: rgba(255, 255, 255, 0.05); --atomix-glass-overlay-highlight-opacity: 0; --atomix-glass-overlay-highlight-bg: radial-gradient(circle at 20% 20%, rgba(255, 255, 255, 0.4) 0%, transparent 60%); --atomix-glass-hover-1-opacity: 0; --atomix-glass-hover-2-opacity: 0; --atomix-glass-hover-3-opacity: 0; --atomix-glass-base-opacity: 0; --atomix-glass-overlay-opacity: 0; --atomix-glass-overlay-highlight-opacity: 0;"
|
|
8
8
|
>
|
|
9
9
|
<div
|
|
10
10
|
class="c-atomix-glass__container "
|
|
11
|
-
style="
|
|
11
|
+
style="--atomix-glass-container-radius: 16px; --atomix-glass-container-backdrop: blur(0.1px) saturate(140%) contrast(1.4) brightness(0.9); --atomix-glass-container-shadow: 0 0 20px rgba(0, 0, 0, 0.15) inset, 0 4px 8px rgba(0, 0, 0, 0.08) inset; --atomix-glass-container-shadow-opacity: 1; --atomix-glass-container-bg: none; --atomix-glass-container-text-shadow: 0px 2px 12px rgba(0, 0, 0, 0.4); --atomix-glass-container-box-shadow: 0px 12px 40px rgba(0, 0, 0, 0.25); --atomix-glass-container-padding: 0;"
|
|
12
12
|
>
|
|
13
13
|
<div
|
|
14
14
|
class="c-atomix-glass__inner"
|
|
@@ -24,7 +24,7 @@ exports[`AtomixGlass Visual Regression > matches snapshot with default props 1`]
|
|
|
24
24
|
<radialgradient
|
|
25
25
|
cx="50%"
|
|
26
26
|
cy="50%"
|
|
27
|
-
id="atomix-glass-filter-
|
|
27
|
+
id="atomix-glass-filter-_r_f_-edge-mask"
|
|
28
28
|
r="50%"
|
|
29
29
|
>
|
|
30
30
|
<stop
|
|
@@ -46,7 +46,7 @@ exports[`AtomixGlass Visual Regression > matches snapshot with default props 1`]
|
|
|
46
46
|
<filter
|
|
47
47
|
color-interpolation-filters="sRGB"
|
|
48
48
|
height="170%"
|
|
49
|
-
id="atomix-glass-filter-
|
|
49
|
+
id="atomix-glass-filter-_r_f_"
|
|
50
50
|
width="170%"
|
|
51
51
|
x="-35%"
|
|
52
52
|
y="-35%"
|
|
@@ -54,7 +54,7 @@ exports[`AtomixGlass Visual Regression > matches snapshot with default props 1`]
|
|
|
54
54
|
<feimage
|
|
55
55
|
height="100%"
|
|
56
56
|
href="data:image/jpeg;base64,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cUAUAkEkziomgGgkoxZGgxZFQFQlYnQHRdPfj/10KCSCKESCNiVkViPSLpD0h6I0Q0I0A2IoBWBIJIBKBIJoJIJ2R2J0JWBUJ0JUB0XTv479dFZDYiglYigkhEgjZFQjRFQjRFQjQigFYigHYigmgEgmglYlYnQlQlYlQHQlQnQ9P/kf1yVkNiNCNkNiVENiNiViNEViNkVCVgKCViViViSCViSCVgdCViVCViVCdgVCVCdD1D+U/XBWQ2I0I2Q2JUQ2I0JWQ0I2JUQ2JUI2JUI2J0JWJWJWA2R0BWJ0I2JUJ2BUJUJ0P//EABkQAQEBAQEBAAAAAAAAAAAAAAECABEDEP/aAAgBAQABAgB1atWrVq1atWrVq1atWrVq1atWrVq1atWrVq+OrVq1atWrVq1atWrVq1atWrVq1atWrVq1atXxVppppppdWrVq1atWrVq1NNNNNNNNNNNPVWmmmmms6tWrVq1atWpppppppppppppp6q0000uc51atWrVq1ammmmmmmmmmmmmt1Vpppc5znVq1atWrVqaaaaaaaaaaaaaeqtNLnOc51atWrVq1ammmmmmmmmmmmmnqrS5znOc6tWrVq16222mmmmmmlVppp6tKuc5znOrVq1a9TbbbbTTTTTSq000qtLnOc5zq1atWrW0222200000qqqtKqrnOc5zq1atTbbbbbbbbTTTSqqqqqq5znOc6tTTTbbbbbbbbTTTSqqqqrlVznOctNNNtttttttttNNNNKqqqrqznKqrTTTTbbbbbbbbbTTTSqqqqrqznOc5aaaabbbbbbbbbaaaaVVVVVdWc5znVq1NNttttttttttNNKqqqqudWc5znVq16tbbbbbbbbbbTTSqqqq5XVnOc6tWrVrb1tttttttttNNKqqqqrWrK5VWmmm2230bbbbbbaaaXOc5zlVa1KuVVppptttt9G22222mmlzlVznK6tWVVWmmmm2222222222mlznOc5znLWppVVWmmm22222229bTWrOc5znOcq1qaaVpWmm222222229erVqznOc5znKtatStK0rTbTTbbbberXr1as5znOc5aVpppppWlabaabbbb1ta9WrVnOc5znU0rTTTTTTTTTbTTbbbTWvVq1as5znOdTTStNNNNNNNNNtNNtttN6tWvVq1ZznOrU00rTTTTTTTTTTTTTbTWvVq1atWrOc6tTTTStNNNNNNNNNNtNNtNa9WrVq1Z1Z1NNNNNK1q1NNNNNNNNNNNtNatWrVq1atWrU00000rWrVq1atWrVq1alaaa1atWrVq1NNNammmmla1atWrVq1aterVq16tWrVnVqa1NK1qaaaVX/xAAWEAADAAAAAAAAAAAAAAAAAAAhgJD/2gAIAQEAAz8AaExf/8QAGhEBAQEBAQEBAAAAAAAAAAAAAQISEQADEP/aAAgBAgEBAgDx48ePHjx48ePHjx48ePHjx48ePHjx48ePHj86IiIiIiInjx48ePHjx48IiIiIj0oooooooooRERER73ve60UUUUUUVrWiiiiiihERERER73ve97ooooorRWiiiiihKERERER73ve973RRRRWtFFFFFFCIiIiIiPe973ve60UUVrRRRRRRQiIlCIiI973ve973pRRWiiiiiiiiiiiiiiihEe973ve973RRWtFFFFFFFFFFFFFFFFFFa13ve973WitaKKKKKKKKKKKKKKKKKK1rWtd1rutFa1oooooooooooosssooorWta1rWta1rRRRRRRRRRRZZZZZZZZZWta1rWta1rRRRRRRRRZZZZZZZZZZZZe9a1rWta1rWitaKLLLLLLLLLLLLLLLLL3rWta1rWtFbLLLLLLLLLLLLLLLLLLLL3vWta1rWita1ssssssss+hZZZZZZZZe961rWta0Vre97LLLLLLLLLLLPoWWWWWXrWta1oorWta3ssss+hZZZZ9Cyyyyyyyyiita1orWta1ve9llllllllllllllllFFa0VorWta1ve9llllllllllllllllllFFFaK1rWta1rWiyyyyyyyyyyyyiiiiiiitFFa1rWta1oosoosssssoooosoooorRRRWta1rWta0UUUUUWUUUUUUUUUUUVoooorWta1rWtaKKKKKKmiiiiiiiiiiiiiiitd73ve61oSiiipoqaKKKKKKKKKK0UUUVrve973vREREZoSihEooooorRRRRWtd73ve9EREREREoSiiiiitFllllla73ve9ERERERESiiiiiitH0PoWWWWVrXe96IiIiMoiJRRRRRRWjwlFFllllFFd6IiIiIlCUUUUUUUUUePHjx48ePCIiIiIiIiUUUUUUUUUUUePHjx48ePHjx48ePHjx48IiUUUUUUJRRRX//xAAWEQADAAAAAAAAAAAAAAAAAAABYJD/2gAIAQIBAz8AtEV7/8QAFxEBAQEBAAAAAAAAAAAAAAAAAAECEP/aAAgBAwEBAgCtNNNNNNNNNNNNNNNNNNNNNNNNNNNNNcrTTTTTTTTTTTTTTTTTTTTTTTTTTTTTXKrTTTTTTTU000000000000000000001FVpppppqampqaaaaaaaaaaaaaaaaaaaa5Vaaaaampqampqammmmmmmmmmmlaaaaaaiq0001NTU1NTU1NTTTTTTTTTTSqqtNNNcqtNNSyzU1LNTU1NTTTTTTTTTSqqq001ytNLLLLNTU1NTU1NTbbbTTTTTSqqq001ytNLLLLLNTU1NTU3NttttNNNNNKqq001KrSyyyyyzU1NTU3Nzc02220000qqqqrSqqyyyyyzU1NTU3Nzc3NttttNNNKqqqqqqssssss1NTU3Nzc3NzbbbbTTTSqqqqqqrLLLLLNTU1Nzc3Nzc22220000qqqqqqqqssss1NTU3Nzc3NzbbbbbTTSqqqqqqqqqqzU1NTc3Nzc3Nzbc22000qqqqqqqqqqqtTU3Nzc3Nzc3NtzbTTSqqqqrKqqqqqtNNzc23Nzc3Nzc3NTU1KqqqrKqqqqqtNNNNttzc3Nzc3NzU1NLLLLLKqqqqqqqq0022223Nzc3NzU1NSyyyyyyqqqqqqqrTTbbbbc3Nzc3NTU1LLLLLLKsqqqqqqrTTTTbbbc3Nzc1NTUsssssssqqqqqqrTTTTTbbbTc3NTU1NTUsssssqqqqqqqq0000222023NTU1NTUsssssqqqqqqqq000000003NTU1NTU1LLLLLNKrTSqqqqtNNNNNNtNNTU1NSzUssss00qq0qqqqrTTTTTTTTTU1NTUs1LLLNNNKrTTTSqqq00000000001NTU1LNTU0000qtNNNKqqqtNNNNNNNNTU1NTUs1NNNNNKss1NNNK00qtK0000001NNTU0s000000qq000001NKrStNNNNK1NNNNStNNNNNKqtNNNNNNNK0000000rU0000rTTTTTSq00000rTTTTTTTTTTTTTTTTStNNNNKr/xAAUEQEAAAAAAAAAAAAAAAAAAACg/9oACAEDAQM/AAAf/9k="
|
|
57
|
-
id="atomix-glass-filter-
|
|
57
|
+
id="atomix-glass-filter-_r_f_-image"
|
|
58
58
|
preserveAspectRatio="xMidYMid slice"
|
|
59
59
|
result="DISPLACEMENT_MAP"
|
|
60
60
|
width="100%"
|
|
@@ -184,7 +184,7 @@ exports[`AtomixGlass Visual Regression > matches snapshot with default props 1`]
|
|
|
184
184
|
</svg>
|
|
185
185
|
<div
|
|
186
186
|
class="c-atomix-glass__filter-overlay"
|
|
187
|
-
style="filter: url(#atomix-glass-filter-
|
|
187
|
+
style="filter: url(#atomix-glass-filter-_r_f_);"
|
|
188
188
|
/>
|
|
189
189
|
<div
|
|
190
190
|
class="c-atomix-glass__filter-shadow"
|
|
@@ -205,6 +205,9 @@ exports[`AtomixGlass Visual Regression > matches snapshot with default props 1`]
|
|
|
205
205
|
<div
|
|
206
206
|
class="c-atomix-glass__background-layer c-atomix-glass__background-layer--black c-atomix-glass__background-layer--hidden"
|
|
207
207
|
/>
|
|
208
|
+
<span
|
|
209
|
+
class="c-atomix-glass__border-backdrop"
|
|
210
|
+
/>
|
|
208
211
|
<span
|
|
209
212
|
class="c-atomix-glass__border-1"
|
|
210
213
|
/>
|
|
@@ -173,7 +173,7 @@ export const extractBorderRadiusFromDOMElement = (element: HTMLElement | null):
|
|
|
173
173
|
/**
|
|
174
174
|
* Extract border-radius from React element
|
|
175
175
|
*/
|
|
176
|
-
export const extractBorderRadiusFromElement = (element: React.ReactElement): number | null => {
|
|
176
|
+
export const extractBorderRadiusFromElement = (element: React.ReactElement<any>): number | null => {
|
|
177
177
|
if (!element || !element.props) {
|
|
178
178
|
return null;
|
|
179
179
|
}
|
|
@@ -275,3 +275,53 @@ export const getDisplacementMap = (
|
|
|
275
275
|
return displacementMap;
|
|
276
276
|
}
|
|
277
277
|
};
|
|
278
|
+
|
|
279
|
+
// ─── Shader LRU Cache ──────────────────────────────────────────────────────
|
|
280
|
+
// Shared across all AtomixGlassContainer instances so identical shader
|
|
281
|
+
// configurations (same size + variant) are only generated once.
|
|
282
|
+
|
|
283
|
+
export const MAX_CACHE_SIZE = 15;
|
|
284
|
+
|
|
285
|
+
export interface ShaderCacheEntry {
|
|
286
|
+
url: string;
|
|
287
|
+
timestamp: number;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/** Module-level LRU cache shared by all container instances. */
|
|
291
|
+
export const sharedShaderCache = new Map<string, ShaderCacheEntry>();
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Retrieve a cached shader URL, updating its LRU timestamp.
|
|
295
|
+
* Returns `null` on a cache miss.
|
|
296
|
+
*/
|
|
297
|
+
export const getCachedShader = (key: string): string | null => {
|
|
298
|
+
const entry = sharedShaderCache.get(key);
|
|
299
|
+
if (entry) {
|
|
300
|
+
entry.timestamp = Date.now();
|
|
301
|
+
return entry.url;
|
|
302
|
+
}
|
|
303
|
+
return null;
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Store a shader URL in the LRU cache, evicting the oldest entry when full.
|
|
308
|
+
*/
|
|
309
|
+
export const setCachedShader = (key: string, url: string): void => {
|
|
310
|
+
if (sharedShaderCache.size >= MAX_CACHE_SIZE) {
|
|
311
|
+
const entries = Array.from(sharedShaderCache.entries());
|
|
312
|
+
entries.sort((a, b) => a[1].timestamp - b[1].timestamp);
|
|
313
|
+
const oldest = entries[0];
|
|
314
|
+
if (oldest) {
|
|
315
|
+
sharedShaderCache.delete(oldest[0]);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
sharedShaderCache.set(key, { url, timestamp: Date.now() });
|
|
319
|
+
|
|
320
|
+
if (typeof process === 'undefined' || process.env?.NODE_ENV !== 'production') {
|
|
321
|
+
if (sharedShaderCache.size >= MAX_CACHE_SIZE * 0.8) {
|
|
322
|
+
console.log(
|
|
323
|
+
`AtomixGlass: Shader cache size: ${String(sharedShaderCache.size).replace(/[\r\n]/g, '')}/${String(MAX_CACHE_SIZE).replace(/[\r\n]/g, '')}`
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
};
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Phase 1 Animation Features Stories
|
|
3
3
|
*
|
|
4
4
|
* Demonstrates the new time-based animation system and multi-layer distortion effects
|
|
5
|
-
* implemented in Phase 1 of the AtomixGlass Feature Implementation
|
|
5
|
+
* implemented in Phase 1 of the AtomixGlass Feature Implementation roadMap.
|
|
6
6
|
*
|
|
7
7
|
* Features:
|
|
8
8
|
* - Feature 1.1: Time-Based Animation System
|
|
@@ -138,8 +138,9 @@ type Story = StoryObj<typeof meta>;
|
|
|
138
138
|
export const DefaultAnimation: Story = {
|
|
139
139
|
args: {
|
|
140
140
|
children: (
|
|
141
|
-
<div style={{ padding: '60px 40px'
|
|
141
|
+
<div className="u-text-center u-text-white" style={{ padding: '60px 40px' }}>
|
|
142
142
|
<div
|
|
143
|
+
className="u-mb-6"
|
|
143
144
|
style={{
|
|
144
145
|
fontSize: '64px',
|
|
145
146
|
marginBottom: '24px',
|
|
@@ -149,6 +150,7 @@ export const DefaultAnimation: Story = {
|
|
|
149
150
|
✨
|
|
150
151
|
</div>
|
|
151
152
|
<h2
|
|
153
|
+
className="u-mt-0 u-mb-3 u-text-5xl u-font-bold u-text-gradient-primary"
|
|
152
154
|
style={{
|
|
153
155
|
margin: '0 0 12px 0',
|
|
154
156
|
fontSize: '32px',
|
|
@@ -160,7 +162,7 @@ export const DefaultAnimation: Story = {
|
|
|
160
162
|
>
|
|
161
163
|
Time-Based Animation
|
|
162
164
|
</h2>
|
|
163
|
-
<p
|
|
165
|
+
<p className="u-m-0 u-text-base u-opacity-90">
|
|
164
166
|
Organic liquid glass effect with continuous flow
|
|
165
167
|
</p>
|
|
166
168
|
</div>
|
|
@@ -193,11 +195,11 @@ export const DefaultAnimation: Story = {
|
|
|
193
195
|
export const BreathingEffect: Story = {
|
|
194
196
|
args: {
|
|
195
197
|
children: (
|
|
196
|
-
<div style={{ padding: '40px'
|
|
197
|
-
<h2
|
|
198
|
+
<div className="u-text-center u-text-white" style={{ padding: '40px' }}>
|
|
199
|
+
<h2 className="u-mt-0 u-mb-2 u-text-xl u-font-semibold">
|
|
198
200
|
Breathing Glass
|
|
199
201
|
</h2>
|
|
200
|
-
<p
|
|
202
|
+
<p className="u-m-0 u-text-sm u-opacity-80">
|
|
201
203
|
Subtle pulse animation
|
|
202
204
|
</p>
|
|
203
205
|
</div>
|
|
@@ -229,8 +231,9 @@ export const BreathingEffect: Story = {
|
|
|
229
231
|
export const UltraDistortion: Story = {
|
|
230
232
|
args: {
|
|
231
233
|
children: (
|
|
232
|
-
<div style={{ padding: '60px 40px'
|
|
234
|
+
<div className="u-text-center u-text-white" style={{ padding: '60px 40px' }}>
|
|
233
235
|
<div
|
|
236
|
+
className="u-mx-auto u-mb-6 u-flex u-items-center u-justify-center u-rounded-lg"
|
|
234
237
|
style={{
|
|
235
238
|
width: '80px',
|
|
236
239
|
height: '80px',
|
|
@@ -246,10 +249,10 @@ export const UltraDistortion: Story = {
|
|
|
246
249
|
>
|
|
247
250
|
🌊
|
|
248
251
|
</div>
|
|
249
|
-
<h2
|
|
252
|
+
<h2 className="u-mt-0 u-mb-2 u-text-2xl u-font-bold">
|
|
250
253
|
Ultra Distortion
|
|
251
254
|
</h2>
|
|
252
|
-
<p
|
|
255
|
+
<p className="u-m-0 u-text-xs u-opacity-80">
|
|
253
256
|
7 octaves of FBM complexity
|
|
254
257
|
</p>
|
|
255
258
|
</div>
|
|
@@ -285,14 +288,15 @@ export const UltraDistortion: Story = {
|
|
|
285
288
|
export const MobileOptimized: Story = {
|
|
286
289
|
args: {
|
|
287
290
|
children: (
|
|
288
|
-
<div style={{ padding: '40px 32px'
|
|
289
|
-
<h3
|
|
291
|
+
<div className="u-text-white" style={{ padding: '40px 32px' }}>
|
|
292
|
+
<h3 className="u-mt-0 u-mb-2 u-text-lg u-font-semibold">
|
|
290
293
|
Mobile Optimized
|
|
291
294
|
</h3>
|
|
292
|
-
<p
|
|
295
|
+
<p className="u-m-0 u-text-xs u-opacity-85">
|
|
293
296
|
2 octaves • Low power
|
|
294
297
|
</p>
|
|
295
298
|
<div
|
|
299
|
+
className="u-mt-4 u-p-3 u-bg-white/10 u-rounded-md u-text-xs"
|
|
296
300
|
style={{
|
|
297
301
|
marginTop: '16px',
|
|
298
302
|
padding: '12px',
|
|
@@ -334,8 +338,9 @@ export const MobileOptimized: Story = {
|
|
|
334
338
|
export const SlowMotion: Story = {
|
|
335
339
|
args: {
|
|
336
340
|
children: (
|
|
337
|
-
<div style={{ padding: '60px 40px'
|
|
341
|
+
<div className="u-text-center u-text-white" style={{ padding: '60px 40px' }}>
|
|
338
342
|
<div
|
|
343
|
+
className="u-text-6xl u-mb-5"
|
|
339
344
|
style={{
|
|
340
345
|
fontSize: '56px',
|
|
341
346
|
marginBottom: '20px',
|
|
@@ -343,10 +348,10 @@ export const SlowMotion: Story = {
|
|
|
343
348
|
>
|
|
344
349
|
🧘
|
|
345
350
|
</div>
|
|
346
|
-
<h2
|
|
351
|
+
<h2 className="u-mt-0 u-mb-3 u-text-2xl u-font-semibold">
|
|
347
352
|
Slow Motion
|
|
348
353
|
</h2>
|
|
349
|
-
<p
|
|
354
|
+
<p className="u-m-0 u-text-base u-opacity-90">
|
|
350
355
|
Calming 0.3x speed
|
|
351
356
|
</p>
|
|
352
357
|
</div>
|
|
@@ -379,8 +384,9 @@ export const SlowMotion: Story = {
|
|
|
379
384
|
export const FastFlow: Story = {
|
|
380
385
|
args: {
|
|
381
386
|
children: (
|
|
382
|
-
<div style={{ padding: '60px 40px'
|
|
387
|
+
<div className="u-text-center u-text-white" style={{ padding: '60px 40px' }}>
|
|
383
388
|
<div
|
|
389
|
+
className="u-text-6xl u-mb-5"
|
|
384
390
|
style={{
|
|
385
391
|
fontSize: '56px',
|
|
386
392
|
marginBottom: '20px',
|
|
@@ -388,10 +394,10 @@ export const FastFlow: Story = {
|
|
|
388
394
|
>
|
|
389
395
|
⚡
|
|
390
396
|
</div>
|
|
391
|
-
<h2
|
|
397
|
+
<h2 className="u-mt-0 u-mb-3 u-text-2xl u-font-bold">
|
|
392
398
|
Fast Flow
|
|
393
399
|
</h2>
|
|
394
|
-
<p
|
|
400
|
+
<p className="u-m-0 u-text-base u-opacity-90">
|
|
395
401
|
Energetic 2.5x speed
|
|
396
402
|
</p>
|
|
397
403
|
</div>
|
|
@@ -424,11 +430,11 @@ export const FastFlow: Story = {
|
|
|
424
430
|
export const NoAnimation: Story = {
|
|
425
431
|
args: {
|
|
426
432
|
children: (
|
|
427
|
-
<div style={{ padding: '40px'
|
|
428
|
-
<h2
|
|
433
|
+
<div className="u-text-center u-text-white" style={{ padding: '40px' }}>
|
|
434
|
+
<h2 className="u-mt-0 u-mb-2 u-text-xl u-font-semibold">
|
|
429
435
|
Static Glass
|
|
430
436
|
</h2>
|
|
431
|
-
<p
|
|
437
|
+
<p className="u-m-0 u-text-sm u-opacity-80">
|
|
432
438
|
Time animations disabled
|
|
433
439
|
</p>
|
|
434
440
|
</div>
|
|
@@ -436,7 +442,7 @@ export const NoAnimation: Story = {
|
|
|
436
442
|
withTimeAnimation: false,
|
|
437
443
|
withMultiLayerDistortion: false,
|
|
438
444
|
mode: 'standard',
|
|
439
|
-
displacementScale:
|
|
445
|
+
displacementScale: 40,
|
|
440
446
|
blurAmount: 2,
|
|
441
447
|
saturation: 140,
|
|
442
448
|
borderRadius: 20,
|
|
@@ -459,11 +465,11 @@ export const NoAnimation: Story = {
|
|
|
459
465
|
export const CustomFBMParameters: Story = {
|
|
460
466
|
args: {
|
|
461
467
|
children: (
|
|
462
|
-
<div style={{ padding: '60px 40px'
|
|
463
|
-
<h2
|
|
468
|
+
<div className="u-text-white" style={{ padding: '60px 40px' }}>
|
|
469
|
+
<h2 className="u-mt-0 u-mb-3 u-text-2xl u-font-bold">
|
|
464
470
|
Custom FBM
|
|
465
471
|
</h2>
|
|
466
|
-
<div
|
|
472
|
+
<div className="u-text-sm u-opacity-85 u-leading-relaxed">
|
|
467
473
|
<div>Octaves: 6</div>
|
|
468
474
|
<div>Lacunarity: 2.5</div>
|
|
469
475
|
<div>Gain: 0.65</div>
|
|
@@ -501,12 +507,12 @@ export const CustomFBMParameters: Story = {
|
|
|
501
507
|
export const AccessibilityReducedMotion: Story = {
|
|
502
508
|
args: {
|
|
503
509
|
children: (
|
|
504
|
-
<div style={{ padding: '40px'
|
|
505
|
-
<div
|
|
506
|
-
<h2
|
|
510
|
+
<div className="u-text-center u-text-white" style={{ padding: '40px' }}>
|
|
511
|
+
<div className="u-text-5xl u-mb-4">♿</div>
|
|
512
|
+
<h2 className="u-mt-0 u-mb-2 u-text-lg u-font-semibold">
|
|
507
513
|
Reduced Motion
|
|
508
514
|
</h2>
|
|
509
|
-
<p
|
|
515
|
+
<p className="u-m-0 u-text-sm u-opacity-80">
|
|
510
516
|
Respects accessibility preferences
|
|
511
517
|
</p>
|
|
512
518
|
</div>
|
|
@@ -537,7 +543,7 @@ export const AccessibilityReducedMotion: Story = {
|
|
|
537
543
|
*/
|
|
538
544
|
export const PerformanceComparison: Story = {
|
|
539
545
|
render: () => (
|
|
540
|
-
<div style={{
|
|
546
|
+
<div className="u-grid u-gap-6" style={{ gridTemplateColumns: 'repeat(2, 1fr)' }}>
|
|
541
547
|
{/* Low Quality */}
|
|
542
548
|
<div>
|
|
543
549
|
<AtomixGlass
|
|
@@ -552,12 +558,12 @@ export const PerformanceComparison: Story = {
|
|
|
552
558
|
borderRadius={20}
|
|
553
559
|
height={200}
|
|
554
560
|
>
|
|
555
|
-
<div style={{ padding: '24px', color: 'white', textAlign: 'center' }}>
|
|
556
|
-
<div
|
|
557
|
-
<h3
|
|
561
|
+
<div className="u-p-6 u-text-center u-text-white" style={{ padding: '24px', color: 'white', textAlign: 'center' }}>
|
|
562
|
+
<div className="u-text-4xl u-mb-3">📱</div>
|
|
563
|
+
<h3 className="u-mt-0 u-mb-1 u-text-sm u-font-semibold">
|
|
558
564
|
Low
|
|
559
565
|
</h3>
|
|
560
|
-
<p
|
|
566
|
+
<p className="u-m-0 u-text-xs u-opacity-80">
|
|
561
567
|
2 octaves • Mobile
|
|
562
568
|
</p>
|
|
563
569
|
</div>
|
|
@@ -578,12 +584,12 @@ export const PerformanceComparison: Story = {
|
|
|
578
584
|
borderRadius={20}
|
|
579
585
|
height={200}
|
|
580
586
|
>
|
|
581
|
-
<div style={{ padding: '24px', color: 'white', textAlign: 'center' }}>
|
|
582
|
-
<div
|
|
583
|
-
<h3
|
|
587
|
+
<div className="u-p-6 u-text-center u-text-white" style={{ padding: '24px', color: 'white', textAlign: 'center' }}>
|
|
588
|
+
<div className="u-text-4xl u-mb-3">💻</div>
|
|
589
|
+
<h3 className="u-mt-0 u-mb-1 u-text-sm u-font-semibold">
|
|
584
590
|
Medium
|
|
585
591
|
</h3>
|
|
586
|
-
<p
|
|
592
|
+
<p className="u-m-0 u-text-xs u-opacity-80">
|
|
587
593
|
4 octaves • Tablet
|
|
588
594
|
</p>
|
|
589
595
|
</div>
|
|
@@ -604,12 +610,12 @@ export const PerformanceComparison: Story = {
|
|
|
604
610
|
borderRadius={20}
|
|
605
611
|
height={200}
|
|
606
612
|
>
|
|
607
|
-
<div style={{ padding: '24px', color: 'white', textAlign: 'center' }}>
|
|
608
|
-
<div
|
|
609
|
-
<h3
|
|
613
|
+
<div className="u-p-6 u-text-center u-text-white" style={{ padding: '24px', color: 'white', textAlign: 'center' }}>
|
|
614
|
+
<div className="u-text-4xl u-mb-3">🖥️</div>
|
|
615
|
+
<h3 className="u-mt-0 u-mb-1 u-text-sm u-font-semibold">
|
|
610
616
|
High
|
|
611
617
|
</h3>
|
|
612
|
-
<p
|
|
618
|
+
<p className="u-m-0 u-text-xs u-opacity-80">
|
|
613
619
|
5 octaves • Desktop
|
|
614
620
|
</p>
|
|
615
621
|
</div>
|
|
@@ -630,12 +636,12 @@ export const PerformanceComparison: Story = {
|
|
|
630
636
|
borderRadius={20}
|
|
631
637
|
height={200}
|
|
632
638
|
>
|
|
633
|
-
<div style={{ padding: '24px', color: 'white', textAlign: 'center' }}>
|
|
634
|
-
<div
|
|
635
|
-
<h3
|
|
639
|
+
<div className="u-p-6 u-text-center u-text-white" style={{ padding: '24px', color: 'white', textAlign: 'center' }}>
|
|
640
|
+
<div className="u-text-4xl u-mb-3">🚀</div>
|
|
641
|
+
<h3 className="u-mt-0 u-mb-1 u-text-sm u-font-semibold">
|
|
636
642
|
Ultra
|
|
637
643
|
</h3>
|
|
638
|
-
<p
|
|
644
|
+
<p className="u-m-0 u-text-xs u-opacity-80">
|
|
639
645
|
7 octaves • High-end
|
|
640
646
|
</p>
|
|
641
647
|
</div>
|