Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/feature-flags.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { ChaiBuilderEditor } from "@chaibuilder/sdk";
dragAndDrop: true,
validateStructure: true,
designTokens: true,
animation: true,
}}
// ... other props
/>;
Expand All @@ -40,6 +41,7 @@ import { ChaiBuilderEditor } from "@chaibuilder/sdk";
| `designTokens` | `false` | Enable design tokens feature |
| `librarySite` | `false` | Enable library site features |
| `gotoSettings` | `false` | Enable settings navigation |
| `animation` | `false` | Enable animation feature |

## Custom Feature Flags

Expand Down
1 change: 1 addition & 0 deletions docs/types-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ interface ChaiBuilderEditorProps {
dragAndDrop?: boolean;
validateStructure?: boolean;
designTokens?: boolean;
animation?: boolean;
};

// Advanced
Expand Down
1 change: 1 addition & 0 deletions src/Editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ function ChaiBuilderDefault() {
importTheme: false,
dragAndDrop: true,
designTokens: true,
animation: true,
}}
gotoPage={(args) => {
console.log("gotoPage", args);
Expand Down
68 changes: 35 additions & 33 deletions src/core/components/settings/new-panel/attributes-editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import { Button } from "@/ui/shadcn/components/ui/button";
import { Input } from "@/ui/shadcn/components/ui/input";
import { Label } from "@/ui/shadcn/components/ui/label";
import { Textarea } from "@/ui/shadcn/components/ui/textarea";
import { Cross1Icon, Pencil2Icon } from "@radix-ui/react-icons";
import { isEmpty } from "lodash-es";
import { Pencil2Icon, Cross1Icon } from "@radix-ui/react-icons";
import React, { useCallback, useEffect, useRef, useState } from "react";
import { useTranslation } from "react-i18next";

Expand Down Expand Up @@ -42,7 +42,7 @@ export default React.memo(function AttrsEditor({

const addAttribute = () => {
if (newKey.startsWith("@")) {
setError(t('Attribute keys cannot start with @'));
setError(t("Attribute keys cannot start with @"));
return;
}
if (newKey) {
Expand All @@ -69,7 +69,7 @@ export default React.memo(function AttrsEditor({

const saveEdit = () => {
if (newKey.startsWith("@")) {
setError(t('Attribute keys cannot start with @'));
setError(t("Attribute keys cannot start with @"));
return;
}
if (editIndex !== null && newKey) {
Expand Down Expand Up @@ -171,10 +171,10 @@ export default React.memo(function AttrsEditor({
editIndex !== null ? saveEdit() : addAttribute();
}}
className="space-y-3">
<div className="flex flex-col gap-y-1">
<div className="w-full">
<Label htmlFor="attrKey" className="text-[11px] font-normal leading-tight text-slate-600">
{t('Key')}
<div className="space-y-2">
<div>
<Label htmlFor="attrKey" className="mb-1 block text-[11px] font-medium text-gray-700">
{t("Key")}
</Label>
<Input
autoCapitalize={"off"}
Expand All @@ -184,14 +184,14 @@ export default React.memo(function AttrsEditor({
ref={keyInputRef}
value={newKey}
onChange={(e) => setNewKey(e.target.value)}
placeholder={t('Enter key')}
className="py-0 text-xs font-normal leading-tight placeholder:text-slate-400"
placeholder={t("Enter key")}
className="h-7 text-[11px] transition-colors focus:border-primary focus:ring-1 focus:ring-primary"
/>
</div>
<div className="w-full">
<div className="flex items-center justify-between">
<Label htmlFor="attrValue" className="text-[11px] font-normal text-slate-600">
{t('Value')}
<div>
<div className="mb-1 flex items-center justify-between">
<Label htmlFor="attrValue" className="text-[11px] font-medium text-gray-700">
{t("Value")}
</Label>
{!isEmpty(pageExternalData) && <NestedPathSelector data={pageExternalData} onSelect={handlePathSelect} />}
</div>
Expand All @@ -204,37 +204,39 @@ export default React.memo(function AttrsEditor({
value={newValue}
onChange={(e) => setNewValue(e.target.value)}
onKeyDown={handleKeyDown}
placeholder={t('Enter value')}
className="text-xs font-normal leading-tight placeholder:text-slate-400"
placeholder={t("Enter value")}
className="min-h-[60px] resize-y text-[11px] transition-colors focus:border-primary focus:ring-1 focus:ring-primary"
/>
</div>
</div>
<div className="flex justify-end">
<Button type="submit" disabled={!newKey.length} variant="default" size="sm" className="h-8 w-24 text-xs">
{editIndex !== null ? t('Save') : t('Add')}
{editIndex !== null ? t("Save") : t("Add")}
</Button>
</div>
{error && <p className="text-xs text-red-500">{error}</p>}
</form>

<div className="space-y-1 py-4">
{attributes.map((attr, index) => (
<div key={index} className="flex items-center justify-between rounded border p-2 text-sm">
<div className="flex flex-col text-xs leading-tight">
<span className="truncate text-[12px] font-light text-muted-foreground">{attr.key}</span>
<span className="max-w-[200px] text-wrap font-normal">{attr.value.toString()}</span>
{attributes?.length > 0 && (
<div className="space-y-1 pt-4">
{attributes.map((attr, index) => (
<div key={index} className="flex items-center justify-between rounded border p-2 text-sm">
<div className="flex flex-col text-xs leading-tight">
<span className="truncate text-[12px] font-light text-muted-foreground">{attr.key}</span>
<span className="max-w-[200px] text-wrap font-normal">{attr.value.toString()}</span>
</div>
<div className="flex-shrink-0 text-slate-400">
<Button variant="ghost" size="icon" className="h-6 w-6" onClick={() => startEdit(index)}>
<Pencil2Icon className="h-3 w-3" />
</Button>
<Button variant="ghost" size="icon" className="h-6 w-6" onClick={() => removeAttribute(index)}>
<Cross1Icon className="h-3 w-3" />
</Button>
</div>
</div>
<div className="flex-shrink-0 text-slate-400">
<Button variant="ghost" size="icon" className="h-6 w-6" onClick={() => startEdit(index)}>
<Pencil2Icon className="h-3 w-3" />
</Button>
<Button variant="ghost" size="icon" className="h-6 w-6" onClick={() => removeAttribute(index)}>
<Cross1Icon className="h-3 w-3" />
</Button>
</div>
</div>
))}
</div>
))}
</div>
)}
</div>
);
});
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ export const BlockAttributesEditor = React.memo(() => {
const attrKey = `${get(selectedStylingBlock, "0.prop")}_attrs`;

React.useEffect(() => {
const _attributes = map(get(block, attrKey), (value, key) => ({ key, value }));
const _attributes = map(get(block, attrKey), (value, key) => ({ key, value })).filter(
(attr) => attr.key !== "data-animation",
);
if (!isEmpty(_attributes)) setAttributes(_attributes as any);
else setAttributes([]);

Expand All @@ -23,6 +25,11 @@ export const BlockAttributesEditor = React.memo(() => {
const updateAttributes = React.useCallback(
(updatedAttributes: any = []) => {
const _attrs = {};
// Preserve existing data-animation if it exists
const existingAnimation = get(block, `${attrKey}.data-animation`);
if (existingAnimation) {
set(_attrs, "data-animation", existingAnimation);
}
forEach(updatedAttributes, (item) => {
if (!isEmpty(item.key)) {
set(_attrs, item.key, item.value);
Expand All @@ -34,7 +41,7 @@ export const BlockAttributesEditor = React.memo(() => {
);

return (
<div className="flex-col gap-y-2">
<div className="flex-col gap-y-2 px-2 pb-2">
<div className="flex flex-col">
<div>
<AttrsEditor preloadedAttributes={attributes} onAttributesChange={updateAttributes} />
Expand Down
9 changes: 6 additions & 3 deletions src/core/components/settings/settings-panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
useSelectedStylingBlocks,
} from "@/core/hooks";
import { PERMISSIONS, usePermissions } from "@/core/main";
import AnimationField from "@/render/animation/animation-field";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/ui/shadcn/components/ui/tabs";
import { ChevronDownIcon, MixerHorizontalIcon } from "@radix-ui/react-icons";
import { isEmpty, isNull, noop } from "lodash-es";
Expand All @@ -28,17 +29,17 @@ function BlockAttributesToggle() {
return null;
}
return (
<>
<div className="my-1 h-max rounded border">
<div
onClick={() => setShowAttributes(!showAttributes)}
className="flex cursor-pointer items-center justify-between border-t border-border py-3 text-xs font-medium hover:underline">
className={`flex cursor-pointer items-center justify-between p-2 text-xs font-medium hover:bg-blue-50`}>
<span>{t("Attributes")}</span>
<span>
<ChevronDownIcon className={"h-4 w-4 text-gray-500 " + (showAttributes ? "rotate-180" : "")} />
</span>
</div>
{showAttributes && <BlockAttributesEditor />}
</>
</div>
);
}

Expand Down Expand Up @@ -128,6 +129,7 @@ const SettingsPanel: React.FC = () => {
<ResetStylesButton />
</div>
<BlockStyling />
<AnimationField />
<BlockAttributesToggle />
<br />
<br />
Expand Down Expand Up @@ -171,6 +173,7 @@ const SettingsPanel: React.FC = () => {
value="styles"
className="no-scrollbar h-full max-h-min max-w-full overflow-y-auto overflow-x-hidden">
<BlockStyling />
<AnimationField />
<BlockAttributesToggle />
<br />
<br />
Expand Down
54 changes: 27 additions & 27 deletions src/core/main/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export { BlockAttributesEditor as ChaiBlockAttributesEditor } from "@/core/compo
export { DefaultChaiBlocks as ChaiDefaultBlocks } from "@/core/components/sidepanels/panels/add-blocks/default-blocks";
export { ChaiDraggableBlock } from "@/core/components/sidepanels/panels/add-blocks/draggable-block";
export { ExportCodeModal as ChaiExportCodeModal } from "@/core/modals/export-code-modal";
export { default as ChaiAnimationContainer } from "@/render/animation/animation-container";
export {
AddBlocksPanel as ChaiAddBlocksPanel,
BlockPropsEditor as ChaiBlockPropsEditor,
Expand All @@ -37,42 +38,42 @@ export {
ImportHTML as ChaiImportHTML,
Outline as ChaiOutline,
ThemeConfigPanel as ChaiThemeConfigPanel,
UILibrariesPanel as ChaiUILibrariesPanel
UILibrariesPanel as ChaiUILibrariesPanel,
};

// i18n
export { i18n };
export { i18n };

// helper functions
export { generateUUID as generateBlockId, cn as mergeClasses } from "@/core/functions/common-functions";
export { getClassValueAndUnit } from "@/core/functions/helper-fn";
export { getBlocksFromHTML as convertHTMLToChaiBlocks, getBlocksFromHTML } from "@/core/import-html/html-to-json";
export { generateUUID as generateBlockId, cn as mergeClasses } from "@/core/functions/common-functions";
export { getClassValueAndUnit } from "@/core/functions/helper-fn";
export { getBlocksFromHTML as convertHTMLToChaiBlocks, getBlocksFromHTML } from "@/core/import-html/html-to-json";

// types
export type { ChaiBlock, ChaiBuilderEditorProps };

// registration apis
export { registerChaiAddBlockTab } from "@/core/extensions/add-block-tabs";
export {
registerBlockSettingField,
registerBlockSettingTemplate,
registerBlockSettingWidget
} from "@/core/extensions/blocks-settings";
export { registerChaiPreImportHTMLHook } from "@/core/extensions/import-html-pre-hook";
export { registerChaiLibrary } from "@/core/extensions/libraries";
export { registerChaiMediaManager } from "@/core/extensions/media-manager";
export { registerChaiSaveToLibrary } from "@/core/extensions/save-to-library";
export { registerChaiSidebarPanel } from "@/core/extensions/sidebar-panels";
export { registerChaiTopBar } from "@/core/extensions/top-bar";
export {
IfChaiFeatureFlag,
registerChaiFeatureFlag,
registerChaiFeatureFlags,
useChaiFeatureFlag,
useChaiFeatureFlags,
useToggleChaiFeatureFlag
} from "@/core/flags/register-chai-flag";
export type { ChaiLibrary, ChaiLibraryBlock } from "@/types/chaibuilder-editor-props";
export { registerChaiAddBlockTab } from "@/core/extensions/add-block-tabs";
export {
registerBlockSettingField,
registerBlockSettingTemplate,
registerBlockSettingWidget,
} from "@/core/extensions/blocks-settings";
export { registerChaiPreImportHTMLHook } from "@/core/extensions/import-html-pre-hook";
export { registerChaiLibrary } from "@/core/extensions/libraries";
export { registerChaiMediaManager } from "@/core/extensions/media-manager";
export { registerChaiSaveToLibrary } from "@/core/extensions/save-to-library";
export { registerChaiSidebarPanel } from "@/core/extensions/sidebar-panels";
export { registerChaiTopBar } from "@/core/extensions/top-bar";
export {
IfChaiFeatureFlag,
registerChaiFeatureFlag,
registerChaiFeatureFlags,
useChaiFeatureFlag,
useChaiFeatureFlags,
useToggleChaiFeatureFlag,
} from "@/core/flags/register-chai-flag";
export type { ChaiLibrary, ChaiLibraryBlock } from "@/types/chaibuilder-editor-props";

// hooks
export { useMediaManagerComponent } from "@/core/extensions/media-manager";
Expand All @@ -82,4 +83,3 @@ export * from "@/core/hooks";
// constants
export { PERMISSIONS } from "@/core/constants/PERMISSIONS";
export type { ChaiThemeValues } from "@/types/chaibuilder-editor-props";

9 changes: 9 additions & 0 deletions src/render/animation/animation-container.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import "./animation-styles.css";
import { useChaiAnimation } from "./use-chai-animation";

const AnimationContainer = ({ children }) => {
useChaiAnimation();
return children;
};

export default AnimationContainer;
Loading