├── .env.development ├── .env.production ├── .eslintignore ├── .gitignore ├── .husky └── pre-commit ├── .nvmrc ├── .prettierignore ├── .prettierrc.json ├── .vscode ├── launch.json ├── settings.json └── tasks.json ├── LICENSE.md ├── README.md ├── apps ├── README.md ├── dashboard-demo │ ├── .eslintrc.json │ ├── .gitignore │ ├── README.md │ ├── next.config.js │ ├── package.json │ ├── postcss.config.js │ ├── seed.js │ ├── src │ │ └── app │ │ │ ├── LiveDataUpdater.tsx │ │ │ ├── NoDocumentError.tsx │ │ │ ├── api │ │ │ └── documents │ │ │ │ └── route.ts │ │ │ ├── assets │ │ │ └── [id] │ │ │ │ └── page.tsx │ │ │ ├── components │ │ │ ├── AppProviders.tsx │ │ │ ├── Button.tsx │ │ │ ├── EasyblocksContent.tsx │ │ │ ├── EasyblocksEditorDialog.tsx │ │ │ └── EventListener.tsx │ │ │ ├── easyblocks-editor │ │ │ └── page.tsx │ │ │ ├── easyblocks │ │ │ ├── buildAppShellContent.ts │ │ │ ├── components.tsx │ │ │ ├── components │ │ │ │ ├── AppShell │ │ │ │ │ ├── AppShell.client.tsx │ │ │ │ │ ├── AppShell.ts │ │ │ │ │ ├── AppShellFooter │ │ │ │ │ │ ├── AppShellFooter.client.tsx │ │ │ │ │ │ └── AppShellFooter.ts │ │ │ │ │ ├── AppShellHeader │ │ │ │ │ │ ├── AppShellHeader.client.tsx │ │ │ │ │ │ ├── AppShellHeader.ts │ │ │ │ │ │ ├── HeaderLink │ │ │ │ │ │ │ ├── HeaderLink.definition.ts │ │ │ │ │ │ │ └── HeaderLink.tsx │ │ │ │ │ │ └── NicePng_snail-png_1032036.png │ │ │ │ │ └── AppShellSidebar │ │ │ │ │ │ ├── AppShellSidebar.client.tsx │ │ │ │ │ │ ├── AppShellSidebar.ts │ │ │ │ │ │ └── SidebarItem │ │ │ │ │ │ ├── SidebarItem.client.tsx │ │ │ │ │ │ └── SidebarItem.ts │ │ │ │ ├── AssetLink │ │ │ │ │ ├── AssetLink.client.tsx │ │ │ │ │ └── AssetLink.ts │ │ │ │ ├── AssetScreenContent │ │ │ │ │ └── AssetsScreenContent.definition.ts │ │ │ │ ├── Button │ │ │ │ │ ├── Button.client.tsx │ │ │ │ │ └── Button.ts │ │ │ │ ├── ButtonsGroup │ │ │ │ │ ├── ButtonsGroup.client.tsx │ │ │ │ │ └── ButtonsGroup.ts │ │ │ │ ├── HorizontalLayout │ │ │ │ │ ├── HorizontalLayout.client.tsx │ │ │ │ │ └── HorizontalLayout.ts │ │ │ │ ├── Image │ │ │ │ │ ├── Image.client.tsx │ │ │ │ │ └── Image.ts │ │ │ │ ├── Link │ │ │ │ │ ├── Link.client.tsx │ │ │ │ │ └── Link.ts │ │ │ │ ├── MainArea │ │ │ │ │ ├── MainArea.client.tsx │ │ │ │ │ ├── MainArea.ts │ │ │ │ │ └── PanelCard │ │ │ │ │ │ ├── PanelCard.client.tsx │ │ │ │ │ │ ├── PanelCard.ts │ │ │ │ │ │ ├── PropertiesForm │ │ │ │ │ │ ├── PropertiesForm.client.tsx │ │ │ │ │ │ ├── PropertiesForm.ts │ │ │ │ │ │ ├── PropertiesFormField │ │ │ │ │ │ │ ├── PropertiesFormField.client.tsx │ │ │ │ │ │ │ └── PropertiesFormField.ts │ │ │ │ │ │ ├── SubmitButtonAction.client.tsx │ │ │ │ │ │ ├── SubmitButtonAction.ts │ │ │ │ │ │ ├── UpdateAssetFormAction.client.tsx │ │ │ │ │ │ └── UpdateAssetFormAction.ts │ │ │ │ │ │ └── PropertiesGroup │ │ │ │ │ │ ├── PropertiesGroup.client.tsx │ │ │ │ │ │ ├── PropertiesGroup.ts │ │ │ │ │ │ └── PropertyItem │ │ │ │ │ │ ├── PropertyItem.client.tsx │ │ │ │ │ │ └── PropertyItem.ts │ │ │ │ ├── OpenDialog │ │ │ │ │ ├── DialogContent │ │ │ │ │ │ ├── DialogContent.client.tsx │ │ │ │ │ │ └── DialogContent.ts │ │ │ │ │ ├── OpenDialog.client.tsx │ │ │ │ │ └── OpenDialog.ts │ │ │ │ ├── Separator │ │ │ │ │ ├── Separator.client.tsx │ │ │ │ │ └── Separator.ts │ │ │ │ ├── Stack │ │ │ │ │ ├── Stack.client.tsx │ │ │ │ │ └── Stack.ts │ │ │ │ ├── TriggerEvent │ │ │ │ │ ├── TriggerEvent.client.tsx │ │ │ │ │ └── TriggerEvent.ts │ │ │ │ ├── WelcomeScreenContent │ │ │ │ │ └── WelcomeScreenContent.definition.ts │ │ │ │ ├── createButtonComponent.tsx │ │ │ │ ├── createButtonDefinition.ts │ │ │ │ └── types.ts │ │ │ ├── easyblocks.config.ts │ │ │ ├── fetchExternalData.ts │ │ │ └── types │ │ │ │ └── asset │ │ │ │ ├── AssetPicker.tsx │ │ │ │ └── fetch.ts │ │ │ ├── edit │ │ │ ├── RemoveDocumentButton.tsx │ │ │ └── page.tsx │ │ │ ├── favicon.ico │ │ │ ├── globals.css │ │ │ ├── layout.tsx │ │ │ └── page.tsx │ ├── tailwind.config.ts │ └── tsconfig.json ├── design-system-app │ ├── .gitignore │ ├── README.md │ ├── next.config.js │ ├── package.json │ ├── pages │ │ ├── _app.tsx │ │ ├── _document.tsx │ │ ├── api │ │ │ └── hello.ts │ │ └── index.tsx │ ├── public │ │ ├── favicon.ico │ │ ├── next.svg │ │ ├── thirteen.svg │ │ └── vercel.svg │ ├── src │ │ ├── BasicRowStories.tsx │ │ ├── ButtonStories.tsx │ │ ├── ColorStories.tsx │ │ ├── FormStories.tsx │ │ ├── InputStories.tsx │ │ ├── ModalStories.tsx │ │ ├── MultiSelectStories.tsx │ │ ├── NavigationControllerStories.tsx │ │ ├── RangeSliderStories.tsx │ │ ├── SelectStories.tsx │ │ ├── ToastStories.tsx │ │ ├── ToggleButtonStories.tsx │ │ ├── ToggleStories.tsx │ │ └── TypographyStories.tsx │ ├── styles │ │ ├── Home.module.css │ │ └── globals.css │ └── tsconfig.json ├── liquid-demo │ ├── .eslintrc.json │ ├── .gitignore │ ├── README.md │ ├── next.config.js │ ├── package.json │ ├── postcss.config.js │ ├── src │ │ └── app │ │ │ ├── easyblocks-editor │ │ │ ├── layout.tsx │ │ │ └── page.tsx │ │ │ ├── favicon.ico │ │ │ └── globals.css │ ├── tailwind.config.js │ └── tsconfig.json ├── page-builder-demo │ ├── .eslintrc.json │ ├── .gitignore │ ├── README.md │ ├── next.config.js │ ├── package.json │ ├── postcss.config.js │ ├── public │ │ ├── next.svg │ │ └── vercel.svg │ ├── src │ │ ├── app │ │ │ ├── (system) │ │ │ │ ├── layout.tsx │ │ │ │ ├── not-found.tsx │ │ │ │ ├── page.tsx │ │ │ │ └── page │ │ │ │ │ └── [documentId] │ │ │ │ │ ├── EasyblocksContent.tsx │ │ │ │ │ └── page.tsx │ │ │ ├── DocumenWidgetInline.tsx │ │ │ ├── easyblocks-editor │ │ │ │ ├── layout.tsx │ │ │ │ └── page.tsx │ │ │ ├── easyblocks │ │ │ │ ├── components.tsx │ │ │ │ ├── components │ │ │ │ │ ├── AlertAction │ │ │ │ │ │ ├── AlertAction.definition.ts │ │ │ │ │ │ └── AlertAction.tsx │ │ │ │ │ ├── BannerCard │ │ │ │ │ │ ├── BannerCard.auto.ts │ │ │ │ │ │ ├── BannerCard.definition.ts │ │ │ │ │ │ ├── BannerCard.tsx │ │ │ │ │ │ └── CoverCard │ │ │ │ │ │ │ ├── CoverCard.definition.ts │ │ │ │ │ │ │ └── CoverCard.tsx │ │ │ │ │ ├── BannerSection │ │ │ │ │ │ ├── BannerSection.definition.ts │ │ │ │ │ │ └── BannerSection.tsx │ │ │ │ │ ├── Button │ │ │ │ │ │ ├── Button.definition.ts │ │ │ │ │ │ ├── Button.styles.ts │ │ │ │ │ │ └── Button.tsx │ │ │ │ │ ├── ButtonGroup │ │ │ │ │ │ ├── ButtonGroup.definition.ts │ │ │ │ │ │ ├── ButtonGroup.styles.ts │ │ │ │ │ │ └── ButtonGroup.tsx │ │ │ │ │ ├── Code │ │ │ │ │ │ ├── Code.definition.ts │ │ │ │ │ │ └── Code.tsx │ │ │ │ │ ├── Grid │ │ │ │ │ │ ├── Grid.auto.ts │ │ │ │ │ │ ├── Grid.definition.ts │ │ │ │ │ │ ├── Grid.styles.ts │ │ │ │ │ │ ├── Grid.tsx │ │ │ │ │ │ ├── Grid.types.ts │ │ │ │ │ │ └── gridHelpers.ts │ │ │ │ │ ├── Image │ │ │ │ │ │ ├── Image.definition.ts │ │ │ │ │ │ ├── Image.styles.ts │ │ │ │ │ │ ├── Image.tsx │ │ │ │ │ │ ├── ImageRenderer.tsx │ │ │ │ │ │ └── Placeholder.tsx │ │ │ │ │ ├── Link │ │ │ │ │ │ ├── Link.definition.ts │ │ │ │ │ │ └── Link.tsx │ │ │ │ │ ├── ProductCard │ │ │ │ │ │ ├── Media │ │ │ │ │ │ │ ├── Image.tsx │ │ │ │ │ │ │ └── Media.tsx │ │ │ │ │ │ ├── ProductCard.definition.ts │ │ │ │ │ │ └── ProductCard.tsx │ │ │ │ │ ├── RootSectionStack │ │ │ │ │ │ ├── RootSectionStack.definition.ts │ │ │ │ │ │ ├── RootSectionStack.styles.ts │ │ │ │ │ │ └── RootSectionStack.tsx │ │ │ │ │ ├── SimpleBanner │ │ │ │ │ │ ├── SimpleBanner.definition.ts │ │ │ │ │ │ └── SimpleBanner.tsx │ │ │ │ │ ├── SolidColor │ │ │ │ │ │ ├── SolidColor.definition.ts │ │ │ │ │ │ ├── SolidColor.styles.ts │ │ │ │ │ │ └── SolidColor.tsx │ │ │ │ │ ├── Stack │ │ │ │ │ │ ├── Stack.definition.ts │ │ │ │ │ │ ├── Stack.styles.ts │ │ │ │ │ │ ├── Stack.tsx │ │ │ │ │ │ └── Stack.types.ts │ │ │ │ │ ├── TextLink │ │ │ │ │ │ ├── TextLink.definition.ts │ │ │ │ │ │ └── TextLink.tsx │ │ │ │ │ ├── TwoCards │ │ │ │ │ │ ├── TwoCards.auto.ts │ │ │ │ │ │ ├── TwoCards.change.ts │ │ │ │ │ │ ├── TwoCards.definition.ts │ │ │ │ │ │ ├── TwoCards.styles.ts │ │ │ │ │ │ ├── TwoCards.tsx │ │ │ │ │ │ ├── TwoCards.types.ts │ │ │ │ │ │ └── twoCardsConstants.ts │ │ │ │ │ ├── Video │ │ │ │ │ │ ├── Video.definition.ts │ │ │ │ │ │ ├── Video.styles.tsx │ │ │ │ │ │ ├── Video.tsx │ │ │ │ │ │ └── VideoRenderer.tsx │ │ │ │ │ ├── VimeoPlayer │ │ │ │ │ │ ├── VimeoPlayer.definition.ts │ │ │ │ │ │ ├── VimeoPlayer.styles.ts │ │ │ │ │ │ ├── VimeoPlayer.tsx │ │ │ │ │ │ └── VimeoPlayerInternal.tsx │ │ │ │ │ └── utils │ │ │ │ │ │ ├── borders.ts │ │ │ │ │ │ ├── cleanupIconSVG.ts │ │ │ │ │ │ ├── corners.ts │ │ │ │ │ │ ├── decomposeValues.ts │ │ │ │ │ │ ├── getFieldProvider.ts │ │ │ │ │ │ ├── parseAspectRatio.ts │ │ │ │ │ │ ├── pxValueNormalize.ts │ │ │ │ │ │ ├── responsiveAuto.ts │ │ │ │ │ │ ├── sectionWrapper │ │ │ │ │ │ ├── SectionWrapper.tsx │ │ │ │ │ │ └── sectionWrapperHelpers.ts │ │ │ │ │ │ └── toStartEnd.ts │ │ │ │ ├── easyblocks.config.ts │ │ │ │ ├── externalData │ │ │ │ │ ├── mockMedia │ │ │ │ │ │ ├── Media.ts │ │ │ │ │ │ ├── MediaPicker.tsx │ │ │ │ │ │ ├── MockImagePicker.tsx │ │ │ │ │ │ ├── MockVideoPicker.tsx │ │ │ │ │ │ ├── fetchMockImages.ts │ │ │ │ │ │ ├── fetchMockVideos.ts │ │ │ │ │ │ ├── mockAssets.ts │ │ │ │ │ │ ├── mockImageWidget.tsx │ │ │ │ │ │ └── mockVideoWidget.tsx │ │ │ │ │ ├── pexels │ │ │ │ │ │ ├── fetchPexelsResources.ts │ │ │ │ │ │ ├── pexelsImageWidget.tsx │ │ │ │ │ │ ├── pexelsShared.ts │ │ │ │ │ │ └── pexelsVideoWidget.tsx │ │ │ │ │ ├── product │ │ │ │ │ │ ├── fetchProductResources.ts │ │ │ │ │ │ ├── productShared.ts │ │ │ │ │ │ └── productWidget.tsx │ │ │ │ │ └── types.ts │ │ │ │ ├── myCustomFetch.ts │ │ │ │ ├── templates │ │ │ │ │ ├── basic │ │ │ │ │ │ ├── BannerSection2_empty.json │ │ │ │ │ │ ├── BasicCard_empty.json │ │ │ │ │ │ ├── Button_icon_standard.json │ │ │ │ │ │ ├── Button_standard.json │ │ │ │ │ │ ├── Button_standard_light.json │ │ │ │ │ │ ├── Button_text.json │ │ │ │ │ │ ├── Grid_empty.json │ │ │ │ │ │ ├── Slider_empty.json │ │ │ │ │ │ ├── TwoCards_empty.json │ │ │ │ │ │ └── video.json │ │ │ │ │ ├── nooma │ │ │ │ │ │ ├── BannerSection1 │ │ │ │ │ │ │ ├── NoomaBannerSection1.ts │ │ │ │ │ │ │ └── NoomaBannerSection1Entry.json │ │ │ │ │ │ ├── BannerSection2 │ │ │ │ │ │ │ ├── NoomaBannerSection2.ts │ │ │ │ │ │ │ └── NoomaBannerSection2Entry.json │ │ │ │ │ │ ├── BannerSection3 │ │ │ │ │ │ │ ├── NoomaBannerSection3.ts │ │ │ │ │ │ │ └── NoomaBannerSection3Entry.json │ │ │ │ │ │ ├── BannerSection4 │ │ │ │ │ │ │ ├── NoomaBannerSection4.ts │ │ │ │ │ │ │ └── NoomaBannerSection4Entry.json │ │ │ │ │ │ ├── BannerSection5 │ │ │ │ │ │ │ ├── NoomaBannerSection5.ts │ │ │ │ │ │ │ └── NoomaBannerSection5Entry.json │ │ │ │ │ │ ├── BasicCard1 │ │ │ │ │ │ │ ├── NoomaBasicCard1.ts │ │ │ │ │ │ │ └── NoomaBasicCard1Entry.json │ │ │ │ │ │ ├── BasicCard2 │ │ │ │ │ │ │ ├── NoomaBasicCard2.ts │ │ │ │ │ │ │ └── NoomaBasicCard2Entry.json │ │ │ │ │ │ ├── BasicCard3 │ │ │ │ │ │ │ ├── NoomaBasicCard3.ts │ │ │ │ │ │ │ └── NoomaBasicCard3Entry.json │ │ │ │ │ │ ├── BasicCard4 │ │ │ │ │ │ │ ├── NoomaBasicCard4.ts │ │ │ │ │ │ │ └── NoomaBasicCard4Entry.json │ │ │ │ │ │ ├── BasicCard5 │ │ │ │ │ │ │ ├── NoomaBasicCard5.ts │ │ │ │ │ │ │ └── NoomaBasicCard5Entry.json │ │ │ │ │ │ ├── Grid1 │ │ │ │ │ │ │ ├── NoomaGrid1.ts │ │ │ │ │ │ │ └── NoomaGrid1Entry.json │ │ │ │ │ │ ├── Grid2 │ │ │ │ │ │ │ ├── NoomaGrid2.ts │ │ │ │ │ │ │ └── NoomaGrid2Entry.json │ │ │ │ │ │ ├── Grid3 │ │ │ │ │ │ │ ├── NoomaGrid3.ts │ │ │ │ │ │ │ └── NoomaGrid3Entry.json │ │ │ │ │ │ ├── Grid4 │ │ │ │ │ │ │ ├── NoomaGrid4.ts │ │ │ │ │ │ │ └── NoomaGrid4Entry.json │ │ │ │ │ │ ├── Grid5 │ │ │ │ │ │ │ ├── NoomaGrid5.ts │ │ │ │ │ │ │ └── NoomaGrid5Entry.json │ │ │ │ │ │ ├── ProductCard1 │ │ │ │ │ │ │ ├── NoomaProductCard1.ts │ │ │ │ │ │ │ └── NoomaProductCard1Entry.json │ │ │ │ │ │ ├── Slider1 │ │ │ │ │ │ │ ├── NoomaSlider1.ts │ │ │ │ │ │ │ └── NoomaSlider1Entry.json │ │ │ │ │ │ ├── Slider2 │ │ │ │ │ │ │ ├── NoomaSlider2.ts │ │ │ │ │ │ │ └── NoomaSlider2Entry.json │ │ │ │ │ │ ├── TwoCards1 │ │ │ │ │ │ │ ├── NoomaTwoCards1.ts │ │ │ │ │ │ │ └── NoomaTwoCards1Entry.json │ │ │ │ │ │ ├── TwoCards2 │ │ │ │ │ │ │ ├── NoomaTwoCards2.ts │ │ │ │ │ │ │ └── NoomaTwoCards2Entry.json │ │ │ │ │ │ ├── TwoCards3 │ │ │ │ │ │ │ ├── NoomaTwoCards3.ts │ │ │ │ │ │ │ └── NoomaTwoCards3Entry.json │ │ │ │ │ │ └── TwoCards4 │ │ │ │ │ │ │ ├── NoomaTwoCards4.ts │ │ │ │ │ │ │ └── NoomaTwoCards4Entry.json │ │ │ │ │ ├── starterTemplate.json │ │ │ │ │ └── templates.ts │ │ │ │ ├── types │ │ │ │ │ └── UrlWidget.tsx │ │ │ │ └── utils │ │ │ │ │ ├── assert.ts │ │ │ │ │ ├── deepClone.ts │ │ │ │ │ ├── last.ts │ │ │ │ │ ├── range.ts │ │ │ │ │ └── useForceRerender.ts │ │ │ ├── favicon.ico │ │ │ ├── fonts │ │ │ │ ├── test-national-2-bold.woff2 │ │ │ │ ├── test-national-2-medium.woff2 │ │ │ │ ├── test-national-2-regular.woff2 │ │ │ │ └── test-soehne-mono-leicht.woff2 │ │ │ └── globals.css │ │ ├── data │ │ │ └── shopify │ │ │ │ ├── fetchProductById.ts │ │ │ │ ├── fetchProducts.ts │ │ │ │ ├── fetchProductsByIds.ts │ │ │ │ ├── fetchShopify.ts │ │ │ │ ├── formatPrice.ts │ │ │ │ ├── graphql │ │ │ │ ├── fetchProductsByIds.ts │ │ │ │ ├── fetchProductsQuery.ts │ │ │ │ ├── productFragment.ts │ │ │ │ └── variantFragment.ts │ │ │ │ ├── index.ts │ │ │ │ ├── loaders.ts │ │ │ │ ├── mapProduct.ts │ │ │ │ ├── removeEdges.ts │ │ │ │ └── types.ts │ │ └── utils │ │ │ └── generatePreviewUrl.ts │ ├── tailwind.config.js │ └── tsconfig.json └── quick-start │ ├── .eslintrc.json │ ├── .gitignore │ ├── README.md │ ├── next.config.js │ ├── package.json │ ├── postcss.config.js │ ├── src │ └── app │ │ ├── easyblocks-editor │ │ ├── layout.tsx │ │ └── page.tsx │ │ ├── favicon.ico │ │ └── globals.css │ ├── tailwind.config.js │ └── tsconfig.json ├── babel.config.json ├── base.tsconfig.json ├── docs ├── .gitbook │ └── assets │ │ ├── access_token.png │ │ ├── basic_properties.png │ │ ├── boolean_ui.gif │ │ ├── collection_ui.gif │ │ ├── color_ui.gif │ │ ├── component_collection_ui.gif │ │ ├── component_ui.gif │ │ ├── custom_input.gif │ │ ├── font_ui.gif │ │ ├── image (1).png │ │ ├── image (10).png │ │ ├── image (11).png │ │ ├── image (12).png │ │ ├── image (13).png │ │ ├── image (14).png │ │ ├── image (15).png │ │ ├── image (16).png │ │ ├── image (2).png │ │ ├── image (3).png │ │ ├── image (4).png │ │ ├── image (5).png │ │ ├── image (6).png │ │ ├── image (7).png │ │ ├── image (8).png │ │ ├── image (9).png │ │ ├── image.png │ │ ├── product_picker_demo_1.gif │ │ ├── product_widget_picker_simple (1).gif │ │ ├── product_widget_picker_simple.gif │ │ ├── select_ui.gif │ │ ├── space_ui.gif │ │ ├── string_ui.gif │ │ └── text_ui.gif ├── README.md ├── SUMMARY.md ├── essentials │ ├── backend.md │ ├── configuration.md │ ├── custom-types.md │ ├── editor-page.md │ ├── external-data.md │ ├── no-code-components │ │ ├── README.md │ │ ├── editing-function.md │ │ ├── schema.md │ │ └── styles-function.md │ ├── rendering-content.md │ └── templates.md └── getting-started.md ├── knip.json ├── lerna.json ├── migrations.json ├── nx.json ├── package.json ├── packages ├── .eslintrc.js ├── build-tools │ ├── package.json │ └── src │ │ └── index.js ├── core │ ├── .babelrc.json │ ├── LICENSE.md │ ├── api-extractor.json │ ├── globals.d.ts │ ├── jest.config.ts │ ├── package.json │ ├── rollup.config.js │ ├── src │ │ ├── EasyblocksBackend.ts │ │ ├── _internals.ts │ │ ├── box.ts │ │ ├── buildDocument.ts │ │ ├── buildEntry.ts │ │ ├── checkers.ts │ │ ├── compiler │ │ │ ├── CompilationCache.ts │ │ │ ├── applyAutoUsingResponsiveTokens.ts │ │ │ ├── areWidthsFullyDefined.ts │ │ │ ├── box.test.ts │ │ │ ├── box.ts │ │ │ ├── builtins │ │ │ │ ├── $richText │ │ │ │ │ ├── $richText.client.tsx │ │ │ │ │ ├── $richText.constants.ts │ │ │ │ │ ├── $richText.editor.tsx │ │ │ │ │ ├── $richText.styles.ts │ │ │ │ │ ├── $richText.ts │ │ │ │ │ ├── $richText.types.ts │ │ │ │ │ ├── $richTextBlockElement │ │ │ │ │ │ ├── $richTextBlockElement.client.tsx │ │ │ │ │ │ ├── $richTextBlockElement.styles.ts │ │ │ │ │ │ └── $richTextBlockElement.ts │ │ │ │ │ ├── $richTextLineElement │ │ │ │ │ │ ├── $richTextLineElement.client.tsx │ │ │ │ │ │ ├── $richTextLineElement.styles.ts │ │ │ │ │ │ └── $richTextLineElement.ts │ │ │ │ │ ├── $richTextPart │ │ │ │ │ │ ├── $richTextPart.client.tsx │ │ │ │ │ │ ├── $richTextPart.styles.ts │ │ │ │ │ │ └── $richTextPart.ts │ │ │ │ │ ├── builders.test.ts │ │ │ │ │ ├── builders.ts │ │ │ │ │ ├── convertTextComponentConfigToRichTextComponentConfig.test.ts │ │ │ │ │ ├── convertTextComponentConfigToRichTextComponentConfig.ts │ │ │ │ │ ├── getAbsoluteRichTextPartPath.ts │ │ │ │ │ ├── richTextEditorActions.test.ts │ │ │ │ │ ├── richTextEditorActions.ts │ │ │ │ │ ├── utils │ │ │ │ │ │ ├── convertEditorValueToRichTextElements.test.ts │ │ │ │ │ │ ├── convertEditorValueToRichTextElements.ts │ │ │ │ │ │ ├── convertRichTextElementsToEditorValue.test.ts │ │ │ │ │ │ ├── convertRichTextElementsToEditorValue.ts │ │ │ │ │ │ ├── createTemporaryEditor.ts │ │ │ │ │ │ ├── extractElementsFromCompiledComponents.ts │ │ │ │ │ │ ├── extractTextPartsFromCompiledComponents.ts │ │ │ │ │ │ ├── getEditorSelectionFromFocusedFields.test.ts │ │ │ │ │ │ ├── getEditorSelectionFromFocusedFields.ts │ │ │ │ │ │ ├── getFocusedFieldsFromSlateSelection.ts │ │ │ │ │ │ ├── getFocusedRichTextPartsConfigPaths.test.ts │ │ │ │ │ │ ├── getFocusedRichTextPartsConfigPaths.ts │ │ │ │ │ │ ├── getRichTextComponentConfigFragment.test.ts │ │ │ │ │ │ ├── getRichTextComponentConfigFragment.ts │ │ │ │ │ │ ├── parseRichTextPartConfigPath.test.ts │ │ │ │ │ │ ├── parseRichTextPartConfigPath.ts │ │ │ │ │ │ ├── stripRichTextTextPartSelection.ts │ │ │ │ │ │ └── traverseCompiledRichTextComponentConfig.ts │ │ │ │ │ └── withEasyblocks.ts │ │ │ │ ├── $text │ │ │ │ │ ├── $text.client.tsx │ │ │ │ │ ├── $text.editor.tsx │ │ │ │ │ ├── $text.styles.ts │ │ │ │ │ ├── $text.ts │ │ │ │ │ ├── InlineTextarea.tsx │ │ │ │ │ └── buildText.ts │ │ │ │ └── useTextValue.ts │ │ │ ├── compileComponent.ts │ │ │ ├── compileComponentValues.ts │ │ │ ├── compileFromSchema.ts │ │ │ ├── compileInternal.ts │ │ │ ├── configFindAllPaths.test.ts │ │ │ ├── configFindAllPaths.ts │ │ │ ├── configTraverse.test.ts │ │ │ ├── configTraverse.ts │ │ │ ├── createCompilationContext.test.ts │ │ │ ├── createCompilationContext.ts │ │ │ ├── definitions.ts │ │ │ ├── devices.ts │ │ │ ├── duplicateConfig.ts │ │ │ ├── findComponentDefinition.ts │ │ │ ├── flattenResponsiveStyles.test.ts │ │ │ ├── flattenResponsiveStyles.ts │ │ │ ├── getDeviceWidthPairs.test.ts │ │ │ ├── getDeviceWidthPairs.ts │ │ │ ├── getMostCommonValueFromRichTextParts.ts │ │ │ ├── index.ts │ │ │ ├── isContextEditorContext.ts │ │ │ ├── linearizeSpace.test.ts │ │ │ ├── linearizeSpace.ts │ │ │ ├── mergeCompilationMeta.ts │ │ │ ├── normalize.ts │ │ │ ├── normalizeInput.ts │ │ │ ├── parsePath.test.ts │ │ │ ├── parsePath.ts │ │ │ ├── public │ │ │ │ ├── compile.ts │ │ │ │ └── findResources.ts │ │ │ ├── resop.test.ts │ │ │ ├── resop.ts │ │ │ ├── schema │ │ │ │ ├── buttonSchemaProps.ts │ │ │ │ └── index.ts │ │ │ ├── schemaPropDefinitions.compile.test.ts │ │ │ ├── schemaPropDefinitions.getHash.test.ts │ │ │ ├── schemaPropDefinitions.test.ts │ │ │ ├── themeValueToResponsiveValue.test.ts │ │ │ ├── themeValueToResponsiveValue.ts │ │ │ ├── tinaFieldProviders.ts │ │ │ ├── traverseComponents.test.ts │ │ │ ├── traverseComponents.ts │ │ │ ├── types.ts │ │ │ ├── validate-color │ │ │ │ ├── index.test.js │ │ │ │ └── index.ts │ │ │ └── validation.ts │ │ ├── components │ │ │ ├── Box │ │ │ │ └── Box.tsx │ │ │ ├── ComponentBuilder │ │ │ │ └── ComponentBuilder.tsx │ │ │ ├── Easyblocks.tsx │ │ │ ├── EasyblocksExternalDataProvider.tsx │ │ │ ├── EasyblocksMetadataProvider.tsx │ │ │ ├── MissingComponent.tsx │ │ │ └── ssr.tsx │ │ ├── events.ts │ │ ├── index.ts │ │ ├── isCompiledComponentConfig.ts │ │ ├── isNoCodeComponentOfType.ts │ │ ├── locales.ts │ │ ├── resourcesUtils.ts │ │ ├── responsiveness │ │ │ ├── index.ts │ │ │ ├── isTrulyResponsiveValue.ts │ │ │ ├── resop.ts │ │ │ ├── responsiveValueAt.ts │ │ │ ├── responsiveValueDoesSatisfyCondition.test.ts │ │ │ ├── responsiveValueDoesSatisfyCondition.ts │ │ │ ├── responsiveValueEntries.ts │ │ │ ├── responsiveValueFill.test.ts │ │ │ ├── responsiveValueFill.ts │ │ │ ├── responsiveValueFindDeviceWithDefinedValue.ts │ │ │ ├── responsiveValueFlatten.test.ts │ │ │ ├── responsiveValueFlatten.ts │ │ │ ├── responsiveValueGet.ts │ │ │ ├── responsiveValueGetDefinedValue.test.ts │ │ │ ├── responsiveValueGetDefinedValue.ts │ │ │ ├── responsiveValueGetHighestDefinedDevice.test.ts │ │ │ ├── responsiveValueGetHighestDefinedDevice.ts │ │ │ ├── responsiveValueIsNotNested.test.ts │ │ │ ├── responsiveValueIsNotNested.ts │ │ │ ├── responsiveValueMap.test.ts │ │ │ ├── responsiveValueMap.ts │ │ │ ├── responsiveValueNormalize.test.ts │ │ │ ├── responsiveValueNormalize.ts │ │ │ ├── responsiveValueReduce.test.ts │ │ │ ├── responsiveValueReduce.ts │ │ │ ├── responsiveValueSet.test.ts │ │ │ ├── responsiveValueSet.ts │ │ │ ├── responsiveValueValues.ts │ │ │ └── types.ts │ │ ├── spacingToPx.ts │ │ ├── testUtils.ts │ │ └── types.ts │ └── tsconfig.json ├── design-system │ ├── .babelrc.json │ ├── LICENSE.md │ ├── package.json │ ├── rollup.config.js │ ├── src │ │ ├── ControlContainer.tsx │ │ ├── CustomComponentSymbol.tsx │ │ ├── FormElement.tsx │ │ ├── Input.tsx │ │ ├── Loader.tsx │ │ ├── Menu.tsx │ │ ├── MultiSelect.tsx │ │ ├── NavigationController │ │ │ └── NavigationController.tsx │ │ ├── Select │ │ │ └── Select.tsx │ │ ├── ShopstoryIcon.tsx │ │ ├── ShopstoryLogo.tsx │ │ ├── SimplePicker.tsx │ │ ├── Slider.tsx │ │ ├── Stack.tsx │ │ ├── Tabs.tsx │ │ ├── ThumbnailButton.tsx │ │ ├── Toaster.tsx │ │ ├── Toggle.tsx │ │ ├── ToggleButton.tsx │ │ ├── ToggleGroup │ │ │ └── ToggleGroup.tsx │ │ ├── Tooltip │ │ │ └── Tooltip.tsx │ │ ├── Typography.tsx │ │ ├── buttons.tsx │ │ ├── colors.tsx │ │ ├── fonts.tsx │ │ ├── icons.tsx │ │ ├── icons │ │ │ ├── add.svg │ │ │ ├── align-center.svg │ │ │ ├── align-left.svg │ │ │ ├── align-right.svg │ │ │ ├── back.svg │ │ │ ├── chevron-left.svg │ │ │ ├── close.svg │ │ │ ├── corner-radius.svg │ │ │ ├── desktop.svg │ │ │ ├── drag.svg │ │ │ ├── dropdown-1.svg │ │ │ ├── dropdown.svg │ │ │ ├── grid-3x3.svg │ │ │ ├── layout-horizontal.svg │ │ │ ├── layout-vertical.svg │ │ │ ├── link.svg │ │ │ ├── margin-bottom.svg │ │ │ ├── margin-horizontal.svg │ │ │ ├── margin-top.svg │ │ │ ├── master.svg │ │ │ ├── max-height.svg │ │ │ ├── mobile.svg │ │ │ ├── open-in-new.svg │ │ │ ├── redo.svg │ │ │ ├── remove.svg │ │ │ ├── reset.svg │ │ │ ├── right-1.svg │ │ │ ├── right.svg │ │ │ ├── share.svg │ │ │ ├── three-dots-horizontal.svg │ │ │ └── undo.svg │ │ ├── index.ts │ │ ├── modals │ │ │ └── Modal.tsx │ │ └── rows │ │ │ └── BasicRow.tsx │ ├── tsconfig.json │ └── typings │ │ └── styled-components.d.ts ├── editor │ ├── .babelrc.json │ ├── LICENSE.md │ ├── jest.config.ts │ ├── package.json │ ├── rollup.config.js │ ├── src │ │ ├── CanvasRoot │ │ │ └── CanvasRoot.tsx │ │ ├── ConfigAfterAutoContext.ts │ │ ├── EasyblocksEditor.tsx │ │ ├── EasyblocksEditorProps.ts │ │ ├── EasyblocksParent.tsx │ │ ├── EditableComponentBuilder │ │ │ ├── BlockControls.tsx │ │ │ ├── EditableComponentBuilder.editor.tsx │ │ │ └── SelectionFrameController.tsx │ │ ├── Editor.tsx │ │ ├── EditorChildWindow.tsx │ │ ├── EditorContext.ts │ │ ├── EditorExternalDataProvider.tsx │ │ ├── EditorHistory.ts │ │ ├── EditorIframe.tsx │ │ ├── EditorSidebar.tsx │ │ ├── EditorTopBar.tsx │ │ ├── ModalPicker.tsx │ │ ├── Placeholder.tsx │ │ ├── PreviewRenderer.tsx │ │ ├── SearchableSmallPickerModal.tsx │ │ ├── SectionPicker.tsx │ │ ├── SidebarFooter.tsx │ │ ├── TemplateModal.tsx │ │ ├── TemplatePicker.ts │ │ ├── buildTinaFields.ts │ │ ├── debug │ │ │ ├── DebugSection │ │ │ │ ├── DebugSection.definition.ts │ │ │ │ └── DebugSection.tsx │ │ │ ├── DebugUrlWidget.tsx │ │ │ ├── addDebugToEditorProps.ts │ │ │ ├── debugTokens.ts │ │ │ └── debugTypes.ts │ │ ├── editorActions.test.ts │ │ ├── editorActions.ts │ │ ├── form.ts │ │ ├── getAllComponentTypes.ts │ │ ├── getAllComponentsOfType.ts │ │ ├── index.ts │ │ ├── inline-settings.tsx │ │ ├── normalizeToStringArray.ts │ │ ├── parseQueryParams.ts │ │ ├── paste │ │ │ ├── destinationResolver.test.ts │ │ │ ├── destinationResolver.ts │ │ │ ├── insert.test.ts │ │ │ ├── insert.ts │ │ │ ├── manager.test.ts │ │ │ ├── manager.ts │ │ │ └── reconcile.ts │ │ ├── pathToCompiledPath.ts │ │ ├── selectionFrame │ │ │ ├── AddButton.tsx │ │ │ ├── SelectionFrame.styles.ts │ │ │ ├── SelectionFrame.tsx │ │ │ ├── calculateAddButtonProperties.test.ts │ │ │ ├── calculateAddButtonProperties.ts │ │ │ └── cssVariables.ts │ │ ├── sidebar │ │ │ ├── ColorTokenWidget.tsx │ │ │ ├── DocumentDataWidget.tsx │ │ │ ├── PositionPickerInput.tsx │ │ │ ├── SpaceTokenWidget.tsx │ │ │ └── validate-color │ │ │ │ ├── index.test.js │ │ │ │ └── index.ts │ │ ├── templates │ │ │ └── getTemplates.ts │ │ ├── tinacms │ │ │ ├── fields │ │ │ │ ├── components │ │ │ │ │ ├── Input.tsx │ │ │ │ │ ├── NumberInput.tsx │ │ │ │ │ ├── RadioGroup.tsx │ │ │ │ │ ├── Select.tsx │ │ │ │ │ ├── Toggle.tsx │ │ │ │ │ ├── constants.ts │ │ │ │ │ ├── getUniqueValues.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── isMixedFieldValue.ts │ │ │ │ ├── index.ts │ │ │ │ └── plugins │ │ │ │ │ ├── BlockFieldPlugin.tsx │ │ │ │ │ ├── ExternalField │ │ │ │ │ └── ExternalField.tsx │ │ │ │ │ ├── IdentityFieldPlugin.tsx │ │ │ │ │ ├── LocalFIeld.tsx │ │ │ │ │ ├── MissingWidget.tsx │ │ │ │ │ ├── NumberFieldPlugin.tsx │ │ │ │ │ ├── PositionFieldPlugin.tsx │ │ │ │ │ ├── RadioGroupFieldPlugin.tsx │ │ │ │ │ ├── ResponsiveField │ │ │ │ │ ├── ResponsiveFieldPlugin.tsx │ │ │ │ │ ├── responsiveFieldController.test.ts │ │ │ │ │ └── responsiveFieldController.ts │ │ │ │ │ ├── SVGPicker │ │ │ │ │ └── SVGPicker.tsx │ │ │ │ │ ├── SelectFieldPlugin.tsx │ │ │ │ │ ├── SliderFieldPlugin.tsx │ │ │ │ │ ├── TextFieldPlugin.tsx │ │ │ │ │ ├── ToggleFieldPlugin.tsx │ │ │ │ │ ├── TokenField │ │ │ │ │ └── TokenFieldPlugin.tsx │ │ │ │ │ ├── Tooltip.tsx │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── numberFormat.ts │ │ │ │ │ ├── textFormat.ts │ │ │ │ │ ├── useTooltip.ts │ │ │ │ │ └── wrapFieldWithMeta.tsx │ │ │ ├── form-builder │ │ │ │ ├── field-plugin.tsx │ │ │ │ ├── fields-builder.tsx │ │ │ │ ├── index.ts │ │ │ │ └── utils │ │ │ │ │ ├── createFieldController.test.ts │ │ │ │ │ ├── createFieldController.ts │ │ │ │ │ ├── mergeCommonFields.test.ts │ │ │ │ │ └── mergeCommonFields.ts │ │ │ ├── react-core │ │ │ │ ├── index.ts │ │ │ │ └── use-form.ts │ │ │ └── styles │ │ │ │ ├── Button.tsx │ │ │ │ ├── Styles.tsx │ │ │ │ └── index.ts │ │ ├── types.ts │ │ ├── unrollAcceptsFieldIntoComponents.ts │ │ ├── useDataSaver.test.tsx │ │ ├── useDataSaver.ts │ │ ├── useEditorGlobalKeyboardShortcuts.test.tsx │ │ ├── useEditorGlobalKeyboardShortcuts.ts │ │ ├── useEditorHistory.ts │ │ ├── useOnClickNTimes.ts │ │ ├── useWindowKeyDown.ts │ │ └── utils │ │ │ ├── config │ │ │ ├── configMap.test.ts │ │ │ ├── configMap.ts │ │ │ └── getConfigSnapshot.ts │ │ │ ├── isConfigPathRichTextPart.test.ts │ │ │ ├── isConfigPathRichTextPart.ts │ │ │ ├── locales │ │ │ ├── addLocalizedFlag.ts │ │ │ ├── checkLocalesCorrectness.test.ts │ │ │ ├── checkLocalesCorrectness.ts │ │ │ └── removeLocalizedFlag.ts │ │ │ └── tests │ │ │ ├── index.ts │ │ │ └── testUtils.ts │ ├── tsconfig.json │ └── typings │ │ ├── globals.d.ts │ │ └── styled-components.d.ts ├── reduce-css-calc │ ├── .babelrc │ ├── .editorconfig │ ├── .gitignore │ ├── CHANGELOG.md │ ├── LICENSE │ ├── README.md │ ├── index.d.ts │ ├── package.json │ ├── parser.jison │ └── src │ │ ├── __tests__ │ │ └── index.js │ │ ├── index.js │ │ ├── lib │ │ ├── convert.js │ │ ├── css-unit-converter.js │ │ ├── reducer.js │ │ └── stringifier.js │ │ └── parser.js ├── test-utils │ ├── package.json │ └── src │ │ └── index.ts └── utils │ ├── jest.config.ts │ ├── package.json │ ├── src │ ├── addEventListener.ts │ ├── array │ │ ├── bubbleDown.test.ts │ │ ├── bubbleDown.ts │ │ ├── includesAny.test.ts │ │ ├── includesAny.ts │ │ ├── index.ts │ │ ├── last.ts │ │ ├── mostCommon.test.ts │ │ ├── mostCommon.ts │ │ ├── nonNullable.ts │ │ ├── preOrderPathComparator.test.ts │ │ ├── preOrderPathComparator.ts │ │ ├── range.test.ts │ │ ├── range.ts │ │ ├── sum.ts │ │ └── toArray.ts │ ├── assert.ts │ ├── cleanString.test.ts │ ├── cleanString.ts │ ├── deepClone.ts │ ├── deepCompare.test.ts │ ├── deepCompare.ts │ ├── func │ │ ├── index.ts │ │ ├── omit.ts │ │ └── pick.ts │ ├── hooks │ │ └── useForceRerender.ts │ ├── index.ts │ ├── object │ │ ├── dotNotationGet.test.ts │ │ ├── dotNotationGet.ts │ │ ├── dotNotationSet.test.ts │ │ ├── dotNotationSet.ts │ │ ├── entries.ts │ │ ├── index.ts │ │ ├── keys.ts │ │ └── objectMap.ts │ ├── raiseError.ts │ ├── serialize.ts │ ├── sleep.ts │ └── uniqueId.ts │ └── tsconfig.json ├── pnpm-lock.yaml ├── pnpm-workspace.yaml ├── scripts ├── clean.sh └── nuke.sh └── tsconfig.json /.env.development: -------------------------------------------------------------------------------- 1 | EASYBLOCKS_API_URL=http://localhost:3100 2 | SHOPSTORY_INTERNAL_COMPILATION_DEBUG=false -------------------------------------------------------------------------------- /.env.production: -------------------------------------------------------------------------------- 1 | EASYBLOCKS_API_URL=https://app.easyblocks.io 2 | SHOPSTORY_INTERNAL_COMPILATION_DEBUG=false -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | built/ 2 | dist/ -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vercel 2 | node_modules/ 3 | .next/ 4 | 5 | .env 6 | .env.local 7 | 8 | # from tina 9 | *.log 10 | **/node_modules/ 11 | 12 | # temporarily removed, build of tina modifications from context of shopstory to be done 13 | **/build/ 14 | **/dist/ 15 | **/dist-types/ 16 | **/stats 17 | 18 | **/.rpt2_cache 19 | **/.rts2_cache 20 | **/.rts2_cache_cjs 21 | **/.rts2_cache_esm 22 | **/.rts2_cache_umd 23 | **/.rts2_cache_system 24 | *.swp 25 | packages/cli/forestry/.env 26 | *.tgz 27 | .DS_Store 28 | .idea/ 29 | **/.cache/ 30 | **/yarn.lock 31 | 32 | # playwrite 33 | **/test-results 34 | **/tests-out 35 | 36 | *.tsbuildinfo 37 | 38 | # jest 39 | **/coverage/ 40 | 41 | # Nx 42 | .nx/ 43 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx lint-staged 5 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | v16 -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | build 2 | dist 3 | .next 4 | node_modules 5 | pnpm-lock.yaml -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules/typescript/lib", 3 | "jest.jestCommandLine": "npx jest", 4 | "search.exclude": { 5 | "apps/**/.next/": true, 6 | "packages/**/dist/": true, 7 | "packages/**/dist-types/": true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /apps/dashboard-demo/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /apps/dashboard-demo/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env*.local 29 | 30 | # vercel 31 | .vercel 32 | 33 | # typescript 34 | *.tsbuildinfo 35 | next-env.d.ts 36 | -------------------------------------------------------------------------------- /apps/dashboard-demo/next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | images: { 4 | domains: ["picsum.photos"], 5 | }, 6 | }; 7 | 8 | module.exports = nextConfig; 9 | -------------------------------------------------------------------------------- /apps/dashboard-demo/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /apps/dashboard-demo/seed.js: -------------------------------------------------------------------------------- 1 | module.exports = () => { 2 | const mockAssets = Array.from({ length: 10 }, (_, i) => ({ 3 | name: `Asset ${i + 1}`, 4 | description: `Asset ${i + 1} description`, 5 | mimeType: "image/png", 6 | id: `${i + 1}`, 7 | type: "image", 8 | isDisabled: Boolean(i % 2), 9 | imageUrl: `https://picsum.photos/1000/1000?random=${i}`, 10 | updatedAt: new Date().toISOString(), 11 | })); 12 | 13 | return { 14 | assets: mockAssets, 15 | }; 16 | }; 17 | -------------------------------------------------------------------------------- /apps/dashboard-demo/src/app/LiveDataUpdater.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { useEffect } from "react"; 4 | import { useRouter } from "next/navigation"; 5 | 6 | // This component reloads external data every 5 seconds 7 | export function LiveDataUpdater() { 8 | const router = useRouter(); 9 | 10 | useEffect(() => { 11 | const interval = setInterval(() => { 12 | router.refresh(); 13 | }, 5 * 1000); 14 | 15 | return () => { 16 | clearInterval(interval); 17 | }; 18 | }, []); 19 | 20 | return null; 21 | } 22 | -------------------------------------------------------------------------------- /apps/dashboard-demo/src/app/NoDocumentError.tsx: -------------------------------------------------------------------------------- 1 | import Link from "next/link"; 2 | 3 | export const NoDocumentError: React.FC<{ documentName: string }> = (props) => { 4 | return ( 5 |
6 |
7 |

8 | To render this screen you must build UI for "{props.documentName} 9 | ". 10 |

11 |

12 | Go to{" "} 13 | 17 | /edit 18 | {" "} 19 | to build UI. 20 |

21 |
22 |
23 | ); 24 | }; 25 | -------------------------------------------------------------------------------- /apps/dashboard-demo/src/app/api/documents/route.ts: -------------------------------------------------------------------------------- 1 | import { cookies } from "next/headers"; 2 | import { NextRequest } from "next/server"; 3 | 4 | export async function POST(req: NextRequest) { 5 | const data = await req.json(); 6 | cookies().set(data.cookieId, data.id); 7 | return new Response(undefined, { 8 | status: 200, 9 | }); 10 | } 11 | 12 | export async function DELETE(req: NextRequest) { 13 | const data = await req.json(); 14 | cookies().delete(data.cookieId); 15 | return new Response(undefined, { 16 | status: 200, 17 | }); 18 | } 19 | -------------------------------------------------------------------------------- /apps/dashboard-demo/src/app/components/AppProviders.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { Theme } from "@radix-ui/themes"; 4 | import type { ReactNode } from "react"; 5 | 6 | function AppProviders({ children }: { children: ReactNode }) { 7 | return {children}; 8 | } 9 | 10 | export { AppProviders }; 11 | -------------------------------------------------------------------------------- /apps/dashboard-demo/src/app/components/EasyblocksContent.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { Easyblocks, easyblocksGetStyleTag } from "@easyblocks/core"; 4 | import { useServerInsertedHTML } from "next/navigation"; 5 | import { ComponentPropsWithoutRef } from "react"; 6 | 7 | function EasyblocksContent(props: ComponentPropsWithoutRef) { 8 | useServerInsertedHTML(() => { 9 | return easyblocksGetStyleTag(); 10 | }); 11 | 12 | return ; 13 | } 14 | 15 | export { EasyblocksContent }; 16 | -------------------------------------------------------------------------------- /apps/dashboard-demo/src/app/components/EventListener.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { useEffect } from "react"; 3 | import toast from "react-hot-toast"; 4 | 5 | function EventListener() { 6 | useEffect(() => { 7 | function showToast(event: MessageEvent) { 8 | if (event.data.type === "triggerEvent") { 9 | toast(event.data.message); 10 | } 11 | } 12 | 13 | window.addEventListener("message", showToast); 14 | 15 | return () => { 16 | window.removeEventListener("message", showToast); 17 | }; 18 | }, []); 19 | 20 | return null; 21 | } 22 | 23 | export { EventListener }; 24 | -------------------------------------------------------------------------------- /apps/dashboard-demo/src/app/easyblocks/buildAppShellContent.ts: -------------------------------------------------------------------------------- 1 | import { buildDocument } from "@easyblocks/core"; 2 | import { cookies } from "next/headers"; 3 | import "server-only"; 4 | import { easyblocksConfig } from "./easyblocks.config"; 5 | import { fetchExternalData } from "./fetchExternalData"; 6 | 7 | async function buildAppShellContent() { 8 | const cookieStore = cookies(); 9 | const appShellDocumentId = cookieStore.get("appShellDocumentId"); 10 | 11 | if (!appShellDocumentId) { 12 | return null; 13 | } 14 | 15 | const appShellDocument = await buildDocument({ 16 | documentId: appShellDocumentId.value, 17 | config: easyblocksConfig, 18 | locale: "en", 19 | }); 20 | 21 | const externalData = await fetchExternalData(appShellDocument.externalData); 22 | 23 | return { 24 | renderableDocument: appShellDocument.renderableDocument, 25 | externalData, 26 | }; 27 | } 28 | 29 | export { buildAppShellContent }; 30 | -------------------------------------------------------------------------------- /apps/dashboard-demo/src/app/easyblocks/components/AppShell/AppShell.client.tsx: -------------------------------------------------------------------------------- 1 | import { NoCodeComponentProps } from "@easyblocks/core"; 2 | 3 | function AppShell({ 4 | Header, 5 | Main, 6 | Sidebar, 7 | isSidebarHidden, 8 | }: NoCodeComponentProps & Record) { 9 | return ( 10 |
14 | 15 |
16 | {!isSidebarHidden && } 17 | 18 |
19 | {Main ? ( 20 | 21 | ) : ( 22 |
23 | Main content placeholder 24 |
25 | )} 26 |
27 |
28 |
29 | ); 30 | } 31 | 32 | export { AppShell }; 33 | -------------------------------------------------------------------------------- /apps/dashboard-demo/src/app/easyblocks/components/AppShell/AppShell.ts: -------------------------------------------------------------------------------- 1 | import { NoCodeComponentDefinition } from "@easyblocks/core"; 2 | 3 | const appShellDefinition: NoCodeComponentDefinition = { 4 | id: "AppShell", 5 | schema: [ 6 | { 7 | prop: "Header", 8 | type: "component", 9 | required: true, 10 | accepts: ["AppShellHeader"], 11 | }, 12 | { 13 | prop: "Main", 14 | type: "component", 15 | accepts: ["MainArea"], 16 | noInline: true, 17 | }, 18 | { 19 | prop: "Sidebar", 20 | type: "component", 21 | required: true, 22 | accepts: ["AppShellSidebar"], 23 | }, 24 | { 25 | prop: "isSidebarHidden", 26 | type: "boolean", 27 | label: "Hide sidebar", 28 | }, 29 | ], 30 | }; 31 | 32 | export { appShellDefinition }; 33 | -------------------------------------------------------------------------------- /apps/dashboard-demo/src/app/easyblocks/components/AppShell/AppShellFooter/AppShellFooter.client.tsx: -------------------------------------------------------------------------------- 1 | import { NoCodeComponentProps } from "../../types"; 2 | 3 | function AppShellFooter({ Items }: NoCodeComponentProps) { 4 | return ( 5 |
6 | 7 |
8 | ); 9 | } 10 | 11 | export { AppShellFooter }; 12 | -------------------------------------------------------------------------------- /apps/dashboard-demo/src/app/easyblocks/components/AppShell/AppShellFooter/AppShellFooter.ts: -------------------------------------------------------------------------------- 1 | import { NoCodeComponentDefinition } from "@easyblocks/core"; 2 | 3 | const appShellFooterDefinition: NoCodeComponentDefinition = { 4 | id: "AppShellFooter", 5 | schema: [ 6 | { 7 | prop: "Items", 8 | type: "component", 9 | required: true, 10 | accepts: ["Stack"], 11 | }, 12 | ], 13 | editing() { 14 | return { 15 | components: { 16 | Items: { 17 | selectable: false, 18 | }, 19 | }, 20 | }; 21 | }, 22 | }; 23 | 24 | export { appShellFooterDefinition }; 25 | -------------------------------------------------------------------------------- /apps/dashboard-demo/src/app/easyblocks/components/AppShell/AppShellHeader/HeaderLink/HeaderLink.definition.ts: -------------------------------------------------------------------------------- 1 | import { NoCodeComponentDefinition } from "@easyblocks/core"; 2 | import { createButtonDefinition } from "../../../createButtonDefinition"; 3 | 4 | const headerLinkDefinition: NoCodeComponentDefinition = createButtonDefinition({ 5 | id: "HeaderLink", 6 | schema: [ 7 | { 8 | prop: "label", 9 | name: "Label", 10 | type: "text", 11 | }, 12 | ], 13 | editing() { 14 | return { 15 | components: {}, 16 | }; 17 | }, 18 | }); 19 | 20 | export { headerLinkDefinition }; 21 | -------------------------------------------------------------------------------- /apps/dashboard-demo/src/app/easyblocks/components/AppShell/AppShellHeader/HeaderLink/HeaderLink.tsx: -------------------------------------------------------------------------------- 1 | import { createButtonComponent } from "../../../createButtonComponent"; 2 | import { ActionTriggerType } from "../../../types"; 3 | 4 | export const HeaderLinkActionTrigger: ActionTriggerType<{ label: string }> = ( 5 | props 6 | ) => { 7 | const { as, __easyblocks, label, ...restProps } = props; 8 | const Element = as ?? "div"; 9 | 10 | const activeClass = 11 | "text-blue-500 underline hover:opacity-50 bg-transparent cursor-pointer"; 12 | const nonActiveClass = "text-black"; 13 | 14 | const isActive = as === "div" || as === undefined; 15 | 16 | return ( 17 | 18 | {label} 19 | 20 | ); 21 | }; 22 | 23 | export const HeaderLink = createButtonComponent(HeaderLinkActionTrigger); 24 | -------------------------------------------------------------------------------- /apps/dashboard-demo/src/app/easyblocks/components/AppShell/AppShellHeader/NicePng_snail-png_1032036.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyblockshq/easyblocks/c97eaa1170131224644fd119bb61173b9c8d6e88/apps/dashboard-demo/src/app/easyblocks/components/AppShell/AppShellHeader/NicePng_snail-png_1032036.png -------------------------------------------------------------------------------- /apps/dashboard-demo/src/app/easyblocks/components/AppShell/AppShellSidebar/AppShellSidebar.client.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement } from "react"; 2 | import { NoCodeComponentProps } from "../../types"; 3 | 4 | function AppShellSidebar({ Items }: NoCodeComponentProps) { 5 | return ( 6 |
7 | {(Items as Array).map((Item, index) => { 8 | return ; 9 | })} 10 |
11 | ); 12 | } 13 | 14 | export { AppShellSidebar }; 15 | -------------------------------------------------------------------------------- /apps/dashboard-demo/src/app/easyblocks/components/AppShell/AppShellSidebar/AppShellSidebar.ts: -------------------------------------------------------------------------------- 1 | import { NoCodeComponentDefinition } from "@easyblocks/core"; 2 | 3 | const appShellSidebarDefinition: NoCodeComponentDefinition = { 4 | id: "AppShellSidebar", 5 | schema: [ 6 | { 7 | prop: "Items", 8 | type: "component-collection", 9 | accepts: ["SidebarItem"], 10 | }, 11 | ], 12 | }; 13 | 14 | export { appShellSidebarDefinition }; 15 | -------------------------------------------------------------------------------- /apps/dashboard-demo/src/app/easyblocks/components/AppShell/AppShellSidebar/SidebarItem/SidebarItem.ts: -------------------------------------------------------------------------------- 1 | import { NoCodeComponentDefinition } from "@easyblocks/core"; 2 | import { createButtonDefinition } from "@/app/easyblocks/components/createButtonDefinition"; 3 | 4 | const sidebarItemDefinition: NoCodeComponentDefinition = createButtonDefinition( 5 | { 6 | id: "SidebarItem", 7 | schema: [ 8 | { 9 | prop: "label", 10 | label: "Label", 11 | type: "text", 12 | }, 13 | { 14 | prop: "icon", 15 | label: "Icon", 16 | type: "select", 17 | params: { 18 | options: [ 19 | "Home", 20 | "EnvelopeOpen", 21 | "Heart", 22 | "Person", 23 | "Gear", 24 | "MagnifyingGlass", 25 | ], 26 | }, 27 | }, 28 | ], 29 | } 30 | ); 31 | 32 | export { sidebarItemDefinition }; 33 | -------------------------------------------------------------------------------- /apps/dashboard-demo/src/app/easyblocks/components/AssetLink/AssetLink.client.tsx: -------------------------------------------------------------------------------- 1 | import { NoCodeActionComponentProps } from "../types"; 2 | 3 | function AssetLink({ 4 | trigger: TriggerElement, 5 | asset, 6 | }: NoCodeActionComponentProps) { 7 | const url = asset ? `/assets/${asset.id}` : ""; 8 | 9 | return ; 10 | } 11 | 12 | export { AssetLink }; 13 | -------------------------------------------------------------------------------- /apps/dashboard-demo/src/app/easyblocks/components/AssetLink/AssetLink.ts: -------------------------------------------------------------------------------- 1 | import { NoCodeComponentDefinition } from "@easyblocks/core"; 2 | 3 | const assetLinkDefinition: NoCodeComponentDefinition = { 4 | id: "AssetLink", 5 | label: "Asset Link", 6 | type: "action", 7 | schema: [ 8 | { 9 | prop: "asset", 10 | type: "asset", 11 | optional: true, 12 | }, 13 | ], 14 | }; 15 | 16 | export { assetLinkDefinition }; 17 | -------------------------------------------------------------------------------- /apps/dashboard-demo/src/app/easyblocks/components/AssetScreenContent/AssetsScreenContent.definition.ts: -------------------------------------------------------------------------------- 1 | import { createMainAreaBasedDefinition } from "../MainArea/MainArea"; 2 | 3 | const assetScreenContentDefinition = createMainAreaBasedDefinition({ 4 | id: "AssetScreenContent", 5 | label: "Asset screen content", 6 | rootParams: [ 7 | { 8 | prop: "asset", 9 | label: "Asset", 10 | widgets: [ 11 | { 12 | id: "asset", 13 | label: "Asset", 14 | }, 15 | ], 16 | }, 17 | ], 18 | }); 19 | 20 | export { assetScreenContentDefinition }; 21 | -------------------------------------------------------------------------------- /apps/dashboard-demo/src/app/easyblocks/components/Button/Button.client.tsx: -------------------------------------------------------------------------------- 1 | import { Button as ButtonUI } from "@/app/components/Button"; 2 | import { createButtonComponent } from "@/app/easyblocks/components/createButtonComponent"; 3 | import { forwardRef } from "react"; 4 | 5 | export const Button = createButtonComponent( 6 | // eslint-disable-next-line react/display-name 7 | forwardRef(({ label, variant, size, ...restProps }: any, ref) => { 8 | return ( 9 | 10 | {label} 11 | 12 | ); 13 | }) 14 | ); 15 | -------------------------------------------------------------------------------- /apps/dashboard-demo/src/app/easyblocks/components/Button/Button.ts: -------------------------------------------------------------------------------- 1 | import { NoCodeComponentDefinition } from "@easyblocks/core"; 2 | import { createButtonDefinition } from "@/app/easyblocks/components/createButtonDefinition"; 3 | 4 | const buttonDefinition: NoCodeComponentDefinition = createButtonDefinition({ 5 | id: "Button", 6 | label: "Button", 7 | type: "button", 8 | schema: [ 9 | { 10 | prop: "variant", 11 | label: "Variant", 12 | type: "select", 13 | params: { 14 | options: ["solid", "soft", "outline", "ghost"], 15 | }, 16 | }, 17 | { 18 | prop: "size", 19 | label: "Size", 20 | type: "select", 21 | params: { 22 | options: ["1", "2", "3", "4"], 23 | }, 24 | defaultValue: "2", 25 | }, 26 | { 27 | prop: "label", 28 | label: "Label", 29 | type: "text", 30 | }, 31 | ], 32 | }); 33 | 34 | export { buttonDefinition }; 35 | -------------------------------------------------------------------------------- /apps/dashboard-demo/src/app/easyblocks/components/ButtonsGroup/ButtonsGroup.client.tsx: -------------------------------------------------------------------------------- 1 | import { NoCodeComponentProps } from "../types"; 2 | 3 | function ButtonsGroup({ ButtonsContainer, Buttons }: NoCodeComponentProps) { 4 | return ( 5 | 6 | {Buttons.map((Button: any, index: number) => ( 7 | 8 | ))} 9 | 10 | ); 11 | } 12 | 13 | export { ButtonsGroup }; 14 | -------------------------------------------------------------------------------- /apps/dashboard-demo/src/app/easyblocks/components/HorizontalLayout/HorizontalLayout.client.tsx: -------------------------------------------------------------------------------- 1 | import { NoCodeComponentProps } from "../types"; 2 | 3 | function HorizontalLayout({ 4 | Container, 5 | Items, 6 | itemWrappers, 7 | }: NoCodeComponentProps) { 8 | return ( 9 | 10 | {Items.map((Item: any, index: number) => { 11 | const OuterWrapper = itemWrappers[index].OuterWrapper; 12 | const InnerWrapper = itemWrappers[index].InnerWrapper; 13 | return ( 14 | 15 | 16 | 17 | 18 | 19 | ); 20 | })} 21 | 22 | ); 23 | } 24 | 25 | export { HorizontalLayout }; 26 | -------------------------------------------------------------------------------- /apps/dashboard-demo/src/app/easyblocks/components/Image/Image.client.tsx: -------------------------------------------------------------------------------- 1 | import { NoCodeComponentProps } from "../types"; 2 | import NextImage from "next/image"; 3 | 4 | function Image({ image }: NoCodeComponentProps) { 5 | return ( 6 |
7 | {image && } 8 |
9 | ); 10 | } 11 | 12 | export { Image }; 13 | -------------------------------------------------------------------------------- /apps/dashboard-demo/src/app/easyblocks/components/Image/Image.ts: -------------------------------------------------------------------------------- 1 | import { NoCodeComponentDefinition } from "@easyblocks/core"; 2 | 3 | const imageComponentDefinition: NoCodeComponentDefinition = { 4 | id: "Image", 5 | type: "item", 6 | schema: [ 7 | { 8 | prop: "image", 9 | label: "Image", 10 | type: "image", 11 | optional: true, 12 | }, 13 | ], 14 | }; 15 | 16 | export { imageComponentDefinition }; 17 | -------------------------------------------------------------------------------- /apps/dashboard-demo/src/app/easyblocks/components/Link/Link.client.tsx: -------------------------------------------------------------------------------- 1 | import { NoCodeActionComponentProps } from "../types"; 2 | 3 | function Link({ trigger: TriggerElement, url }: NoCodeActionComponentProps) { 4 | return ; 5 | } 6 | 7 | export { Link }; 8 | -------------------------------------------------------------------------------- /apps/dashboard-demo/src/app/easyblocks/components/Link/Link.ts: -------------------------------------------------------------------------------- 1 | import { NoCodeComponentDefinition } from "@easyblocks/core"; 2 | 3 | const linkDefinition: NoCodeComponentDefinition = { 4 | id: "Link", 5 | label: "Link", 6 | type: "action", 7 | schema: [ 8 | { 9 | prop: "url", 10 | label: "URL", 11 | type: "string", 12 | defaultValue: "/", 13 | }, 14 | ], 15 | }; 16 | 17 | export { linkDefinition }; 18 | -------------------------------------------------------------------------------- /apps/dashboard-demo/src/app/easyblocks/components/MainArea/MainArea.client.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement } from "react"; 2 | import { NoCodeComponentProps } from "../types"; 3 | 4 | function MainArea({ 5 | HeaderStack, 6 | Panels, 7 | PanelsGrid, 8 | panelWrappers, 9 | }: NoCodeComponentProps) { 10 | return ( 11 |
12 |
13 | 14 |
15 | 16 | {(Panels as Array).map((Panel, index) => { 17 | const PanelWrapper = panelWrappers[index]; 18 | 19 | return ( 20 | 21 | 22 | 23 | ); 24 | })} 25 | 26 |
27 | ); 28 | } 29 | 30 | export { MainArea }; 31 | -------------------------------------------------------------------------------- /apps/dashboard-demo/src/app/easyblocks/components/MainArea/PanelCard/PanelCard.client.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement } from "react"; 2 | import { NoCodeComponentProps } from "../../types"; 3 | 4 | function PanelCard({ 5 | HeaderStack, 6 | Items, 7 | Buttons, 8 | isEditable, 9 | }: NoCodeComponentProps) { 10 | return ( 11 |
12 |
13 |
14 | 15 |
16 | {isEditable && ( 17 |
18 | 19 |
20 | )} 21 |
22 | 23 | {(Items as Array).map((Item, index) => { 24 | return ; 25 | })} 26 |
27 | ); 28 | } 29 | 30 | export { PanelCard }; 31 | -------------------------------------------------------------------------------- /apps/dashboard-demo/src/app/easyblocks/components/MainArea/PanelCard/PropertiesForm/PropertiesForm.client.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement } from "react"; 2 | import { NoCodeComponentProps } from "../../../types"; 3 | 4 | function PropertiesForm({ Fields, Action, Buttons }: NoCodeComponentProps) { 5 | const formElement = ( 6 |
7 | {(Fields as Array).map((Field, index) => { 8 | return ; 9 | })} 10 |
11 | 12 |
13 | 14 | ); 15 | 16 | if (Action) { 17 | return ; 18 | } 19 | 20 | return formElement; 21 | } 22 | 23 | export { PropertiesForm }; 24 | -------------------------------------------------------------------------------- /apps/dashboard-demo/src/app/easyblocks/components/MainArea/PanelCard/PropertiesForm/PropertiesForm.ts: -------------------------------------------------------------------------------- 1 | import { NoCodeComponentDefinition } from "@easyblocks/core"; 2 | 3 | const propertiesFormDefinition: NoCodeComponentDefinition = { 4 | id: "PropertiesForm", 5 | label: "Properties Form", 6 | schema: [ 7 | { 8 | prop: "Fields", 9 | type: "component-collection", 10 | accepts: ["PropertiesFormTextField", "PropertiesFormBooleanField"], 11 | }, 12 | { 13 | prop: "Action", 14 | type: "component", 15 | accepts: ["formAction"], 16 | visible: true, 17 | noInline: true, 18 | }, 19 | { 20 | prop: "Buttons", 21 | type: "component", 22 | required: true, 23 | accepts: ["ButtonsGroup"], 24 | }, 25 | ], 26 | type: "item", 27 | }; 28 | 29 | export { propertiesFormDefinition }; 30 | -------------------------------------------------------------------------------- /apps/dashboard-demo/src/app/easyblocks/components/MainArea/PanelCard/PropertiesForm/PropertiesFormField/PropertiesFormField.ts: -------------------------------------------------------------------------------- 1 | import { NoCodeComponentDefinition } from "@easyblocks/core"; 2 | 3 | const propertiesFormTextField: NoCodeComponentDefinition = { 4 | id: "PropertiesFormTextField", 5 | schema: [ 6 | { 7 | prop: "property", 8 | type: "propertyText", 9 | }, 10 | ], 11 | }; 12 | 13 | const propertiesFormBooleanField: NoCodeComponentDefinition = { 14 | id: "PropertiesFormBooleanField", 15 | schema: [ 16 | { 17 | prop: "property", 18 | type: "propertyBoolean", 19 | }, 20 | ], 21 | }; 22 | 23 | export { propertiesFormTextField, propertiesFormBooleanField }; 24 | -------------------------------------------------------------------------------- /apps/dashboard-demo/src/app/easyblocks/components/MainArea/PanelCard/PropertiesForm/SubmitButtonAction.client.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement } from "react"; 2 | import { NoCodeComponentProps } from "../../../types"; 3 | 4 | function SubmitButtonAction({ 5 | trigger: Trigger, 6 | }: NoCodeComponentProps & { trigger: ReactElement }) { 7 | return ; 8 | } 9 | 10 | export { SubmitButtonAction }; 11 | -------------------------------------------------------------------------------- /apps/dashboard-demo/src/app/easyblocks/components/MainArea/PanelCard/PropertiesForm/SubmitButtonAction.ts: -------------------------------------------------------------------------------- 1 | import { NoCodeComponentDefinition } from "@easyblocks/core"; 2 | 3 | const submitButton: NoCodeComponentDefinition = { 4 | id: "SubmitButtonAction", 5 | type: "action", 6 | label: "Submit Button", 7 | schema: [], 8 | }; 9 | 10 | export { submitButton }; 11 | -------------------------------------------------------------------------------- /apps/dashboard-demo/src/app/easyblocks/components/MainArea/PanelCard/PropertiesForm/UpdateAssetFormAction.ts: -------------------------------------------------------------------------------- 1 | import { NoCodeComponentDefinition } from "@easyblocks/core"; 2 | 3 | const updateAssetFormAction: NoCodeComponentDefinition = { 4 | id: "UpdateAssetFormAction", 5 | label: "Update Asset", 6 | schema: [ 7 | { 8 | prop: "asset", 9 | type: "formAction", 10 | }, 11 | ], 12 | type: "formAction", 13 | }; 14 | 15 | export { updateAssetFormAction }; 16 | -------------------------------------------------------------------------------- /apps/dashboard-demo/src/app/easyblocks/components/MainArea/PanelCard/PropertiesGroup/PropertiesGroup.client.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement } from "react"; 2 | import { NoCodeComponentProps } from "../../../types"; 3 | 4 | function PropertiesGroup({ Properties, variant }: NoCodeComponentProps) { 5 | return ( 6 |
11 | {(Properties as Array).map((PropertyItem, index) => { 12 | return ; 13 | })} 14 |
15 | ); 16 | } 17 | 18 | export { PropertiesGroup }; 19 | -------------------------------------------------------------------------------- /apps/dashboard-demo/src/app/easyblocks/components/OpenDialog/DialogContent/DialogContent.client.tsx: -------------------------------------------------------------------------------- 1 | import { NoCodeActionComponentProps } from "../../types"; 2 | 3 | function DialogContent({ Root, Content }: NoCodeActionComponentProps) { 4 | return ( 5 | 6 | 7 | 8 | ); 9 | } 10 | 11 | export { DialogContent }; 12 | -------------------------------------------------------------------------------- /apps/dashboard-demo/src/app/easyblocks/components/OpenDialog/OpenDialog.ts: -------------------------------------------------------------------------------- 1 | import { NoCodeComponentDefinition } from "@easyblocks/core"; 2 | 3 | const openDialogDefinition: NoCodeComponentDefinition = { 4 | id: "OpenDialog", 5 | type: "action", 6 | schema: [ 7 | { 8 | prop: "Content", 9 | type: "component", 10 | required: true, 11 | accepts: ["DialogContent"], 12 | }, 13 | ], 14 | }; 15 | 16 | export { openDialogDefinition }; 17 | -------------------------------------------------------------------------------- /apps/dashboard-demo/src/app/easyblocks/components/Separator/Separator.client.tsx: -------------------------------------------------------------------------------- 1 | import { NoCodeActionComponentProps } from "../types"; 2 | 3 | function Separator({ Container, Separator }: NoCodeActionComponentProps) { 4 | return ( 5 | 6 | 7 | 8 | ); 9 | } 10 | 11 | export { Separator }; 12 | -------------------------------------------------------------------------------- /apps/dashboard-demo/src/app/easyblocks/components/Stack/Stack.client.tsx: -------------------------------------------------------------------------------- 1 | import { NoCodeComponentProps } from "../types"; 2 | 3 | function Stack({ 4 | StackContainer, 5 | Items, 6 | outerItemWrappers, 7 | innerItemWrappers, 8 | }: NoCodeComponentProps) { 9 | return ( 10 | 11 | {Items.map((Item: any, index: number) => { 12 | const StackItemOuter = outerItemWrappers[index]; 13 | const StackItemInner = innerItemWrappers[index]; 14 | 15 | return ( 16 | 17 | 18 | 19 | 20 | 21 | ); 22 | })} 23 | 24 | ); 25 | } 26 | 27 | export { Stack }; 28 | -------------------------------------------------------------------------------- /apps/dashboard-demo/src/app/easyblocks/components/TriggerEvent/TriggerEvent.client.tsx: -------------------------------------------------------------------------------- 1 | import { NoCodeActionComponentProps } from "../types"; 2 | 3 | function TriggerEvent({ 4 | trigger: TriggerElement, 5 | message, 6 | }: NoCodeActionComponentProps) { 7 | return ( 8 | { 12 | window.postMessage({ 13 | type: "triggerEvent", 14 | message, 15 | }); 16 | }} 17 | /> 18 | ); 19 | } 20 | 21 | export { TriggerEvent }; 22 | -------------------------------------------------------------------------------- /apps/dashboard-demo/src/app/easyblocks/components/TriggerEvent/TriggerEvent.ts: -------------------------------------------------------------------------------- 1 | import { NoCodeComponentDefinition } from "@easyblocks/core"; 2 | 3 | const triggerEventDefinition: NoCodeComponentDefinition = { 4 | id: "TriggerEvent", 5 | label: "Trigger Event", 6 | type: "action", 7 | schema: [ 8 | { 9 | prop: "message", 10 | type: "text", 11 | }, 12 | ], 13 | }; 14 | 15 | export { triggerEventDefinition }; 16 | -------------------------------------------------------------------------------- /apps/dashboard-demo/src/app/easyblocks/components/WelcomeScreenContent/WelcomeScreenContent.definition.ts: -------------------------------------------------------------------------------- 1 | import { createMainAreaBasedDefinition } from "../MainArea/MainArea"; 2 | 3 | const welcomeScreenContentDefinition = createMainAreaBasedDefinition({ 4 | id: "WelcomeScreenContent", 5 | label: "Welcome screen content", 6 | }); 7 | 8 | export { welcomeScreenContentDefinition }; 9 | -------------------------------------------------------------------------------- /apps/dashboard-demo/src/app/easyblocks/components/createButtonComponent.tsx: -------------------------------------------------------------------------------- 1 | // A universal wrapper for button components that takes into account the Action 2 | 3 | import React from "react"; 4 | import { ActionTriggerType } from "./types"; 5 | 6 | export function createButtonComponent( 7 | TriggerComponent: ActionTriggerType 8 | ) { 9 | const Component = ({ Action, ...props }: any) => { 10 | if (Action) { 11 | return ( 12 | } 15 | /> 16 | ); 17 | } 18 | 19 | return ; 20 | }; 21 | 22 | Component.displayName = "ButtonComponent"; 23 | return Component; 24 | } 25 | -------------------------------------------------------------------------------- /apps/dashboard-demo/src/app/easyblocks/components/createButtonDefinition.ts: -------------------------------------------------------------------------------- 1 | import { NoCodeComponentDefinition } from "@easyblocks/core"; 2 | 3 | /** 4 | * This is a generic wrapper that always adds Action property to the button 5 | */ 6 | export function createButtonDefinition( 7 | componentDefinition: NoCodeComponentDefinition 8 | ): NoCodeComponentDefinition { 9 | return { 10 | ...componentDefinition, 11 | schema: [ 12 | ...componentDefinition.schema, 13 | { 14 | prop: "Action", 15 | label: "Action", 16 | type: "component", 17 | accepts: ["action"], 18 | visible: true, 19 | noInline: true, 20 | }, 21 | ], 22 | }; 23 | } 24 | -------------------------------------------------------------------------------- /apps/dashboard-demo/src/app/easyblocks/components/types.ts: -------------------------------------------------------------------------------- 1 | import { NoCodeComponentProps as NoCodeActionComponentProps$ } from "@easyblocks/core"; 2 | 3 | export type NoCodeActionComponentProps = Record; 4 | 5 | export type ActionTriggerType = React.FC< 6 | { 7 | href?: string; 8 | target?: string; 9 | as?: "div" | "a" | "button"; 10 | onClick?: any; 11 | } & OwnProps & 12 | NoCodeComponentProps 13 | >; 14 | 15 | export type ActionWrapperType = React.FC< 16 | { 17 | trigger: ReturnType>; 18 | } & OwnProps & 19 | NoCodeComponentProps 20 | >; 21 | 22 | export type NoCodeComponentProps = NoCodeActionComponentProps & { 23 | [key: string]: any; 24 | }; 25 | -------------------------------------------------------------------------------- /apps/dashboard-demo/src/app/easyblocks/fetchExternalData.ts: -------------------------------------------------------------------------------- 1 | import { RequestedExternalData } from "@easyblocks/core"; 2 | import { fetchAssets } from "./types/asset/fetch"; 3 | 4 | async function fetchExternalData(externalData: RequestedExternalData) { 5 | const assetsExternalData = await fetchAssets(externalData); 6 | 7 | return { 8 | ...assetsExternalData, 9 | }; 10 | } 11 | 12 | export { fetchExternalData }; 13 | -------------------------------------------------------------------------------- /apps/dashboard-demo/src/app/edit/RemoveDocumentButton.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { Button } from "@radix-ui/themes"; 3 | import { useRouter } from "next/navigation"; 4 | 5 | function RemoveDocumentButton({ cookieId }: { cookieId: string }) { 6 | const router = useRouter(); 7 | 8 | return ( 9 | 22 | ); 23 | } 24 | 25 | export { RemoveDocumentButton }; 26 | -------------------------------------------------------------------------------- /apps/dashboard-demo/src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyblockshq/easyblocks/c97eaa1170131224644fd119bb61173b9c8d6e88/apps/dashboard-demo/src/app/favicon.ico -------------------------------------------------------------------------------- /apps/dashboard-demo/src/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import "@radix-ui/themes/styles.css"; 2 | import type { Metadata } from "next"; 3 | import { Inter } from "next/font/google"; 4 | import { Toaster } from "react-hot-toast"; 5 | import { AppProviders } from "./components/AppProviders"; 6 | import { EventListener } from "./components/EventListener"; 7 | import "./globals.css"; 8 | 9 | const inter = Inter({ subsets: ["latin"] }); 10 | 11 | export const metadata: Metadata = { 12 | title: "Easyblocks POC", 13 | }; 14 | 15 | export default function RootLayout({ 16 | children, 17 | }: { 18 | children: React.ReactNode; 19 | }) { 20 | return ( 21 | 22 | 23 | {children} 24 | 25 | 26 | 27 | 28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /apps/dashboard-demo/tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from "tailwindcss"; 2 | 3 | const config: Config = { 4 | content: [ 5 | "./src/pages/**/*.{js,ts,jsx,tsx,mdx}", 6 | "./src/components/**/*.{js,ts,jsx,tsx,mdx}", 7 | "./src/app/**/*.{js,ts,jsx,tsx,mdx}", 8 | ], 9 | theme: { 10 | screens: { 11 | xs: "568px", 12 | sm: "768px", 13 | md: "992px", 14 | lg: "1280px", 15 | xl: "1600px", 16 | }, 17 | }, 18 | plugins: [], 19 | corePlugins: { 20 | preflight: false, 21 | }, 22 | }; 23 | export default config; 24 | -------------------------------------------------------------------------------- /apps/dashboard-demo/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2015", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "noEmit": true, 10 | "esModuleInterop": true, 11 | "module": "esnext", 12 | "moduleResolution": "bundler", 13 | "resolveJsonModule": true, 14 | "isolatedModules": true, 15 | "jsx": "preserve", 16 | "incremental": true, 17 | "plugins": [ 18 | { 19 | "name": "next" 20 | } 21 | ], 22 | "paths": { 23 | "@/*": ["./src/*"] 24 | } 25 | }, 26 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 27 | "exclude": ["node_modules"] 28 | } 29 | -------------------------------------------------------------------------------- /apps/design-system-app/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | .pnpm-debug.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | -------------------------------------------------------------------------------- /apps/design-system-app/next.config.js: -------------------------------------------------------------------------------- 1 | const nextConfig = {}; 2 | 3 | module.exports = nextConfig; 4 | -------------------------------------------------------------------------------- /apps/design-system-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "design-system-app", 3 | "private": true, 4 | "scripts": { 5 | "dev": "next dev --port 3400", 6 | "build": "next build", 7 | "start": "next start", 8 | "lint": "next lint" 9 | }, 10 | "dependencies": { 11 | "@easyblocks/design-system": "workspace:*", 12 | "@types/node": "18.14.2", 13 | "@types/react": "^18", 14 | "@types/react-dom": "^18", 15 | "next": "^12.0.8", 16 | "react": "^18", 17 | "react-dom": "^18", 18 | "typescript": "4.9.5" 19 | }, 20 | "version": "1.0.10" 21 | } 22 | -------------------------------------------------------------------------------- /apps/design-system-app/pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import "@/styles/globals.css"; 2 | import type { AppProps } from "next/app"; 3 | import { GlobalModalStyles } from "@easyblocks/design-system"; 4 | 5 | export default function App({ Component, pageProps }: AppProps) { 6 | return ( 7 |
8 | 9 |
13 | {/* @ts-ignore */} 14 | 15 |
16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /apps/design-system-app/pages/_document.tsx: -------------------------------------------------------------------------------- 1 | import { Html, Head, Main, NextScript } from "next/document"; 2 | 3 | export default function Document() { 4 | return ( 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /apps/design-system-app/pages/api/hello.ts: -------------------------------------------------------------------------------- 1 | // Next.js API route support: https://nextjs.org/docs/api-routes/introduction 2 | import type { NextApiRequest, NextApiResponse } from "next"; 3 | 4 | type Data = { 5 | name: string; 6 | }; 7 | 8 | export default function handler( 9 | req: NextApiRequest, 10 | res: NextApiResponse 11 | ) { 12 | res.status(200).json({ name: "John Doe" }); 13 | } 14 | -------------------------------------------------------------------------------- /apps/design-system-app/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyblockshq/easyblocks/c97eaa1170131224644fd119bb61173b9c8d6e88/apps/design-system-app/public/favicon.ico -------------------------------------------------------------------------------- /apps/design-system-app/public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/design-system-app/src/InputStories.tsx: -------------------------------------------------------------------------------- 1 | import { Input, Typography } from "@easyblocks/design-system"; 2 | 3 | export function InputStories() { 4 | return ( 5 |
6 | Input 7 |
8 | 9 |
10 | 11 |
12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /apps/design-system-app/src/MultiSelectStories.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | FormElement, 3 | MultiSelect, 4 | Input, 5 | Typography, 6 | } from "@easyblocks/design-system"; 7 | 8 | export function MultiSelectStories() { 9 | return ( 10 |
11 | Multiselect 12 |
13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 |
23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /apps/design-system-app/src/RangeSliderStories.tsx: -------------------------------------------------------------------------------- 1 | import { RangeSlider, Typography } from "@easyblocks/design-system"; 2 | 3 | export function RangeSliderStories() { 4 | return ( 5 |
6 | Range slider 7 |
8 | 9 |
10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /apps/design-system-app/src/SelectStories.tsx: -------------------------------------------------------------------------------- 1 | import { Select, SelectItem, Typography } from "@easyblocks/design-system"; 2 | import { useState } from "react"; 3 | 4 | export function SelectStories() { 5 | const [value, setValue] = useState(""); 6 | 7 | return ( 8 |
9 | Select 10 |
11 | 16 |
17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /apps/design-system-app/src/ToggleStories.tsx: -------------------------------------------------------------------------------- 1 | import { Toggle, Typography } from "@easyblocks/design-system"; 2 | 3 | export function ToggleStories() { 4 | return ( 5 |
6 | Toggle 7 |
8 | 9 |
10 | Forced checked 11 |
12 | 13 |
14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /apps/design-system-app/src/TypographyStories.tsx: -------------------------------------------------------------------------------- 1 | import { Typography } from "@easyblocks/design-system"; 2 | 3 | export function TypographyStories() { 4 | return ( 5 |
6 | Typography 7 |
8 | 9 | Body 10 |
11 | Body 4 12 |
13 | Label 14 |
15 | Label 2 16 |
17 | Label 3 18 |
19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /apps/design-system-app/styles/globals.css: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | padding: 0; 4 | margin: 0; 5 | } 6 | 7 | html, 8 | body { 9 | max-width: 100vw; 10 | overflow-x: hidden; 11 | } 12 | 13 | body { 14 | padding: 32px; 15 | } 16 | 17 | a { 18 | color: inherit; 19 | text-decoration: none; 20 | } 21 | -------------------------------------------------------------------------------- /apps/design-system-app/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "noEmit": true, 10 | "esModuleInterop": true, 11 | "module": "esnext", 12 | "moduleResolution": "node", 13 | "resolveJsonModule": true, 14 | "isolatedModules": true, 15 | "jsx": "preserve", 16 | "incremental": true, 17 | "baseUrl": ".", 18 | "paths": { 19 | "@/*": ["./*"] 20 | } 21 | }, 22 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], 23 | "exclude": ["node_modules"] 24 | } 25 | -------------------------------------------------------------------------------- /apps/liquid-demo/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /apps/liquid-demo/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env*.local 29 | 30 | # vercel 31 | .vercel 32 | 33 | # typescript 34 | *.tsbuildinfo 35 | next-env.d.ts 36 | -------------------------------------------------------------------------------- /apps/liquid-demo/README.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | Easyblocks Quickstart project. 4 | -------------------------------------------------------------------------------- /apps/liquid-demo/next.config.js: -------------------------------------------------------------------------------- 1 | const path = require("node:path"); 2 | 3 | /** @type {import('next').NextConfig} */ 4 | const nextConfig = { 5 | webpack: (config) => { 6 | // apps/README.md#Apps and internal packages 7 | config.resolve.modules.unshift(path.resolve(__dirname, "node_modules")); 8 | 9 | return config; 10 | }, 11 | }; 12 | 13 | module.exports = nextConfig; 14 | -------------------------------------------------------------------------------- /apps/liquid-demo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "liquid-demo", 3 | "version": "1.0.10", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "@easyblocks/core": "workspace:*", 13 | "@easyblocks/editor": "workspace:*", 14 | "@types/node": "20.4.2", 15 | "@types/react": "18.2.20", 16 | "@types/react-dom": "18.2.7", 17 | "autoprefixer": "10.4.14", 18 | "eslint": "8.45.0", 19 | "eslint-config-next": "13.4.10", 20 | "html-to-react": "^1.7.0", 21 | "liquidjs": "^10.9.3", 22 | "next": "13.4.12", 23 | "postcss": "8.4.26", 24 | "react": "18.2.0", 25 | "react-dom": "18.2.0", 26 | "tailwindcss": "3.3.3", 27 | "typescript": "5.1.6" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /apps/liquid-demo/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /apps/liquid-demo/src/app/easyblocks-editor/layout.tsx: -------------------------------------------------------------------------------- 1 | import "../globals.css"; 2 | 3 | export default function RootLayout({ 4 | children, 5 | }: { 6 | children: React.ReactNode; 7 | }) { 8 | return ( 9 | 10 | {children} 11 | 12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /apps/liquid-demo/src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyblockshq/easyblocks/c97eaa1170131224644fd119bb61173b9c8d6e88/apps/liquid-demo/src/app/favicon.ico -------------------------------------------------------------------------------- /apps/liquid-demo/src/app/globals.css: -------------------------------------------------------------------------------- 1 | body { 2 | -webkit-font-smoothing: antialiased; 3 | margin: 0; 4 | padding: 0; 5 | } 6 | -------------------------------------------------------------------------------- /apps/liquid-demo/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "noEmit": true, 10 | "esModuleInterop": true, 11 | "module": "esnext", 12 | "moduleResolution": "bundler", 13 | "resolveJsonModule": true, 14 | "isolatedModules": true, 15 | "jsx": "preserve", 16 | "incremental": true, 17 | "plugins": [ 18 | { 19 | "name": "next" 20 | } 21 | ], 22 | "paths": { 23 | "@/*": ["./src/*"] 24 | } 25 | }, 26 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 27 | "exclude": ["node_modules"] 28 | } 29 | -------------------------------------------------------------------------------- /apps/page-builder-demo/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /apps/page-builder-demo/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env*.local 29 | 30 | # vercel 31 | .vercel 32 | 33 | # typescript 34 | *.tsbuildinfo 35 | next-env.d.ts 36 | -------------------------------------------------------------------------------- /apps/page-builder-demo/README.md: -------------------------------------------------------------------------------- 1 | # Easyblocks Page Builder Example 2 | 3 | This repository serves as an example of how you can use Easyblocks to create a page builder. 4 | 5 | ## Installation 6 | 7 | `npm install` 8 | 9 | ## Add an access token 10 | 11 | Easyblocks editor requires an external service for saving, updating and versioning of your documents and templates. In order to play around with this demo we recommend using our simple and free cloud service. You'll be able to provide your custom one later (read [here](https://docs.easyblocks.io/essentials/backend) for more info). 12 | 13 | 1. Go to [https://app.easyblocks.io](https://app.easyblocks.io) and create a free account. 14 | 2. Go to "Playground project" 15 | 3. Copy acccess token 16 | 4. Create `.env.local` file and assign the acquired token to `NEXT_PUBLIC_EASYBLOCKS_ACCESS_TOKEN` environment variable. 17 | 18 | ## Run 19 | 20 | `npm run dev` 21 | -------------------------------------------------------------------------------- /apps/page-builder-demo/next.config.js: -------------------------------------------------------------------------------- 1 | const path = require("node:path"); 2 | 3 | /** @type {import('next').NextConfig} */ 4 | const nextConfig = { 5 | webpack: (config) => { 6 | // apps/README.md#Apps and internal packages 7 | config.resolve.modules.unshift(path.resolve(__dirname, "node_modules")); 8 | 9 | return config; 10 | }, 11 | transpilePackages: ["@easyblocks/utils"], 12 | }; 13 | 14 | module.exports = nextConfig; 15 | -------------------------------------------------------------------------------- /apps/page-builder-demo/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /apps/page-builder-demo/public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/(system)/layout.tsx: -------------------------------------------------------------------------------- 1 | import "../globals.css"; 2 | import type { Metadata } from "next"; 3 | import { Inter } from "next/font/google"; 4 | 5 | const inter = Inter({ subsets: ["latin"] }); 6 | 7 | export const metadata: Metadata = { 8 | title: "Easyblocks demo", 9 | description: "Easyblocks demo", 10 | }; 11 | 12 | export default function RootLayout({ 13 | children, 14 | }: { 15 | children: React.ReactNode; 16 | }) { 17 | return ( 18 | 19 | {children} 20 | 21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/(system)/not-found.tsx: -------------------------------------------------------------------------------- 1 | import Link from "next/link"; 2 | 3 | export default function NotFound() { 4 | return ( 5 |
6 |

Could not find requested resource

7 |
8 | ); 9 | } 10 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/(system)/page/[documentId]/EasyblocksContent.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { Easyblocks, easyblocksGetStyleTag } from "@easyblocks/core"; 3 | import { useServerInsertedHTML } from "next/navigation"; 4 | import { ComponentPropsWithoutRef } from "react"; 5 | 6 | function EasyblocksContent({ 7 | renderableDocument, 8 | externalData, 9 | components, 10 | }: ComponentPropsWithoutRef) { 11 | useServerInsertedHTML(() => { 12 | return easyblocksGetStyleTag(); 13 | }); 14 | 15 | return ( 16 | 21 | ); 22 | } 23 | 24 | export { EasyblocksContent }; 25 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/easyblocks-editor/layout.tsx: -------------------------------------------------------------------------------- 1 | import "../globals.css"; 2 | 3 | export default function RootLayout({ 4 | children, 5 | }: { 6 | children: React.ReactNode; 7 | }) { 8 | return ( 9 | 10 | 11 | {children} 12 | 13 | 14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/easyblocks/components/AlertAction/AlertAction.definition.ts: -------------------------------------------------------------------------------- 1 | import { NoCodeComponentDefinition } from "@easyblocks/core"; 2 | 3 | export const alertActionDefinition: NoCodeComponentDefinition = { 4 | id: "AlertAction", 5 | label: "Alert (demo)", 6 | type: "action", 7 | thumbnail: 8 | "https://shopstory.s3.eu-central-1.amazonaws.com/picker_icon_action.png", 9 | schema: [ 10 | { 11 | prop: "text", 12 | type: "string", 13 | defaultValue: "Lorem ipsum", 14 | }, 15 | ], 16 | }; 17 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/easyblocks/components/AlertAction/AlertAction.tsx: -------------------------------------------------------------------------------- 1 | export const AlertAction = (props: any) => { 2 | const { text, trigger: TriggerElement } = props; 3 | 4 | return ( 5 | { 9 | alert(text); 10 | }} 11 | /> 12 | ); 13 | }; 14 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/easyblocks/components/BannerCard/BannerCard.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | export function BannerCard(props: any) { 4 | const { 5 | Container, 6 | Root, 7 | Stack, 8 | StackContainer, 9 | StackInnerContainer, 10 | CoverContainer, 11 | CoverCard, 12 | } = props; 13 | 14 | return ( 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | ); 28 | } 29 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/easyblocks/components/BannerCard/CoverCard/CoverCard.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | export function CoverCard(props: any) { 4 | const { Root, Background, Overlay } = props; 5 | 6 | return ( 7 | 8 | 9 | 10 | 11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/easyblocks/components/ButtonGroup/ButtonGroup.tsx: -------------------------------------------------------------------------------- 1 | import React, { ReactElement } from "react"; 2 | 3 | function ButtonGroup(props: { 4 | ButtonsContainer: ReactElement; 5 | Buttons: Array; 6 | }) { 7 | const { ButtonsContainer, Buttons } = props; 8 | 9 | return ( 10 | 11 | {Buttons.map((Button, index) => ( 12 | 13 | ))} 14 | 15 | ); 16 | } 17 | 18 | export { ButtonGroup }; 19 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/easyblocks/components/Code/Code.definition.ts: -------------------------------------------------------------------------------- 1 | import { NoCodeComponentDefinition } from "@easyblocks/core"; 2 | 3 | const codeDefinition: NoCodeComponentDefinition = { 4 | id: "Code", 5 | type: "@easyblocks/text-wrapper", 6 | schema: [], 7 | styles() { 8 | return { 9 | styled: { 10 | Wrapper: { 11 | __as: "span", 12 | padding: "0.1em 0.3em", 13 | background: "rgba(135,131,120,0.15)", 14 | borderRadius: 3, 15 | color: "#EB5757", 16 | fontFamily: "monospace", 17 | fontSize: "0.8em", 18 | }, 19 | }, 20 | }; 21 | }, 22 | thumbnail: 23 | "https://shopstory.s3.eu-central-1.amazonaws.com/picker_icon_text.png", 24 | }; 25 | 26 | export { codeDefinition }; 27 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/easyblocks/components/Code/Code.tsx: -------------------------------------------------------------------------------- 1 | import { NoCodeComponentProps } from "@easyblocks/core"; 2 | 3 | function Code({ 4 | children, 5 | Wrapper, 6 | }: NoCodeComponentProps & Record) { 7 | return {children}; 8 | } 9 | 10 | export { Code }; 11 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/easyblocks/components/Image/ImageRenderer.tsx: -------------------------------------------------------------------------------- 1 | import { ImageSrc } from "../../externalData/types"; 2 | import { Placeholder } from "./Placeholder"; 3 | 4 | type ImageRendererProps = { 5 | image: ImageSrc | undefined; 6 | }; 7 | 8 | function ImageRenderer({ image }: ImageRendererProps) { 9 | if (!image) { 10 | return ; 11 | } 12 | 13 | const { srcset, alt } = image; 14 | 15 | return ( 16 | {alt} 27 | ); 28 | } 29 | 30 | export { ImageRenderer }; 31 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/easyblocks/components/Link/Link.definition.ts: -------------------------------------------------------------------------------- 1 | import { NoCodeComponentDefinition } from "@easyblocks/core"; 2 | 3 | /** 4 | * ProductCard is just for the demo purpose, it's not really styleable 5 | * It's only to show how external data can be connected to component 6 | */ 7 | export const linkDefinition: NoCodeComponentDefinition = { 8 | id: "Link", 9 | label: "Link", 10 | type: "action", 11 | thumbnail: 12 | "https://shopstory.s3.eu-central-1.amazonaws.com/picker_icon_link.png", 13 | schema: [ 14 | { 15 | prop: "url", 16 | type: "url", 17 | }, 18 | { 19 | prop: "shouldOpenInNewWindow", 20 | label: "Open in new window?", 21 | type: "boolean", 22 | defaultValue: true, 23 | }, 24 | ], 25 | }; 26 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/easyblocks/components/Link/Link.tsx: -------------------------------------------------------------------------------- 1 | export const Link = (props: any) => { 2 | const { url, shouldOpenInNewWindow, trigger: TriggerElement } = props; 3 | 4 | return ( 5 | 11 | ); 12 | }; 13 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/easyblocks/components/RootSectionStack/RootSectionStack.tsx: -------------------------------------------------------------------------------- 1 | import React, { Fragment, ReactElement } from "react"; 2 | 3 | export function RootSectionStack(props: { 4 | data: Array; 5 | ItemWrappers: Array; 6 | }) { 7 | const { data, ItemWrappers: itemWrappers } = props; 8 | 9 | return ( 10 | 11 | {data.map((Item, index) => { 12 | const ItemWrapper = itemWrappers[index]; 13 | 14 | return ( 15 | 16 | 17 | 18 | ); 19 | })} 20 | 21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/easyblocks/components/SimpleBanner/SimpleBanner.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement } from "react"; 2 | 3 | type SimpleBannerProps = { 4 | Root: ReactElement; 5 | Title: ReactElement; 6 | Wrapper: ReactElement; 7 | Buttons: ReactElement[]; 8 | ButtonsWrapper: ReactElement; 9 | }; 10 | 11 | export function SimpleBanner(props: SimpleBannerProps) { 12 | const { Root, Title, Wrapper, Buttons, ButtonsWrapper } = props; 13 | 14 | return ( 15 | 16 | 17 | 18 | 19 | {Buttons.map((Button, index) => ( 20 | 21 | ))} 22 | 23 | 24 | 25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/easyblocks/components/SolidColor/SolidColor.definition.ts: -------------------------------------------------------------------------------- 1 | import { NoCodeComponentDefinition, TokenValue } from "@easyblocks/core"; 2 | import { solidColorStyles } from "./SolidColor.styles"; 3 | 4 | const solidColorComponentDefinition: NoCodeComponentDefinition = { 5 | id: "SolidColor", 6 | label: "Solid color", 7 | styles: solidColorStyles, 8 | thumbnail: 9 | "https://shopstory.s3.eu-central-1.amazonaws.com/picker_solid_color.png", 10 | schema: [ 11 | { 12 | prop: "color", 13 | label: "Color", 14 | type: "color", 15 | }, 16 | ], 17 | preview({ values }) { 18 | const colorValue = values.color as TokenValue; 19 | 20 | return { 21 | thumbnail: { 22 | type: "color", 23 | color: colorValue.value, 24 | }, 25 | description: colorValue.tokenId ?? colorValue.value, 26 | }; 27 | }, 28 | }; 29 | 30 | export { solidColorComponentDefinition }; 31 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/easyblocks/components/SolidColor/SolidColor.styles.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | NoCodeComponentStylesFunctionInput, 3 | NoCodeComponentStylesFunctionResult, 4 | } from "@easyblocks/core"; 5 | 6 | export function solidColorStyles({ 7 | values, 8 | }: NoCodeComponentStylesFunctionInput): NoCodeComponentStylesFunctionResult { 9 | return { 10 | styled: { 11 | Root: { 12 | position: "relative", 13 | backgroundColor: values.color, 14 | }, 15 | }, 16 | }; 17 | } 18 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/easyblocks/components/SolidColor/SolidColor.tsx: -------------------------------------------------------------------------------- 1 | import React, { ReactElement } from "react"; 2 | 3 | function SolidColor(props: { Root: ReactElement }) { 4 | const { Root } = props; 5 | return ; 6 | } 7 | 8 | export { SolidColor }; 9 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/easyblocks/components/Stack/Stack.types.ts: -------------------------------------------------------------------------------- 1 | import { Spacing } from "@easyblocks/core"; 2 | 3 | export type StackCompiledValues = { 4 | Items: Array<{ 5 | _component: string; 6 | width: string; 7 | marginBottom: Spacing; 8 | align: string; 9 | }>; 10 | }; 11 | 12 | export type StackParams = { 13 | paddingLeft?: Spacing; 14 | paddingRight?: Spacing; 15 | paddingTop?: Spacing; 16 | paddingBottom?: Spacing; 17 | passedAlign?: string; 18 | }; 19 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/easyblocks/components/TextLink/TextLink.tsx: -------------------------------------------------------------------------------- 1 | import { NoCodeComponentProps } from "@easyblocks/core"; 2 | 3 | function TextLink({ 4 | Link, 5 | url, 6 | shouldOpenInNewWindow, 7 | children, 8 | __easyblocks, 9 | }: NoCodeComponentProps & Record) { 10 | return ( 11 | 19 | {children} 20 | 21 | ); 22 | } 23 | 24 | export { TextLink }; 25 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/easyblocks/components/TwoCards/TwoCards.types.ts: -------------------------------------------------------------------------------- 1 | import { Spacing } from "@easyblocks/core"; 2 | 3 | export type TwoCardsCompiledValues = { 4 | Card1: any[]; 5 | Card2: any[]; 6 | card1Width: string; 7 | card2Width: string; 8 | card1EscapeMargin: boolean; 9 | card2EscapeMargin: boolean; 10 | verticalLayout: string; 11 | verticalOffset: string; 12 | collapse: boolean; 13 | verticalGap: Spacing; 14 | gap: Spacing; 15 | invertCollapsed: boolean; 16 | }; 17 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/easyblocks/components/TwoCards/twoCardsConstants.ts: -------------------------------------------------------------------------------- 1 | export const TWO_CARDS_COL_NUM = 24; 2 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/easyblocks/components/VimeoPlayer/VimeoPlayer.tsx: -------------------------------------------------------------------------------- 1 | import React, { ReactElement } from "react"; 2 | import { VimeoPlayerInternal } from "./VimeoPlayerInternal"; 3 | 4 | export function VimeoPlayer(props: { 5 | AspectRatioMaker: ReactElement; 6 | ContentWrapper: ReactElement; 7 | Wrapper: ReactElement; 8 | }) { 9 | const { AspectRatioMaker, ContentWrapper, Wrapper } = props; 10 | 11 | return ( 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/easyblocks/components/utils/corners.ts: -------------------------------------------------------------------------------- 1 | import { pxValueNormalize } from "@/app/easyblocks/components/utils/pxValueNormalize"; 2 | import { SchemaProp } from "@easyblocks/core"; 3 | 4 | export const cornerSchemaProps: SchemaProp[] = [ 5 | { 6 | prop: "cornerRadius", 7 | label: "Radius", 8 | group: "Corner", 9 | type: "string", 10 | responsive: true, 11 | params: { 12 | normalize: pxValueNormalize(0, 32), 13 | }, 14 | defaultValue: "0", 15 | }, 16 | ]; 17 | 18 | export function cornerStyles( 19 | values: Record, 20 | disable: boolean = false 21 | ) { 22 | return { 23 | borderRadius: disable ? "initial" : `${values.cornerRadius}px`, 24 | }; 25 | } 26 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/easyblocks/components/utils/getFieldProvider.ts: -------------------------------------------------------------------------------- 1 | import { EditingInfo } from "@easyblocks/core"; 2 | 3 | export const getFieldProvider = (editingInfo: EditingInfo) => (path: string) => 4 | editingInfo.fields.find((field) => field.path === path)!; 5 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/easyblocks/components/utils/parseAspectRatio.ts: -------------------------------------------------------------------------------- 1 | function parseAspectRatio(aspectRatio: string): number { 2 | const error = new Error(`wrong input to parseAspectRatio: ${aspectRatio}`); 3 | 4 | const split = aspectRatio.split(":"); 5 | const val1 = parseInt(split[0]); 6 | const val2 = parseInt(split[1]); 7 | 8 | if (isNaN(val1) || isNaN(val2)) { 9 | throw error; 10 | } 11 | 12 | const result = val2 / val1; 13 | 14 | if (isNaN(result)) { 15 | throw error; 16 | } 17 | 18 | return result; 19 | } 20 | 21 | export function getPaddingBottomFromAspectRatio(aspectRatio: string) { 22 | return `${parseAspectRatio(aspectRatio) * 100}%`; 23 | } 24 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/easyblocks/components/utils/pxValueNormalize.ts: -------------------------------------------------------------------------------- 1 | export function pxValueNormalize(from: number, to: number) { 2 | return (x: string) => { 3 | const num = parseInt(x); 4 | if (isNaN(num)) { 5 | return null; 6 | } 7 | 8 | if (num < from || num > to) { 9 | return null; 10 | } 11 | 12 | return num.toString(); 13 | }; 14 | } 15 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/easyblocks/components/utils/toStartEnd.ts: -------------------------------------------------------------------------------- 1 | export function toStartEnd( 2 | position: "left" | "center" | "right" | "top" | "bottom" 3 | ) { 4 | if (position === "left" || position === "top") { 5 | return "start"; 6 | } else if (position === "center") { 7 | return "center"; 8 | } else if (position === "right" || position === "bottom") { 9 | return "end"; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/easyblocks/externalData/mockMedia/Media.ts: -------------------------------------------------------------------------------- 1 | export type Media = { 2 | id: string; 3 | title: string; 4 | url: string; 5 | mimeType: string; 6 | isVideo: boolean; 7 | thumbnail?: string; 8 | width?: number; 9 | height?: number; 10 | }; 11 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/easyblocks/externalData/mockMedia/MockImagePicker.tsx: -------------------------------------------------------------------------------- 1 | import type { WidgetComponentProps } from "@easyblocks/core"; 2 | import { MediaPicker } from "./MediaPicker"; 3 | 4 | export function MockImagePicker({ 5 | id, 6 | onChange, 7 | }: WidgetComponentProps) { 8 | return ; 9 | } 10 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/easyblocks/externalData/mockMedia/MockVideoPicker.tsx: -------------------------------------------------------------------------------- 1 | import type { WidgetComponentProps } from "@easyblocks/core"; 2 | import { MediaPicker } from "./MediaPicker"; 3 | 4 | export function MockVideoPicker({ 5 | id, 6 | onChange, 7 | }: WidgetComponentProps) { 8 | return ; 9 | } 10 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/easyblocks/externalData/mockMedia/mockImageWidget.tsx: -------------------------------------------------------------------------------- 1 | import type { Widget } from "@easyblocks/core"; 2 | 3 | export const mockImageWidget: Widget = { 4 | id: "mockImage", 5 | label: "Library", 6 | }; 7 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/easyblocks/externalData/mockMedia/mockVideoWidget.tsx: -------------------------------------------------------------------------------- 1 | import type { Widget } from "@easyblocks/core"; 2 | 3 | export const mockVideoWidget: Widget = { 4 | id: "mockVideo", 5 | label: "Library", 6 | }; 7 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/easyblocks/externalData/pexels/pexelsShared.ts: -------------------------------------------------------------------------------- 1 | export const PEXELS_VIDEO_WIDGET_ID = "@pexels/video"; 2 | export const PEXELS_IMAGE_WIDGET_ID = "@pexels/photo"; 3 | 4 | const pexelsApiKey = "us4Cvb33zDTkpH0RqYmuSgzyx0Nnc4qwDkIMkDMOuCw5giL06WmSONgY"; 5 | 6 | export const pexelsApiFetch: (path: `/${string}`) => Promise = async ( 7 | path 8 | ) => { 9 | return fetch(`https://api.pexels.com${path}`, { 10 | headers: { 11 | Authorization: pexelsApiKey, 12 | }, 13 | }); 14 | }; 15 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/easyblocks/externalData/product/productShared.ts: -------------------------------------------------------------------------------- 1 | export const PRODUCT_WIDGET_ID = "product"; 2 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/easyblocks/externalData/types.ts: -------------------------------------------------------------------------------- 1 | type ImageSrcSetEntry = { 2 | w: number; 3 | h: number; 4 | url: string; 5 | }; 6 | 7 | export type ImageSrc = { 8 | alt: string; 9 | url: string; 10 | aspectRatio: number; 11 | srcset: ImageSrcSetEntry[]; 12 | mimeType: string; 13 | }; 14 | 15 | export type VideoSrc = { 16 | alt: string; 17 | url: string; 18 | aspectRatio: number; 19 | }; 20 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/easyblocks/templates/basic/BasicCard_empty.json: -------------------------------------------------------------------------------- 1 | { 2 | "_id": "2daa681f-f8d8-49ec-abdf-7e4807a7ac07", 3 | "_component": "BannerCard" 4 | } 5 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/easyblocks/templates/nooma/BannerSection1/NoomaBannerSection1.ts: -------------------------------------------------------------------------------- 1 | import { Template } from "@easyblocks/core"; 2 | import entry from "./NoomaBannerSection1Entry.json"; 3 | 4 | export const NoomaBannerSection1: Template = { 5 | id: "BannerSection1", 6 | entry: entry, 7 | thumbnail: 8 | "https://images.ctfassets.net/blh4anz05qu1/2lBJl4UUaSkpiadI6vOT7b/82f09dd9b1d700ff4f2e43c89f31f74b/Screenshot_2023-10-16_at_12.24.20.png", 9 | }; 10 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/easyblocks/templates/nooma/BannerSection2/NoomaBannerSection2.ts: -------------------------------------------------------------------------------- 1 | import { Template } from "@easyblocks/core"; 2 | import entry from "./NoomaBannerSection2Entry.json"; 3 | 4 | export const NoomaBannerSection2: Template = { 5 | id: "BannerSection2", 6 | entry: entry, 7 | thumbnail: 8 | "https://images.ctfassets.net/blh4anz05qu1/7E9ZU6yqFUj8EgTz0zXqtf/93558c209d1f087f7b29a9795c53aea7/Screenshot_2023-10-16_at_13.22.35.png", 9 | }; 10 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/easyblocks/templates/nooma/BannerSection3/NoomaBannerSection3.ts: -------------------------------------------------------------------------------- 1 | import { Template } from "@easyblocks/core"; 2 | import entry from "./NoomaBannerSection3Entry.json"; 3 | 4 | export const NoomaBannerSection3: Template = { 5 | id: "BannerSection3", 6 | entry: entry, 7 | thumbnail: 8 | "https://images.ctfassets.net/blh4anz05qu1/7GjSPRpFYrh2wvCeBSSg3Z/9f11e67bfb7b4dae813f85cc377ee8c8/Screenshot_2023-10-16_at_13.37.45.png", 9 | }; 10 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/easyblocks/templates/nooma/BannerSection4/NoomaBannerSection4.ts: -------------------------------------------------------------------------------- 1 | import { Template } from "@easyblocks/core"; 2 | import entry from "./NoomaBannerSection4Entry.json"; 3 | 4 | export const NoomaBannerSection4: Template = { 5 | id: "BannerSection4", 6 | entry: entry, 7 | thumbnail: 8 | "https://images.ctfassets.net/blh4anz05qu1/6W4fwr5kLP90HbywlGTguE/f8ab0051f16ae8e3ac26ee771eafab1f/Screenshot_2023-10-16_at_15.26.32.png", 9 | }; 10 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/easyblocks/templates/nooma/BannerSection5/NoomaBannerSection5.ts: -------------------------------------------------------------------------------- 1 | import { Template } from "@easyblocks/core"; 2 | import entry from "./NoomaBannerSection5Entry.json"; 3 | 4 | export const NoomaBannerSection5: Template = { 5 | id: "BannerSection5", 6 | entry: entry, 7 | thumbnail: 8 | "https://images.ctfassets.net/blh4anz05qu1/15nwuZM92JbQQywMCZV7VP/28683a534c5b935c689d8b4b527fe88b/Screenshot_2023-10-16_at_15.56.45.png", 9 | }; 10 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/easyblocks/templates/nooma/BasicCard1/NoomaBasicCard1.ts: -------------------------------------------------------------------------------- 1 | import { Template } from "@easyblocks/core"; 2 | import entry from "./NoomaBasicCard1Entry.json"; 3 | 4 | export const NoomaBasicCard1: Template = { 5 | id: "BasicCard1", 6 | entry: entry, 7 | thumbnail: 8 | "https://images.ctfassets.net/blh4anz05qu1/3IFi8EhTHyZ4syRflhzpO/3964969a54a78d02b3075a07e0f4ebfe/Screenshot_2023-10-16_at_16.09.23.png", 9 | }; 10 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/easyblocks/templates/nooma/BasicCard2/NoomaBasicCard2.ts: -------------------------------------------------------------------------------- 1 | import { Template } from "@easyblocks/core"; 2 | import entry from "./NoomaBasicCard2Entry.json"; 3 | 4 | export const NoomaBasicCard2: Template = { 5 | id: "BasicCard2", 6 | entry: entry, 7 | thumbnail: 8 | "https://images.ctfassets.net/blh4anz05qu1/4TKdp7GmUp7gOYPVKDR7Zj/c96e32d829798cf882b43199dc829c36/Screenshot_2023-10-16_at_16.13.57.png", 9 | }; 10 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/easyblocks/templates/nooma/BasicCard3/NoomaBasicCard3.ts: -------------------------------------------------------------------------------- 1 | import { Template } from "@easyblocks/core"; 2 | import entry from "./NoomaBasicCard3Entry.json"; 3 | 4 | export const NoomaBasicCard3: Template = { 5 | id: "BasicCard3", 6 | entry: entry, 7 | thumbnail: 8 | "https://images.ctfassets.net/blh4anz05qu1/KpMpJxM64cBObZinCU5Of/61c787895bb51c3fa006a59cc12838ab/Screenshot_2023-10-16_at_16.17.47.png", 9 | }; 10 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/easyblocks/templates/nooma/BasicCard4/NoomaBasicCard4.ts: -------------------------------------------------------------------------------- 1 | import { Template } from "@easyblocks/core"; 2 | import entry from "./NoomaBasicCard4Entry.json"; 3 | 4 | export const NoomaBasicCard4: Template = { 5 | id: "BasicCard4", 6 | entry: entry, 7 | thumbnail: 8 | "https://images.ctfassets.net/blh4anz05qu1/1iXsqbwod4yP2Ftw7E384f/ba37ec0536ee72d02f697b9fa91c84f0/Screenshot_2023-10-16_at_16.20.24.png", 9 | }; 10 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/easyblocks/templates/nooma/BasicCard5/NoomaBasicCard5.ts: -------------------------------------------------------------------------------- 1 | import { Template } from "@easyblocks/core"; 2 | import entry from "./NoomaBasicCard5Entry.json"; 3 | 4 | export const NoomaBasicCard5: Template = { 5 | id: "BasicCard5", 6 | entry: entry, 7 | thumbnail: 8 | "https://images.ctfassets.net/blh4anz05qu1/aDSFdqEz6mIpxnrD4WD3G/5eafe264cad75f441b4721a1d6e42e56/Screenshot_2023-10-16_at_16.22.27.png", 9 | }; 10 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/easyblocks/templates/nooma/Grid1/NoomaGrid1.ts: -------------------------------------------------------------------------------- 1 | import { Template } from "@easyblocks/core"; 2 | import entry from "./NoomaGrid1Entry.json"; 3 | 4 | export const NoomaGrid1: Template = { 5 | id: "Grid1", 6 | entry: entry, 7 | thumbnail: 8 | "https://images.ctfassets.net/blh4anz05qu1/4dsSRhhr5udchcZhEQ3Atr/b3918636d0ccf2091f35bbf252d17d2a/Screenshot_2023-10-16_at_14.53.42.png", 9 | }; 10 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/easyblocks/templates/nooma/Grid2/NoomaGrid2.ts: -------------------------------------------------------------------------------- 1 | import { Template } from "@easyblocks/core"; 2 | import entry from "./NoomaGrid2Entry.json"; 3 | 4 | export const NoomaGrid2: Template = { 5 | id: "Grid2", 6 | entry: entry, 7 | thumbnail: 8 | "https://images.ctfassets.net/blh4anz05qu1/7EGShDCsJIzKF8VKAP5uR2/dcdab83b4dfd59c059c36a5c994e3ebe/Screenshot_2023-10-16_at_14.59.47.png", 9 | }; 10 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/easyblocks/templates/nooma/Grid3/NoomaGrid3.ts: -------------------------------------------------------------------------------- 1 | import { Template } from "@easyblocks/core"; 2 | import entry from "./NoomaGrid3Entry.json"; 3 | 4 | export const NoomaGrid3: Template = { 5 | id: "Grid3", 6 | entry: entry, 7 | thumbnail: 8 | "https://images.ctfassets.net/blh4anz05qu1/1S3OvGb58O3yid9EGYxF6X/34f7a93b2adce5125ddf96a994efe5d7/Screenshot_2023-10-16_at_15.06.26.png", 9 | }; 10 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/easyblocks/templates/nooma/Grid4/NoomaGrid4.ts: -------------------------------------------------------------------------------- 1 | import { Template } from "@easyblocks/core"; 2 | import entry from "./NoomaGrid4Entry.json"; 3 | 4 | export const NoomaGrid4: Template = { 5 | id: "Grid4", 6 | entry: entry, 7 | thumbnail: 8 | "https://images.ctfassets.net/blh4anz05qu1/2Cf0Od05keCKatpBXPZwHB/de03290cf7b22ae19a8e7cd642468faa/Screenshot_2023-10-16_at_15.11.11.png", 9 | }; 10 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/easyblocks/templates/nooma/Grid5/NoomaGrid5.ts: -------------------------------------------------------------------------------- 1 | import { Template } from "@easyblocks/core"; 2 | import entry from "./NoomaGrid5Entry.json"; 3 | 4 | export const NoomaGrid5: Template = { 5 | id: "Grid5", 6 | entry: entry, 7 | thumbnail: 8 | "https://images.ctfassets.net/blh4anz05qu1/1ICEg2xesO0vZhIs21aQYI/49b3d52dcc5d5d95f16ce43bf164a8e1/Screenshot_2023-10-16_at_15.16.41.png", 9 | }; 10 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/easyblocks/templates/nooma/ProductCard1/NoomaProductCard1.ts: -------------------------------------------------------------------------------- 1 | import { Template } from "@easyblocks/core"; 2 | import entry from "./NoomaProductCard1Entry.json"; 3 | 4 | export const NoomaProductCard1: Template = { 5 | id: "ProductCard1", 6 | entry: entry, 7 | thumbnail: 8 | "https://images.ctfassets.net/blh4anz05qu1/3gZE2gBjMmDppU9h0DISSp/62501a914805d235a364594c87c6845c/Screenshot_2023-10-16_at_16.05.26.png", 9 | }; 10 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/easyblocks/templates/nooma/ProductCard1/NoomaProductCard1Entry.json: -------------------------------------------------------------------------------- 1 | { 2 | "_id": "d72cebfd-ed3d-41a2-837f-3ec84c551d96", 3 | "_component": "ProductCard", 4 | "_itemProps": { 5 | "Grid": { 6 | "Cards": { 7 | "itemSize": { 8 | "$res": true, 9 | "xl": "1x1" 10 | }, 11 | "verticalAlign": { 12 | "$res": true, 13 | "xl": "auto" 14 | } 15 | } 16 | } 17 | }, 18 | "product": { 19 | "id": "gid://shopify/Product/7620416209108", 20 | "widgetId": "product", 21 | "key": "self" 22 | }, 23 | "relatedProductsMode": "enabled", 24 | "withBackdrop": false 25 | } 26 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/easyblocks/templates/nooma/Slider1/NoomaSlider1.ts: -------------------------------------------------------------------------------- 1 | import { Template } from "@easyblocks/core"; 2 | import entry from "./NoomaSlider1Entry.json"; 3 | 4 | export const NoomaSlider1: Template = { 5 | id: "Slider1", 6 | entry: entry, 7 | thumbnail: 8 | "https://images.ctfassets.net/blh4anz05qu1/7puLkIVcgW4P7IIzwtdgv6/dfe2b76e28a5d4f1c2ade3ad04db709b/Screenshot_2023-10-16_at_15.20.26.png", 9 | }; 10 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/easyblocks/templates/nooma/Slider2/NoomaSlider2.ts: -------------------------------------------------------------------------------- 1 | import { Template } from "@easyblocks/core"; 2 | import entry from "./NoomaSlider2Entry.json"; 3 | 4 | export const NoomaSlider2: Template = { 5 | id: "Slider2", 6 | entry: entry, 7 | thumbnail: 8 | "https://images.ctfassets.net/blh4anz05qu1/5UVmxfm7Fa7RGT09UIy5PP/4745a56f0bced3c3273a05a61a4dbefc/Screenshot_2023-10-16_at_15.22.51.png", 9 | }; 10 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/easyblocks/templates/nooma/TwoCards1/NoomaTwoCards1.ts: -------------------------------------------------------------------------------- 1 | import { Template } from "@easyblocks/core"; 2 | import entry from "./NoomaTwoCards1Entry.json"; 3 | 4 | export const NoomaTwoCards1: Template = { 5 | id: "TwoCards1", 6 | entry: entry, 7 | thumbnail: 8 | "https://images.ctfassets.net/blh4anz05qu1/4MuzwPC1hqG9PgskCQNDsd/edb32f846dff36f1dc2f7fef0b3cfdc5/Screenshot_2023-10-16_at_13.45.44.png", 9 | }; 10 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/easyblocks/templates/nooma/TwoCards2/NoomaTwoCards2.ts: -------------------------------------------------------------------------------- 1 | import { Template } from "@easyblocks/core"; 2 | import entry from "./NoomaTwoCards2Entry.json"; 3 | 4 | export const NoomaTwoCards2: Template = { 5 | id: "TwoCards2", 6 | entry: entry, 7 | thumbnail: 8 | "https://images.ctfassets.net/blh4anz05qu1/R2iiiCxFtsGsAvS81Oc1v/08a51a26c1d34d917157b59785c964d7/Screenshot_2023-10-16_at_14.01.33.png", 9 | }; 10 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/easyblocks/templates/nooma/TwoCards3/NoomaTwoCards3.ts: -------------------------------------------------------------------------------- 1 | import { Template } from "@easyblocks/core"; 2 | import entry from "./NoomaTwoCards3Entry.json"; 3 | 4 | export const NoomaTwoCards3: Template = { 5 | id: "TwoCards3", 6 | entry: entry, 7 | thumbnail: 8 | "https://images.ctfassets.net/blh4anz05qu1/3TjTIsNy3aMEu7LG1KNK03/025242f17c066ccbe7949eeff0776e60/Screenshot_2023-10-16_at_14.11.06.png", 9 | }; 10 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/easyblocks/templates/nooma/TwoCards4/NoomaTwoCards4.ts: -------------------------------------------------------------------------------- 1 | import { Template } from "@easyblocks/core"; 2 | import entry from "./NoomaTwoCards4Entry.json"; 3 | 4 | export const NoomaTwoCards4: Template = { 5 | id: "TwoCards4", 6 | entry: entry, 7 | thumbnail: 8 | "https://images.ctfassets.net/blh4anz05qu1/4arNLu2JXthVNUHvTIGVLt/eff0131168a9cdf4d8e9749c9c83a170/Screenshot_2023-10-16_at_14.19.24.png", 9 | }; 10 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/easyblocks/types/UrlWidget.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | import { InlineTypeWidgetComponentProps } from "@easyblocks/core"; 3 | import { Input } from "@easyblocks/design-system"; 4 | import { useState } from "react"; 5 | 6 | function UrlWidget(props: InlineTypeWidgetComponentProps) { 7 | const [active, setActive] = useState(false); 8 | const [value, setValue] = useState(props.value); 9 | 10 | useEffect(() => { 11 | if (!active) { 12 | setValue(props.value); 13 | } 14 | }); 15 | 16 | return ( 17 | { 20 | setActive(true); 21 | setValue(event.target.value); 22 | }} 23 | onBlur={() => { 24 | setActive(false); 25 | props.onChange(value); 26 | }} 27 | align={"right"} 28 | /> 29 | ); 30 | } 31 | 32 | export { UrlWidget }; 33 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/easyblocks/utils/assert.ts: -------------------------------------------------------------------------------- 1 | function assertDefined(value: T, message?: string): Exclude { 2 | if (value === undefined) { 3 | throw new Error(message ?? "Value is undefined"); 4 | } 5 | 6 | return value as Exclude; 7 | } 8 | 9 | function assertNonNullable(value: T): Exclude { 10 | if (value === null) { 11 | throw new Error("Value is null"); 12 | } 13 | 14 | return value as Exclude; 15 | } 16 | 17 | export { assertDefined, assertNonNullable }; 18 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/easyblocks/utils/deepClone.ts: -------------------------------------------------------------------------------- 1 | function deepClone(source: T): T { 2 | return JSON.parse(JSON.stringify(source)) as T; 3 | } 4 | 5 | export { deepClone }; 6 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/easyblocks/utils/last.ts: -------------------------------------------------------------------------------- 1 | function last>(collection: T): T[number] { 2 | return collection[collection.length - 1]; 3 | } 4 | 5 | export { last }; 6 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/easyblocks/utils/range.ts: -------------------------------------------------------------------------------- 1 | function range(start: number, end: number): Array { 2 | const itemsCount = start === end ? 1 : end - start + 1; 3 | 4 | return Array.from({ length: itemsCount }, (_, index) => { 5 | return start + index; 6 | }); 7 | } 8 | 9 | export { range }; 10 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/easyblocks/utils/useForceRerender.ts: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { useRef, useState } from "react"; 3 | 4 | function useForceRerender() { 5 | const [, setDummyState] = useState({}); 6 | 7 | const forceRerender = useRef(() => { 8 | setDummyState({}); 9 | }).current; 10 | 11 | return { forceRerender }; 12 | } 13 | 14 | export { useForceRerender }; 15 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyblockshq/easyblocks/c97eaa1170131224644fd119bb61173b9c8d6e88/apps/page-builder-demo/src/app/favicon.ico -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/fonts/test-national-2-bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyblockshq/easyblocks/c97eaa1170131224644fd119bb61173b9c8d6e88/apps/page-builder-demo/src/app/fonts/test-national-2-bold.woff2 -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/fonts/test-national-2-medium.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyblockshq/easyblocks/c97eaa1170131224644fd119bb61173b9c8d6e88/apps/page-builder-demo/src/app/fonts/test-national-2-medium.woff2 -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/fonts/test-national-2-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyblockshq/easyblocks/c97eaa1170131224644fd119bb61173b9c8d6e88/apps/page-builder-demo/src/app/fonts/test-national-2-regular.woff2 -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/fonts/test-soehne-mono-leicht.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyblockshq/easyblocks/c97eaa1170131224644fd119bb61173b9c8d6e88/apps/page-builder-demo/src/app/fonts/test-soehne-mono-leicht.woff2 -------------------------------------------------------------------------------- /apps/page-builder-demo/src/app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | body { 6 | -webkit-font-smoothing: antialiased; 7 | font-family: "Inter", sans-serif; 8 | } 9 | 10 | @font-face { 11 | font-family: "test-national-2"; 12 | src: url("fonts/test-national-2-regular.woff2") format("woff2"); 13 | font-display: block; 14 | font-weight: 400; 15 | } 16 | 17 | @font-face { 18 | font-family: "test-national-2"; 19 | src: url("fonts/test-national-2-medium.woff2") format("woff2"); 20 | font-display: block; 21 | font-weight: 600; 22 | } 23 | 24 | @font-face { 25 | font-family: "test-national-2"; 26 | src: url("fonts/test-national-2-bold.woff2") format("woff2"); 27 | font-display: block; 28 | font-weight: 700; 29 | } 30 | 31 | @font-face { 32 | font-family: "test-soehne-mono"; 33 | src: url("fonts/test-soehne-mono-leicht.woff2") format("woff2"); 34 | font-display: block; 35 | } 36 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/data/shopify/fetchProductById.ts: -------------------------------------------------------------------------------- 1 | import { fetchProductsByIds } from "./fetchProductsByIds"; 2 | 3 | export async function fetchProductById(id: string) { 4 | return (await fetchProductsByIds([id]))[0]; 5 | } 6 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/data/shopify/fetchProducts.ts: -------------------------------------------------------------------------------- 1 | import { mapProduct } from "./mapProduct"; 2 | import { removeEdges } from "./removeEdges"; 3 | import fetchShopify from "./fetchShopify"; 4 | import { fetchProductsQuery } from "./graphql/fetchProductsQuery"; 5 | 6 | const fetchProducts = async (query: string) => { 7 | const data: any = await fetchShopify(fetchProductsQuery, { 8 | query, 9 | sortKey: "CREATED_AT", 10 | sortIndex: 0, 11 | reverse: false, 12 | first: 250, 13 | }); 14 | 15 | return removeEdges(data.products).map(mapProduct); 16 | }; 17 | 18 | export { fetchProducts }; 19 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/data/shopify/fetchShopify.ts: -------------------------------------------------------------------------------- 1 | import { GraphQLClient } from "graphql-request"; 2 | 3 | const client = new GraphQLClient( 4 | "https://shopstory-demo.myshopify.com/api/2022-01/graphql.json", 5 | { 6 | headers: { 7 | "Content-Type": "application/json", 8 | "X-Shopify-Storefront-Access-Token": "f8b32eaae0d2dc7996bfc02f2dc33b56", 9 | }, 10 | } 11 | ); 12 | 13 | async function fetchShopify(query: string, variables: any) { 14 | try { 15 | const data = await client.request(query, variables); 16 | return data; 17 | } catch (error: any) { 18 | throw new Error(error); 19 | } 20 | } 21 | 22 | export default fetchShopify; 23 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/data/shopify/formatPrice.ts: -------------------------------------------------------------------------------- 1 | import { ShopifyPrice } from "./types"; 2 | 3 | export const formatPrice = (price: ShopifyPrice) => { 4 | let _amount = price.amount.replace(".0", ".00"); 5 | 6 | if (price.currencyCode === "USD") { 7 | return `$${_amount}`; 8 | } 9 | 10 | if (price.currencyCode === "EUR") { 11 | return `€${_amount}`; 12 | } 13 | 14 | if (price.currencyCode === "PLN") { 15 | return `${_amount}zł`; 16 | } 17 | 18 | if (price.currencyCode === "GBP") { 19 | return `£${_amount}`; 20 | } 21 | 22 | return `${price.currencyCode}${_amount}`; 23 | }; 24 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/data/shopify/graphql/fetchProductsByIds.ts: -------------------------------------------------------------------------------- 1 | import { gql } from "graphql-request"; 2 | import { PRODUCT_FRAGMENT_SLIM } from "./productFragment"; 3 | 4 | export const fetchProductsByIdsQuery = gql` 5 | ${PRODUCT_FRAGMENT_SLIM} 6 | query ($ids: [ID!]!) { 7 | nodes(ids: $ids) { 8 | ... on Product { 9 | ...ProductFragmentSlim 10 | } 11 | } 12 | } 13 | `; 14 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/data/shopify/graphql/fetchProductsQuery.ts: -------------------------------------------------------------------------------- 1 | import { gql } from "graphql-request"; 2 | import { PRODUCTS_FRAGMENT } from "./productFragment"; 3 | 4 | export const fetchProductsQuery = gql` 5 | ${PRODUCTS_FRAGMENT} 6 | query products( 7 | $cursor: String 8 | $query: String! 9 | $sortKey: ProductSortKeys! 10 | $reverse: Boolean! 11 | $first: Int! 12 | ) { 13 | products( 14 | first: $first 15 | after: $cursor 16 | query: $query 17 | sortKey: $sortKey 18 | reverse: $reverse 19 | ) { 20 | ...ProductsFragment 21 | } 22 | } 23 | `; 24 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/data/shopify/graphql/variantFragment.ts: -------------------------------------------------------------------------------- 1 | import { gql } from "graphql-request"; 2 | 3 | export const VariantFragment = gql` 4 | fragment VariantFragment on ProductVariant { 5 | availableForSale 6 | id 7 | title 8 | quantityAvailable 9 | currentlyNotInStock 10 | selectedOptions { 11 | name 12 | value 13 | } 14 | priceV2 { 15 | amount 16 | currencyCode 17 | } 18 | compareAtPriceV2 { 19 | amount 20 | currencyCode 21 | } 22 | product { 23 | id 24 | } 25 | sku 26 | } 27 | `; 28 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/data/shopify/index.ts: -------------------------------------------------------------------------------- 1 | import { fetchProductsByIds } from "./fetchProductsByIds"; 2 | import { fetchProducts } from "./fetchProducts"; 3 | import { fetchProductById } from "./fetchProductById"; 4 | import type { ShopifyProduct } from "./types"; 5 | 6 | export { fetchProducts, fetchProductsByIds, fetchProductById }; 7 | export type { ShopifyProduct }; 8 | -------------------------------------------------------------------------------- /apps/page-builder-demo/src/data/shopify/removeEdges.ts: -------------------------------------------------------------------------------- 1 | export const removeEdges = (data: { 2 | edges: [{ node: Object }]; 3 | }): Array => { 4 | if (!data) return []; 5 | return data.edges.map((edge) => edge.node); 6 | }; 7 | -------------------------------------------------------------------------------- /apps/page-builder-demo/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "noEmit": true, 10 | "esModuleInterop": true, 11 | "module": "esnext", 12 | "moduleResolution": "bundler", 13 | "resolveJsonModule": true, 14 | "isolatedModules": true, 15 | "jsx": "preserve", 16 | "incremental": true, 17 | "plugins": [ 18 | { 19 | "name": "next" 20 | } 21 | ], 22 | "paths": { 23 | "@/*": ["./src/*"] 24 | } 25 | }, 26 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 27 | "exclude": ["node_modules"] 28 | } 29 | -------------------------------------------------------------------------------- /apps/quick-start/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /apps/quick-start/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env*.local 29 | 30 | # vercel 31 | .vercel 32 | 33 | # typescript 34 | *.tsbuildinfo 35 | next-env.d.ts 36 | -------------------------------------------------------------------------------- /apps/quick-start/README.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | Easyblocks Quickstart project. 4 | -------------------------------------------------------------------------------- /apps/quick-start/next.config.js: -------------------------------------------------------------------------------- 1 | const path = require("node:path"); 2 | 3 | /** @type {import('next').NextConfig} */ 4 | const nextConfig = { 5 | webpack: (config) => { 6 | // apps/README.md#Apps and internal packages 7 | config.resolve.modules.unshift(path.resolve(__dirname, "node_modules")); 8 | 9 | return config; 10 | }, 11 | }; 12 | 13 | module.exports = nextConfig; 14 | -------------------------------------------------------------------------------- /apps/quick-start/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "quick-start", 3 | "version": "1.0.10", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "@easyblocks/core": "workspace:*", 13 | "@easyblocks/editor": "workspace:*", 14 | "@types/node": "20.4.2", 15 | "@types/react": "18.2.20", 16 | "@types/react-dom": "18.2.7", 17 | "autoprefixer": "10.4.14", 18 | "eslint": "8.45.0", 19 | "eslint-config-next": "13.4.10", 20 | "next": "13.4.12", 21 | "postcss": "8.4.26", 22 | "react": "18.2.0", 23 | "react-dom": "18.2.0", 24 | "tailwindcss": "3.3.3", 25 | "typescript": "5.1.6" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /apps/quick-start/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /apps/quick-start/src/app/easyblocks-editor/layout.tsx: -------------------------------------------------------------------------------- 1 | import "../globals.css"; 2 | 3 | export default function RootLayout({ 4 | children, 5 | }: { 6 | children: React.ReactNode; 7 | }) { 8 | return ( 9 | 10 | {children} 11 | 12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /apps/quick-start/src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyblockshq/easyblocks/c97eaa1170131224644fd119bb61173b9c8d6e88/apps/quick-start/src/app/favicon.ico -------------------------------------------------------------------------------- /apps/quick-start/src/app/globals.css: -------------------------------------------------------------------------------- 1 | body { 2 | -webkit-font-smoothing: antialiased; 3 | margin: 0; 4 | padding: 0; 5 | } 6 | -------------------------------------------------------------------------------- /apps/quick-start/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "noEmit": true, 10 | "esModuleInterop": true, 11 | "module": "esnext", 12 | "moduleResolution": "bundler", 13 | "resolveJsonModule": true, 14 | "isolatedModules": true, 15 | "jsx": "preserve", 16 | "incremental": true, 17 | "plugins": [ 18 | { 19 | "name": "next" 20 | } 21 | ], 22 | "paths": { 23 | "@/*": ["./src/*"] 24 | } 25 | }, 26 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 27 | "exclude": ["node_modules"] 28 | } 29 | -------------------------------------------------------------------------------- /babel.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["@babel/preset-env", { "targets": "defaults" }], 4 | "@babel/preset-typescript", 5 | "@babel/preset-react" 6 | ], 7 | "plugins": ["babel-plugin-styled-components"] 8 | } 9 | -------------------------------------------------------------------------------- /base.tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig.json", 3 | "compilerOptions": { 4 | "target": "ES5", 5 | "declaration": true, 6 | "declarationMap": true, 7 | "emitDeclarationOnly": true, 8 | "strict": true, 9 | "allowJs": true, 10 | "esModuleInterop": true, 11 | "lib": ["DOM", "ESNext", "DOM.Iterable"], 12 | "module": "Node16", 13 | "moduleResolution": "Node16", 14 | "resolveJsonModule": true, 15 | "jsx": "react", 16 | "skipLibCheck": true, 17 | "noErrorTruncation": true 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /docs/.gitbook/assets/access_token.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyblockshq/easyblocks/c97eaa1170131224644fd119bb61173b9c8d6e88/docs/.gitbook/assets/access_token.png -------------------------------------------------------------------------------- /docs/.gitbook/assets/basic_properties.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyblockshq/easyblocks/c97eaa1170131224644fd119bb61173b9c8d6e88/docs/.gitbook/assets/basic_properties.png -------------------------------------------------------------------------------- /docs/.gitbook/assets/boolean_ui.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyblockshq/easyblocks/c97eaa1170131224644fd119bb61173b9c8d6e88/docs/.gitbook/assets/boolean_ui.gif -------------------------------------------------------------------------------- /docs/.gitbook/assets/collection_ui.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyblockshq/easyblocks/c97eaa1170131224644fd119bb61173b9c8d6e88/docs/.gitbook/assets/collection_ui.gif -------------------------------------------------------------------------------- /docs/.gitbook/assets/color_ui.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyblockshq/easyblocks/c97eaa1170131224644fd119bb61173b9c8d6e88/docs/.gitbook/assets/color_ui.gif -------------------------------------------------------------------------------- /docs/.gitbook/assets/component_collection_ui.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyblockshq/easyblocks/c97eaa1170131224644fd119bb61173b9c8d6e88/docs/.gitbook/assets/component_collection_ui.gif -------------------------------------------------------------------------------- /docs/.gitbook/assets/component_ui.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyblockshq/easyblocks/c97eaa1170131224644fd119bb61173b9c8d6e88/docs/.gitbook/assets/component_ui.gif -------------------------------------------------------------------------------- /docs/.gitbook/assets/custom_input.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyblockshq/easyblocks/c97eaa1170131224644fd119bb61173b9c8d6e88/docs/.gitbook/assets/custom_input.gif -------------------------------------------------------------------------------- /docs/.gitbook/assets/font_ui.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyblockshq/easyblocks/c97eaa1170131224644fd119bb61173b9c8d6e88/docs/.gitbook/assets/font_ui.gif -------------------------------------------------------------------------------- /docs/.gitbook/assets/image (1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyblockshq/easyblocks/c97eaa1170131224644fd119bb61173b9c8d6e88/docs/.gitbook/assets/image (1).png -------------------------------------------------------------------------------- /docs/.gitbook/assets/image (10).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyblockshq/easyblocks/c97eaa1170131224644fd119bb61173b9c8d6e88/docs/.gitbook/assets/image (10).png -------------------------------------------------------------------------------- /docs/.gitbook/assets/image (11).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyblockshq/easyblocks/c97eaa1170131224644fd119bb61173b9c8d6e88/docs/.gitbook/assets/image (11).png -------------------------------------------------------------------------------- /docs/.gitbook/assets/image (12).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyblockshq/easyblocks/c97eaa1170131224644fd119bb61173b9c8d6e88/docs/.gitbook/assets/image (12).png -------------------------------------------------------------------------------- /docs/.gitbook/assets/image (13).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyblockshq/easyblocks/c97eaa1170131224644fd119bb61173b9c8d6e88/docs/.gitbook/assets/image (13).png -------------------------------------------------------------------------------- /docs/.gitbook/assets/image (14).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyblockshq/easyblocks/c97eaa1170131224644fd119bb61173b9c8d6e88/docs/.gitbook/assets/image (14).png -------------------------------------------------------------------------------- /docs/.gitbook/assets/image (15).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyblockshq/easyblocks/c97eaa1170131224644fd119bb61173b9c8d6e88/docs/.gitbook/assets/image (15).png -------------------------------------------------------------------------------- /docs/.gitbook/assets/image (16).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyblockshq/easyblocks/c97eaa1170131224644fd119bb61173b9c8d6e88/docs/.gitbook/assets/image (16).png -------------------------------------------------------------------------------- /docs/.gitbook/assets/image (2).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyblockshq/easyblocks/c97eaa1170131224644fd119bb61173b9c8d6e88/docs/.gitbook/assets/image (2).png -------------------------------------------------------------------------------- /docs/.gitbook/assets/image (3).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyblockshq/easyblocks/c97eaa1170131224644fd119bb61173b9c8d6e88/docs/.gitbook/assets/image (3).png -------------------------------------------------------------------------------- /docs/.gitbook/assets/image (4).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyblockshq/easyblocks/c97eaa1170131224644fd119bb61173b9c8d6e88/docs/.gitbook/assets/image (4).png -------------------------------------------------------------------------------- /docs/.gitbook/assets/image (5).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyblockshq/easyblocks/c97eaa1170131224644fd119bb61173b9c8d6e88/docs/.gitbook/assets/image (5).png -------------------------------------------------------------------------------- /docs/.gitbook/assets/image (6).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyblockshq/easyblocks/c97eaa1170131224644fd119bb61173b9c8d6e88/docs/.gitbook/assets/image (6).png -------------------------------------------------------------------------------- /docs/.gitbook/assets/image (7).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyblockshq/easyblocks/c97eaa1170131224644fd119bb61173b9c8d6e88/docs/.gitbook/assets/image (7).png -------------------------------------------------------------------------------- /docs/.gitbook/assets/image (8).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyblockshq/easyblocks/c97eaa1170131224644fd119bb61173b9c8d6e88/docs/.gitbook/assets/image (8).png -------------------------------------------------------------------------------- /docs/.gitbook/assets/image (9).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyblockshq/easyblocks/c97eaa1170131224644fd119bb61173b9c8d6e88/docs/.gitbook/assets/image (9).png -------------------------------------------------------------------------------- /docs/.gitbook/assets/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyblockshq/easyblocks/c97eaa1170131224644fd119bb61173b9c8d6e88/docs/.gitbook/assets/image.png -------------------------------------------------------------------------------- /docs/.gitbook/assets/product_picker_demo_1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyblockshq/easyblocks/c97eaa1170131224644fd119bb61173b9c8d6e88/docs/.gitbook/assets/product_picker_demo_1.gif -------------------------------------------------------------------------------- /docs/.gitbook/assets/product_widget_picker_simple (1).gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyblockshq/easyblocks/c97eaa1170131224644fd119bb61173b9c8d6e88/docs/.gitbook/assets/product_widget_picker_simple (1).gif -------------------------------------------------------------------------------- /docs/.gitbook/assets/product_widget_picker_simple.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyblockshq/easyblocks/c97eaa1170131224644fd119bb61173b9c8d6e88/docs/.gitbook/assets/product_widget_picker_simple.gif -------------------------------------------------------------------------------- /docs/.gitbook/assets/select_ui.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyblockshq/easyblocks/c97eaa1170131224644fd119bb61173b9c8d6e88/docs/.gitbook/assets/select_ui.gif -------------------------------------------------------------------------------- /docs/.gitbook/assets/space_ui.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyblockshq/easyblocks/c97eaa1170131224644fd119bb61173b9c8d6e88/docs/.gitbook/assets/space_ui.gif -------------------------------------------------------------------------------- /docs/.gitbook/assets/string_ui.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyblockshq/easyblocks/c97eaa1170131224644fd119bb61173b9c8d6e88/docs/.gitbook/assets/string_ui.gif -------------------------------------------------------------------------------- /docs/.gitbook/assets/text_ui.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easyblockshq/easyblocks/c97eaa1170131224644fd119bb61173b9c8d6e88/docs/.gitbook/assets/text_ui.gif -------------------------------------------------------------------------------- /docs/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Table of contents 2 | 3 | - [Introduction](README.md) 4 | - [Getting started](getting-started.md) 5 | 6 | ## Essentials 7 | 8 | - [Editor page](essentials/editor-page.md) 9 | - [Configuration](essentials/configuration.md) 10 | - [Rendering content](essentials/rendering-content.md) 11 | - [No-Code Components](essentials/no-code-components/README.md) 12 | - [Schema](essentials/no-code-components/schema.md) 13 | - [styles function](essentials/no-code-components/styles-function.md) 14 | - [editing function](essentials/no-code-components/editing-function.md) 15 | - [Custom types](essentials/custom-types.md) 16 | - [External data](essentials/external-data.md) 17 | - [Templates](essentials/templates.md) 18 | - [Backend](essentials/backend.md) 19 | -------------------------------------------------------------------------------- /knip.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://unpkg.com/knip@3/schema.json", 3 | "workspaces": { 4 | ".": { 5 | "eslint": { 6 | "config": ["packages/.eslintrc.js"] 7 | } 8 | }, 9 | "apps/*": { 10 | "next": true 11 | }, 12 | "packages/*": { 13 | "project": ["src/**/*.{js,ts,tsx}"] 14 | }, 15 | "packages/core": { 16 | "entry": ["src/index.ts", "src/_internals.ts"], 17 | "ignoreDependencies": ["postcss-value-parser"] 18 | } 19 | }, 20 | "ignore": [ 21 | "apps/dashboard-demo/seed.js", 22 | "apps/website/src/components/TwitterSection/TwitterSection.tsx" 23 | ], 24 | "ignoreBinaries": ["supabase"], 25 | "ignoreDependencies": ["babel-jest"] 26 | } 27 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "node_modules/lerna/schemas/lerna-schema.json", 3 | "version": "1.0.10", 4 | "npmClient": "pnpm", 5 | "useWorkspaces": true 6 | } 7 | -------------------------------------------------------------------------------- /nx.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/nx/schemas/nx-schema.json", 3 | "workspaceLayout": { 4 | "appsDir": "apps", 5 | "libsDir": "packages" 6 | }, 7 | "targetDefaults": { 8 | "build": { 9 | "dependsOn": ["^build"], 10 | "outputs": [ 11 | "{projectRoot}/dist", 12 | "{projectRoot}/build", 13 | "{projectRoot}/src/built", 14 | "{projectRoot}/.next" 15 | ] 16 | }, 17 | "test": { 18 | "dependsOn": [] 19 | } 20 | }, 21 | "tasksRunnerOptions": { 22 | "default": { 23 | "runner": "nx/tasks-runners/default", 24 | "options": { 25 | "cacheableOperations": ["build", "lint", "test", "check"] 26 | } 27 | } 28 | }, 29 | "defaultBase": "main" 30 | } 31 | -------------------------------------------------------------------------------- /packages/build-tools/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@easyblocks/build-tools", 3 | "version": "1.0.10", 4 | "private": true, 5 | "main": "./src/index.js", 6 | "types": "./src/index.js" 7 | } 8 | -------------------------------------------------------------------------------- /packages/core/.babelrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../babel.config.json", 3 | "plugins": ["@babel/plugin-transform-runtime"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/core/LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (C) Shopstory Sp. z o.o, All Rights Reserved 2 | 3 | Unauthorized copying of this file, via any medium is strictly prohibited 4 | Proprietary and confidential 5 | 6 | Written by Andrzej Dąbrowski , September 2021 7 | -------------------------------------------------------------------------------- /packages/core/globals.d.ts: -------------------------------------------------------------------------------- 1 | type ShopstoryFeatureStatus = "enabled" | "disabled"; 2 | 3 | type BooleanString = "true" | "false"; 4 | 5 | declare namespace NodeJS { 6 | interface ProcessEnv { 7 | readonly NEXT_PUBLIC_SHOPSTORY_FEATURE_RICH_TEXT_TEXT_MODIFIERS?: ShopstoryFeatureStatus; 8 | readonly NEXT_PUBLIC_SHOPSTORY_INTERNAL_COMPILATION_DEBUG?: BooleanString; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/core/jest.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from "@jest/types"; 2 | 3 | const config: Config.InitialOptions = { 4 | transform: { 5 | "\\.[jt]sx?$": ["babel-jest", { rootMode: "upward" }], 6 | }, 7 | }; 8 | 9 | export default config; 10 | -------------------------------------------------------------------------------- /packages/core/src/compiler/areWidthsFullyDefined.ts: -------------------------------------------------------------------------------- 1 | import { TrulyResponsiveValue, Devices } from "../types"; 2 | 3 | export function areWidthsFullyDefined( 4 | widths: TrulyResponsiveValue, 5 | devices: Devices 6 | ): boolean { 7 | let areWidthsFullyDefined = true; 8 | 9 | devices.forEach((device) => { 10 | if (widths[device.id] === -1) { 11 | areWidthsFullyDefined = false; 12 | } 13 | }); 14 | 15 | return areWidthsFullyDefined; 16 | } 17 | -------------------------------------------------------------------------------- /packages/core/src/compiler/box.test.ts: -------------------------------------------------------------------------------- 1 | import { testDevices } from "../testUtils"; 2 | import { getBoxStyles } from "./box"; 3 | 4 | test("corrects order of non responsive and breakpoint properties (from highest breakpoint, to lowest)", () => { 5 | const styles = getBoxStyles( 6 | { 7 | "@b1": { 8 | textAlign: "left", 9 | }, 10 | textAlign: "center", 11 | "@b3": { 12 | textAlign: "right", 13 | }, 14 | }, 15 | testDevices 16 | ); 17 | 18 | expect(styles).toStrictEqual({ 19 | "@media (max-width: 349px)": { 20 | textAlign: "right", 21 | }, 22 | textAlign: "center", 23 | "@media (max-width: 149px)": { 24 | textAlign: "left", 25 | }, 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /packages/core/src/compiler/builtins/$richText/$richText.client.tsx: -------------------------------------------------------------------------------- 1 | import React, { ReactElement, ReactNode } from "react"; 2 | 3 | interface RichTextProps { 4 | elements: Array; 5 | Root: ReactElement<{ children: ReactNode }>; 6 | } 7 | 8 | function RichTextClient(props: RichTextProps) { 9 | const { elements: Elements, Root } = props; 10 | 11 | return ( 12 | 13 | {Elements.map((Element, index) => { 14 | return ; 15 | })} 16 | 17 | ); 18 | } 19 | 20 | export { RichTextClient }; 21 | -------------------------------------------------------------------------------- /packages/core/src/compiler/builtins/$richText/$richText.constants.ts: -------------------------------------------------------------------------------- 1 | export const RICH_TEXT_CONFIG_SYNC_THROTTLE_TIMEOUT = 150; 2 | export const RICH_TEXT_FOCUSED_FIELDS_SYNC_THROTTLE_TIMEOUT = 100; 3 | -------------------------------------------------------------------------------- /packages/core/src/compiler/builtins/$richText/$richTextPart/$richTextPart.client.tsx: -------------------------------------------------------------------------------- 1 | import type { ReactElement, ReactNode } from "react"; 2 | import React from "react"; 3 | 4 | type RichTextPartProps = { 5 | TextWrapper: React.ReactElement<{ trigger: React.ReactElement }> | undefined; 6 | // ReactElement when editing, string when not 7 | value: string | ReactElement; 8 | Text: React.ReactElement<{ children: ReactNode; style: Record }>; 9 | }; 10 | 11 | export function RichTextPartClient(props: RichTextPartProps) { 12 | const { value, Text, TextWrapper } = props; 13 | const textValue = value || "\uFEFF"; 14 | 15 | if (TextWrapper) { 16 | return ( 17 | 18 | {textValue} 19 | 20 | ); 21 | } 22 | 23 | return {textValue}; 24 | } 25 | -------------------------------------------------------------------------------- /packages/core/src/compiler/builtins/$richText/getAbsoluteRichTextPartPath.ts: -------------------------------------------------------------------------------- 1 | function getAbsoluteRichTextPartPath( 2 | relativeRichTextPartPath: string, 3 | richTextPath: string, 4 | locale: string 5 | ) { 6 | return `${richTextPath}.elements.${locale}.${relativeRichTextPartPath}`; 7 | } 8 | 9 | export { getAbsoluteRichTextPartPath }; 10 | -------------------------------------------------------------------------------- /packages/core/src/compiler/builtins/$richText/utils/createTemporaryEditor.ts: -------------------------------------------------------------------------------- 1 | import { createEditor, Editor } from "slate"; 2 | import { withReact } from "slate-react"; 3 | import { withEasyblocks } from "../withEasyblocks"; 4 | 5 | // Slate's transforms methods mutates given editor instance. 6 | // By creating temporary editor instance we can apply all transformations without 7 | // touching original editor and read result from `temporaryEditor.children` 8 | function createTemporaryEditor( 9 | editor: Pick 10 | ): Editor { 11 | const temporaryEditor = withEasyblocks(withReact(createEditor())); 12 | temporaryEditor.children = [...editor.children]; 13 | temporaryEditor.selection = editor.selection ? { ...editor.selection } : null; 14 | return temporaryEditor; 15 | } 16 | 17 | export { createTemporaryEditor }; 18 | -------------------------------------------------------------------------------- /packages/core/src/compiler/builtins/$richText/utils/getFocusedFieldsFromSlateSelection.ts: -------------------------------------------------------------------------------- 1 | import type { Editor } from "slate"; 2 | import { getAbsoluteRichTextPartPath } from "../getAbsoluteRichTextPartPath"; 3 | import { getFocusedRichTextPartsConfigPaths } from "./getFocusedRichTextPartsConfigPaths"; 4 | 5 | function getFocusedFieldsFromSlateSelection( 6 | editor: Editor, 7 | richTextComponentConfigPath: string, 8 | locale: string 9 | ) { 10 | if (editor.selection === null) { 11 | return undefined; 12 | } 13 | 14 | const focusedRichTextPartPaths = getFocusedRichTextPartsConfigPaths(editor); 15 | 16 | const focusedFields = focusedRichTextPartPaths.map((richTextPartPath) => 17 | getAbsoluteRichTextPartPath( 18 | richTextPartPath, 19 | richTextComponentConfigPath, 20 | locale 21 | ) 22 | ); 23 | return focusedFields; 24 | } 25 | 26 | export { getFocusedFieldsFromSlateSelection }; 27 | -------------------------------------------------------------------------------- /packages/core/src/compiler/builtins/$richText/utils/stripRichTextTextPartSelection.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * When selecting text within $richText, we keep information about which text parts are selected 3 | * within focused fields. If the text part is partially selected, we add information about the selection. 4 | * This selection has format: ".{textPartCharacterSelectionStartIndex,textPartCharacterSelectionEndIndex}". 5 | * We often want to query related to selection text part component config and to do that correctly we need to 6 | * strip information about selection. 7 | */ 8 | function stripRichTextPartSelection(value: string): string { 9 | return value.replace(/\.\{\d+,\d+\}$/g, ""); 10 | } 11 | export { stripRichTextPartSelection }; 12 | -------------------------------------------------------------------------------- /packages/core/src/compiler/builtins/$text/$text.client.tsx: -------------------------------------------------------------------------------- 1 | import { cleanString } from "@easyblocks/utils"; 2 | import React, { ReactElement } from "react"; 3 | 4 | type TextProps = { 5 | value?: string; 6 | Text: ReactElement; 7 | }; 8 | 9 | function TextClient(props: TextProps) { 10 | const { value, Text } = props; 11 | 12 | // We need to transform new lines into
13 | const lines = cleanString(value || "").split(/(?:\r\n|\r|\n)/g); 14 | 15 | const elements: React.ReactElement[] = []; 16 | 17 | lines.forEach((line, index) => { 18 | elements.push({line}); 19 | if (index !== lines.length - 1) { 20 | elements.push(
); 21 | } 22 | }); 23 | 24 | return {elements}; 25 | } 26 | 27 | export { TextClient }; 28 | -------------------------------------------------------------------------------- /packages/core/src/compiler/builtins/$text/buildText.ts: -------------------------------------------------------------------------------- 1 | import { uniqueId } from "@easyblocks/utils"; 2 | import { getDefaultLocale } from "../../../locales"; 3 | import { EditorContextType } from "../../types"; 4 | 5 | export function buildText(x: string, editorContext: EditorContextType) { 6 | const defaultLocale = getDefaultLocale(editorContext.locales); 7 | 8 | return { 9 | id: "locale." + uniqueId(), 10 | value: { 11 | [defaultLocale.code]: x, 12 | }, 13 | }; 14 | } 15 | -------------------------------------------------------------------------------- /packages/core/src/compiler/configFindAllPaths.ts: -------------------------------------------------------------------------------- 1 | import { NoCodeComponentEntry } from "../types"; 2 | import { traverseComponents } from "./traverseComponents"; 3 | import { CompilationContextType } from "./types"; 4 | 5 | function configFindAllPaths( 6 | config: NoCodeComponentEntry, 7 | editorContext: CompilationContextType, 8 | predicate: ( 9 | config: NoCodeComponentEntry, 10 | editorContext: CompilationContextType 11 | ) => config is T 12 | ): Array { 13 | const matchedConfigPaths: Array = []; 14 | 15 | traverseComponents(config, editorContext, ({ path, componentConfig }) => { 16 | if (predicate(componentConfig, editorContext)) { 17 | matchedConfigPaths.push(path); 18 | } 19 | }); 20 | 21 | return matchedConfigPaths; 22 | } 23 | 24 | export { configFindAllPaths }; 25 | -------------------------------------------------------------------------------- /packages/core/src/compiler/index.ts: -------------------------------------------------------------------------------- 1 | export { CompilationCache } from "./CompilationCache"; 2 | export type { CompilationCacheItemValue } from "./CompilationCache"; 3 | export { compile } from "./public/compile"; 4 | export { compileInternal } from "./compileInternal"; 5 | export { createCompilationContext } from "./createCompilationContext"; 6 | export { getSchemaDefinition } from "./definitions"; 7 | export type { SchemaPropDefinitionProviders } from "./definitions"; 8 | export { findExternals } from "./public/findResources"; 9 | export { normalize } from "./normalize"; 10 | export { validate } from "./validation"; 11 | export { normalizeInput } from "./normalizeInput"; 12 | export { mergeCompilationMeta } from "./mergeCompilationMeta"; 13 | -------------------------------------------------------------------------------- /packages/core/src/compiler/isContextEditorContext.ts: -------------------------------------------------------------------------------- 1 | import { CompilationContextType, EditorContextType } from "./types"; 2 | 3 | function isContextEditorContext( 4 | context: CompilationContextType 5 | ): context is EditorContextType { 6 | return "form" in context; 7 | } 8 | 9 | export { isContextEditorContext }; 10 | -------------------------------------------------------------------------------- /packages/core/src/compiler/normalize.ts: -------------------------------------------------------------------------------- 1 | import { SetOptional } from "type-fest"; 2 | import { NoCodeComponentEntry } from "../types"; 3 | import { normalizeComponent } from "./definitions"; 4 | import { CompilationContextType } from "./types"; 5 | 6 | export function normalize( 7 | configComponent: SetOptional, 8 | compilationContext: CompilationContextType 9 | ): NoCodeComponentEntry { 10 | return normalizeComponent(configComponent, compilationContext); 11 | } 12 | -------------------------------------------------------------------------------- /packages/core/src/compiler/normalizeInput.ts: -------------------------------------------------------------------------------- 1 | import { isDocument } from "../checkers"; 2 | import { NoCodeComponentEntry } from "../types"; 3 | import { isLegacyInput } from "./validation"; 4 | 5 | export function normalizeInput(input: unknown): NoCodeComponentEntry { 6 | if (isLegacyInput(input)) { 7 | return input; 8 | } 9 | 10 | if (isDocument(input) && input.entry) { 11 | return input.entry; 12 | } 13 | 14 | throw new Error("Internal error: Can't obtain config from remote document."); 15 | } 16 | -------------------------------------------------------------------------------- /packages/core/src/compiler/schema/buttonSchemaProps.ts: -------------------------------------------------------------------------------- 1 | import { ComponentSchemaProp } from "../../types"; 2 | 3 | export const buttonOptionalIconSchemaProp: ComponentSchemaProp = { 4 | prop: "symbol", 5 | label: "Symbol", 6 | type: "component", 7 | accepts: ["symbol"], 8 | visible: true, 9 | group: "Properties", 10 | }; 11 | 12 | export const buttonRequiredIconSchemaProp: ComponentSchemaProp = { 13 | ...buttonOptionalIconSchemaProp, 14 | required: true, 15 | }; 16 | -------------------------------------------------------------------------------- /packages/core/src/compiler/themeValueToResponsiveValue.ts: -------------------------------------------------------------------------------- 1 | import { isTrulyResponsiveValue } from "../responsiveness"; 2 | import { Devices, ResponsiveValue, TrulyResponsiveValue } from "../types"; 3 | 4 | export function themeScalarValueToResponsiveValue( 5 | input: ResponsiveValue, 6 | devices: Devices 7 | ): ResponsiveValue { 8 | if (!isTrulyResponsiveValue(input)) { 9 | return input; 10 | } 11 | 12 | const output: TrulyResponsiveValue = { $res: true }; 13 | 14 | devices.forEach((device) => { 15 | const val = input[device.id]; 16 | 17 | if (val !== undefined) { 18 | output[device.id] = val; 19 | } 20 | }); 21 | 22 | return output; 23 | } 24 | -------------------------------------------------------------------------------- /packages/core/src/compiler/validation.ts: -------------------------------------------------------------------------------- 1 | import { isComponentConfig, isDocument } from "../checkers"; 2 | import { NoCodeComponentEntry, Document } from "../types"; 3 | 4 | function validate(input: unknown): 5 | | { 6 | isValid: true; 7 | input: Document | NoCodeComponentEntry | null | undefined; 8 | } 9 | | { isValid: false } { 10 | const isValid = 11 | input === null || 12 | input === undefined || 13 | isDocument(input) || 14 | isLegacyInput(input); 15 | 16 | if (!isValid) { 17 | return { 18 | isValid: false, 19 | }; 20 | } 21 | 22 | return { 23 | isValid: true, 24 | input: input as Document | NoCodeComponentEntry | null | undefined, 25 | }; 26 | } 27 | 28 | export { validate }; 29 | 30 | export function isLegacyInput(input: unknown): input is NoCodeComponentEntry { 31 | return isComponentConfig(input); 32 | } 33 | -------------------------------------------------------------------------------- /packages/core/src/components/ssr.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | export const easyblocksStitchesInstances: any[] = []; 4 | 5 | export function easyblocksGetCssText() { 6 | return easyblocksStitchesInstances 7 | .map((stitches) => stitches.getCssText()) 8 | .join(" "); 9 | } 10 | 11 | export function easyblocksGetStyleTag() { 12 | return ( 13 |