@jhits/botanics_and_you 0.0.9 → 0.0.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/dist/index.d.ts +2 -4
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +2 -4
  4. package/dist/plugin-blog/blocks/CategoryDropdown.d.ts.map +1 -1
  5. package/dist/plugin-blog/blocks/CategoryDropdown.js +18 -15
  6. package/dist/plugin-blog/blocks/Heading.d.ts.map +1 -1
  7. package/dist/plugin-blog/blocks/Heading.js +29 -26
  8. package/dist/plugin-blog/blocks/Hero.d.ts.map +1 -1
  9. package/dist/plugin-blog/blocks/Hero.js +12 -4
  10. package/dist/plugin-blog/blocks/List.d.ts.map +1 -1
  11. package/dist/plugin-blog/blocks/List.js +40 -171
  12. package/dist/plugin-blog/blocks/Paragraph.d.ts.map +1 -1
  13. package/dist/plugin-blog/blocks/Paragraph.js +19 -43
  14. package/dist/plugin-blog/blocks/Recipe.d.ts +0 -1
  15. package/dist/plugin-blog/blocks/Recipe.d.ts.map +1 -1
  16. package/dist/plugin-blog/blocks/Recipe.js +22 -23
  17. package/dist/plugin-blog/blocks/Table.d.ts +0 -3
  18. package/dist/plugin-blog/blocks/Table.d.ts.map +1 -1
  19. package/dist/plugin-blog/blocks/Table.js +19 -50
  20. package/dist/plugin-blog/colors.d.ts +12 -0
  21. package/dist/plugin-blog/colors.d.ts.map +1 -0
  22. package/dist/plugin-blog/colors.js +11 -0
  23. package/dist/plugin-blog/components/BotanyBlogWrapper.d.ts +15 -0
  24. package/dist/plugin-blog/components/BotanyBlogWrapper.d.ts.map +1 -0
  25. package/dist/plugin-blog/components/BotanyBlogWrapper.js +12 -0
  26. package/dist/plugin-blog/index.d.ts +1 -0
  27. package/dist/plugin-blog/index.d.ts.map +1 -1
  28. package/dist/plugin-blog/index.js +3 -0
  29. package/dist/plugin-blog/theme.d.ts.map +1 -1
  30. package/dist/plugin-blog/theme.js +7 -0
  31. package/dist/plugin-newsletter/index.d.ts +1 -0
  32. package/dist/plugin-newsletter/index.d.ts.map +1 -1
  33. package/dist/plugin-newsletter/index.js +8 -1
  34. package/package.json +4 -4
  35. package/src/index.ts +2 -4
  36. package/src/plugin-blog/blocks/CategoryDropdown.tsx +37 -22
  37. package/src/plugin-blog/blocks/Heading.tsx +33 -32
  38. package/src/plugin-blog/blocks/Hero.tsx +26 -11
  39. package/src/plugin-blog/blocks/List.tsx +72 -234
  40. package/src/plugin-blog/blocks/Paragraph.tsx +37 -53
  41. package/src/plugin-blog/blocks/Recipe.tsx +31 -28
  42. package/src/plugin-blog/blocks/Table.tsx +40 -87
  43. package/src/plugin-blog/colors.ts +11 -0
  44. package/src/plugin-blog/components/BotanyBlogWrapper.tsx +43 -0
  45. package/src/plugin-blog/index.ts +3 -0
  46. package/src/plugin-blog/theme.ts +7 -0
  47. package/src/plugin-newsletter/index.ts +10 -1
package/dist/index.d.ts CHANGED
@@ -2,9 +2,7 @@
2
2
  * BotanicsAndYou Client Configuration for JHITS
3
3
  *
4
4
  * This package contains all client-specific plugin configurations.
5
- *
6
- * Usage:
7
- * import { blogPluginConfig, botanyBlocks } from '@jhits/BotanicsAndYou/plugin-blog';
8
5
  */
9
- export * from './plugin-blog';
6
+ export { blogPluginConfig, botanyBlocks, customBlocks } from './plugin-blog';
7
+ export { newsletterPluginConfig, botanyNewsletterBlocks, customNewsletterBlocks } from './plugin-newsletter';
10
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,cAAc,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7E,OAAO,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC"}
package/dist/index.js CHANGED
@@ -2,8 +2,6 @@
2
2
  * BotanicsAndYou Client Configuration for JHITS
3
3
  *
4
4
  * This package contains all client-specific plugin configurations.
5
- *
6
- * Usage:
7
- * import { blogPluginConfig, botanyBlocks } from '@jhits/BotanicsAndYou/plugin-blog';
8
5
  */
9
- export * from './plugin-blog';
6
+ export { blogPluginConfig, botanyBlocks, customBlocks } from './plugin-blog';
7
+ export { newsletterPluginConfig, botanyNewsletterBlocks, customNewsletterBlocks } from './plugin-newsletter';
@@ -1 +1 @@
1
- {"version":3,"file":"CategoryDropdown.d.ts","sourceRoot":"","sources":["../../../src/plugin-blog/blocks/CategoryDropdown.tsx"],"names":[],"mappings":"AAKA,MAAM,WAAW,qBAAqB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,kBAAkB,EAAE,MAAM,EAAE,CAAC;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,wBAAgB,gBAAgB,CAAC,EAC7B,KAAK,EACL,QAAQ,EACR,kBAAuB,EACvB,SAAc,EACd,WAAyC,EACzC,SAAiB,GACpB,EAAE,qBAAqB,2CAiJvB"}
1
+ {"version":3,"file":"CategoryDropdown.d.ts","sourceRoot":"","sources":["../../../src/plugin-blog/blocks/CategoryDropdown.tsx"],"names":[],"mappings":"AAMA,MAAM,WAAW,qBAAqB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,kBAAkB,EAAE,MAAM,EAAE,CAAC;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,wBAAgB,gBAAgB,CAAC,EAC7B,KAAK,EACL,QAAQ,EACR,kBAAuB,EACvB,SAAc,EACd,WAAyC,EACzC,SAAiB,GACpB,EAAE,qBAAqB,2CA+JvB"}
@@ -2,42 +2,41 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useState, useEffect, useRef } from 'react';
4
4
  import { ChevronDown, Plus } from 'lucide-react';
5
+ import { colors } from '../colors';
5
6
  export function CategoryDropdown({ value, onChange, existingCategories = [], className = '', placeholder = 'Select or add category...', showLabel = false, }) {
6
7
  const [isOpen, setIsOpen] = useState(false);
7
8
  const [inputValue, setInputValue] = useState('');
8
9
  const [isAddingNew, setIsAddingNew] = useState(false);
9
10
  const dropdownRef = useRef(null);
10
11
  const inputRef = useRef(null);
11
- // This ref tracks if we have successfully set an initial value
12
- // to avoid snapping back to the first item if the user manually clears the field
13
12
  const hasAutoSelected = useRef(false);
14
- /**
15
- * AUTO-SELECT LOGIC
16
- * We watch 'existingCategories'. As soon as it populates and we have no value, we pick the first.
17
- */
13
+ const prevCategoriesRef = useRef(existingCategories);
14
+ // Reset auto-select when categories change (e.g., language switch)
15
+ useEffect(() => {
16
+ const prev = prevCategoriesRef.current;
17
+ const changed = prev.length !== existingCategories.length ||
18
+ prev.some((cat, i) => cat !== existingCategories[i]);
19
+ if (changed) {
20
+ hasAutoSelected.current = false;
21
+ prevCategoriesRef.current = existingCategories;
22
+ }
23
+ }, [existingCategories]);
18
24
  useEffect(() => {
19
25
  const isValueEmpty = !value || value.trim() === '';
20
26
  if (isValueEmpty && existingCategories.length > 0 && !hasAutoSelected.current) {
21
27
  const firstCategory = existingCategories.find(cat => cat && cat.trim() !== '');
22
28
  if (firstCategory) {
23
- // Set the flag FIRST to prevent race conditions
24
29
  hasAutoSelected.current = true;
25
- // Use requestAnimationFrame to ensure we are outside the
26
- // render phase of the category fetch completion
27
30
  requestAnimationFrame(() => {
28
31
  onChange(firstCategory);
29
32
  });
30
33
  }
31
34
  }
32
- // If a value is manually set (by user or saved data),
33
- // we mark auto-select as "done" so it doesn't fight the user
34
35
  if (!isValueEmpty) {
35
36
  hasAutoSelected.current = true;
36
37
  }
37
38
  }, [existingCategories, value, onChange]);
38
- // UI filtering
39
39
  const otherCategories = existingCategories.filter(cat => cat && cat.trim() !== '' && cat !== value);
40
- // Outside Click Handler
41
40
  useEffect(() => {
42
41
  const handleClickOutside = (event) => {
43
42
  if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
@@ -51,7 +50,6 @@ export function CategoryDropdown({ value, onChange, existingCategories = [], cla
51
50
  return () => document.removeEventListener('mousedown', handleClickOutside);
52
51
  }
53
52
  }, [isOpen]);
54
- // Focus on new input
55
53
  useEffect(() => {
56
54
  if (isAddingNew && inputRef.current) {
57
55
  inputRef.current.focus();
@@ -63,5 +61,10 @@ export function CategoryDropdown({ value, onChange, existingCategories = [], cla
63
61
  setIsAddingNew(false);
64
62
  setInputValue('');
65
63
  };
66
- return (_jsxs("div", { className: `relative ${className}`, ref: dropdownRef, children: [showLabel && (_jsx("label", { className: "text-[10px] text-neutral-500 uppercase font-bold block mb-2 tracking-widest", children: "Category" })), _jsxs("div", { className: "relative", children: [_jsxs("div", { onClick: () => setIsOpen(!isOpen), className: "w-full bg-transparent border border-sage/20 rounded-full px-4 py-1.5 text-xs font-bold uppercase tracking-[0.2em] text-sage flex items-center justify-between cursor-pointer hover:bg-sage/5 transition-all", children: [_jsx("span", { className: "truncate", children: value || placeholder }), _jsx(ChevronDown, { size: 14, className: `transition-transform text-sage/50 ${isOpen ? 'rotate-180' : ''}` })] }), isOpen && (_jsxs("div", { className: "absolute z-50 w-full mt-2 bg-white dark:bg-neutral-900 rounded-xl shadow-xl border border-neutral-100 dark:border-neutral-800 overflow-hidden animate-in fade-in zoom-in-95 duration-150", children: [otherCategories.length > 0 && (_jsx("div", { className: "py-1 max-h-48 overflow-y-auto", children: otherCategories.map((category) => (_jsx("button", { type: "button", onClick: () => handleSelect(category), className: "w-full text-left px-4 py-2.5 text-xs font-bold text-neutral-700 dark:text-neutral-300 hover:bg-sage/5 hover:text-sage transition-colors", children: category }, category))) })), !isAddingNew ? (_jsxs("button", { type: "button", onClick: () => setIsAddingNew(true), className: "w-full text-left px-4 py-3 text-xs font-bold text-sage hover:bg-primary/5 transition-colors border-t border-neutral-50 dark:border-neutral-800 flex items-center gap-2", children: [_jsx(Plus, { size: 14 }), " Add new category"] })) : (_jsxs("div", { className: "p-3 border-t border-neutral-50 dark:border-neutral-800 bg-neutral-50/50 dark:bg-neutral-800/50", children: [_jsx("input", { ref: inputRef, value: inputValue, onChange: (e) => setInputValue(e.target.value), onKeyDown: (e) => e.key === 'Enter' && inputValue.trim() && handleSelect(inputValue.trim()), placeholder: "Category name...", className: "w-full border border-neutral-200 dark:border-neutral-700 bg-white dark:bg-neutral-900 p-2 rounded-lg text-xs font-bold outline-none focus:border-sage mb-2" }), _jsxs("div", { className: "flex gap-2", children: [_jsx("button", { onClick: () => inputValue.trim() && handleSelect(inputValue.trim()), className: "flex-1 bg-sage text-white px-3 py-1.5 rounded-lg text-[10px] font-bold uppercase tracking-wider", children: "Add" }), _jsx("button", { onClick: () => { setIsAddingNew(false); setInputValue(''); }, className: "px-3 py-1.5 rounded-lg text-[10px] font-bold uppercase border border-neutral-200 dark:border-neutral-700 text-neutral-500", children: "Cancel" })] })] }))] }))] })] }));
64
+ return (_jsxs("div", { className: `relative ${className}`, ref: dropdownRef, children: [showLabel && (_jsx("label", { className: "text-[10px] text-neutral-500 uppercase font-bold block mb-2 tracking-widest", children: "Category" })), _jsxs("div", { className: "relative", children: [_jsxs("div", { onClick: () => setIsOpen(!isOpen), className: "w-full bg-transparent rounded-full px-4 py-1.5 text-xs font-bold uppercase tracking-[0.2em] flex items-center justify-between cursor-pointer transition-all", style: {
65
+ color: colors.sage,
66
+ borderWidth: 1,
67
+ borderStyle: 'solid',
68
+ borderColor: `${colors.sage}33`,
69
+ }, onMouseEnter: (e) => { e.currentTarget.style.backgroundColor = `${colors.sage}0D`; }, onMouseLeave: (e) => { e.currentTarget.style.backgroundColor = ''; }, children: [_jsx("span", { className: "truncate", children: value || placeholder }), _jsx(ChevronDown, { size: 14, style: { color: `${colors.sage}80` }, className: `transition-transform ${isOpen ? 'rotate-180' : ''}` })] }), isOpen && (_jsxs("div", { className: "absolute z-50 w-full mt-2 bg-white dark:bg-neutral-900 rounded-xl shadow-xl border border-neutral-100 dark:border-neutral-800 overflow-hidden animate-in fade-in zoom-in-95 duration-150", children: [otherCategories.length > 0 && (_jsx("div", { className: "py-1 max-h-48 overflow-y-auto", children: otherCategories.map((category) => (_jsx("button", { type: "button", onClick: () => handleSelect(category), className: "w-full text-left px-4 py-2.5 text-xs font-bold text-neutral-700 dark:text-neutral-300 transition-colors", onMouseEnter: (e) => { e.currentTarget.style.backgroundColor = `${colors.sage}0D`; e.currentTarget.style.color = colors.sage; }, onMouseLeave: (e) => { e.currentTarget.style.backgroundColor = ''; e.currentTarget.style.color = ''; }, children: category }, category))) })), !isAddingNew ? (_jsxs("button", { type: "button", onClick: () => setIsAddingNew(true), className: "w-full text-left px-4 py-3 text-xs font-bold transition-colors border-t border-neutral-50 dark:border-neutral-800 flex items-center gap-2", style: { color: colors.sage }, onMouseEnter: (e) => { e.currentTarget.style.backgroundColor = `${colors.sage}0D`; }, onMouseLeave: (e) => { e.currentTarget.style.backgroundColor = ''; }, children: [_jsx(Plus, { size: 14 }), " Add new category"] })) : (_jsxs("div", { className: "p-3 border-t border-neutral-50 dark:border-neutral-800 bg-neutral-50/50 dark:bg-neutral-800/50", children: [_jsx("input", { ref: inputRef, value: inputValue, onChange: (e) => setInputValue(e.target.value), onKeyDown: (e) => e.key === 'Enter' && inputValue.trim() && handleSelect(inputValue.trim()), placeholder: "Category name...", className: "w-full border border-neutral-200 dark:border-neutral-700 bg-white dark:bg-neutral-900 p-2 rounded-lg text-xs font-bold outline-none mb-2", style: { borderColor: undefined }, onFocus: (e) => { e.currentTarget.style.borderColor = colors.sage; }, onBlur: (e) => { e.currentTarget.style.borderColor = ''; } }), _jsxs("div", { className: "flex gap-2", children: [_jsx("button", { onClick: () => inputValue.trim() && handleSelect(inputValue.trim()), className: "flex-1 text-white px-3 py-1.5 rounded-lg text-[10px] font-bold uppercase tracking-wider", style: { backgroundColor: colors.sage }, children: "Add" }), _jsx("button", { onClick: () => { setIsAddingNew(false); setInputValue(''); }, className: "px-3 py-1.5 rounded-lg text-[10px] font-bold uppercase border border-neutral-200 dark:border-neutral-700 text-neutral-500", children: "Cancel" })] })] }))] }))] })] }));
67
70
  }
@@ -1 +1 @@
1
- {"version":3,"file":"Heading.d.ts","sourceRoot":"","sources":["../../../src/plugin-blog/blocks/Heading.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAqC,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAmH9F,eAAO,MAAM,YAAY,EAAE,qBAoB1B,CAAC"}
1
+ {"version":3,"file":"Heading.d.ts","sourceRoot":"","sources":["../../../src/plugin-blog/blocks/Heading.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAqC,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAoH9F,eAAO,MAAM,YAAY,EAAE,qBAoB1B,CAAC"}
@@ -2,26 +2,34 @@
2
2
  import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
3
3
  import { Heading, ChevronDown } from "lucide-react";
4
4
  import { useState, useRef, useEffect } from "react";
5
+ const getStyle = (level) => {
6
+ const base = {
7
+ fontFamily: 'var(--content-heading-font, var(--font-serif, Georgia, serif))',
8
+ color: 'var(--color-forest, #6B7C5A)'
9
+ };
10
+ switch (level) {
11
+ case 4:
12
+ case 5:
13
+ case 6:
14
+ return Object.assign(Object.assign({}, base), { color: 'var(--color-sage, #9CAF88)', fontStyle: level === 4 ? 'italic' : undefined });
15
+ default:
16
+ return base;
17
+ }
18
+ };
5
19
  const getClassName = (level) => `
6
- font-serif text-forest
20
+ font-[family:var(--content-heading-font,serif)]
7
21
  ${level === 1 ? "text-5xl lg:text-6xl mt-12 mb-8 font-bold" : ""}
8
22
  ${level === 2 ? "text-4xl lg:text-5xl mt-10 mb-2" : ""}
9
- ${level === 3 ? "text-3xl mt-8 mb-4 text-forest/90" : ""}
10
- ${level === 4 ? "text-xl mt-6 mb-3 font-medium text-sage italic" : ""}
11
- ${level === 5 ? "text-lg mt-4 mb-2 font-medium text-sage" : ""}
12
- ${level === 6 ? "text-base mt-3 mb-2 font-medium text-sage/80" : ""}
23
+ ${level === 3 ? "text-3xl mt-8 mb-4" : ""}
24
+ ${level === 4 ? "text-xl mt-6 mb-3 font-medium" : ""}
25
+ ${level === 5 ? "text-lg mt-4 mb-2 font-medium" : ""}
26
+ ${level === 6 ? "text-base mt-3 mb-2 font-medium" : ""}
13
27
  `;
14
- /**
15
- * Heading Block
16
- * Simple heading block for botanical articles with SEO-optimized heading levels
17
- */
18
- const HeadingEdit = ({ block, onUpdate, isSelected }) => {
28
+ const HeadingEdit = ({ block, onUpdate }) => {
19
29
  const level = block.data.level || 2;
20
30
  const text = block.data.text || '';
21
31
  const [showLevelDropdown, setShowLevelDropdown] = useState(false);
22
32
  const dropdownRef = useRef(null);
23
- const defaultClassName = getClassName(level);
24
- // Close dropdown when clicking outside
25
33
  useEffect(() => {
26
34
  const handleClickOutside = (event) => {
27
35
  if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
@@ -39,25 +47,20 @@ const HeadingEdit = ({ block, onUpdate, isSelected }) => {
39
47
  };
40
48
  return (_jsx("div", { className: "relative group", children: _jsxs("div", { className: "flex items-center gap-3", children: [_jsxs("div", { className: "relative", ref: dropdownRef, children: [_jsxs("button", { type: "button", onClick: () => setShowLevelDropdown(!showLevelDropdown), className: "flex items-center gap-1.5 px-3 py-1.5 rounded-lg border border-gray-200 bg-white hover:bg-gray-50 transition-colors text-xs font-bold text-gray-600", title: "Select heading level for SEO", children: [_jsxs("span", { children: ["H", level] }), _jsx(ChevronDown, { size: 12, className: `transition-transform ${showLevelDropdown ? 'rotate-180' : ''}` })] }), showLevelDropdown && (_jsx("div", { className: "absolute z-50 mt-1 bg-white border border-gray-200 rounded-lg shadow-lg overflow-hidden min-w-[120px]", children: [1, 2, 3, 4, 5, 6].map((lvl) => (_jsxs("button", { type: "button", onClick: () => handleLevelChange(lvl), className: `w-full text-left px-3 py-2 text-xs font-bold transition-colors ${level === lvl
41
49
  ? 'bg-primary/10 text-primary'
42
- : 'text-gray-700 hover:bg-gray-50'}`, children: ["H", lvl, " ", lvl === 1 ? '(Page Title)' : lvl === 2 ? '(Section)' : lvl === 3 ? '(Subsection)' : ''] }, lvl))) }))] }), _jsx("input", { type: "text", value: text, onChange: (e) => onUpdate({ text: e.target.value }), placeholder: "Enter heading text...", className: `flex-1 ${defaultClassName}` })] }) }));
50
+ : 'text-gray-700 hover:bg-gray-50'}`, children: ["H", lvl, " ", lvl === 1 ? '(Page Title)' : lvl === 2 ? '(Section)' : lvl === 3 ? '(Subsection)' : ''] }, lvl))) }))] }), _jsx("input", { type: "text", value: text, onChange: (e) => onUpdate({ text: e.target.value }), placeholder: "Enter heading text...", style: getStyle(level), className: `flex-1 bg-transparent border-none outline-none focus:ring-0 ${getClassName(level)}` })] }) }));
43
51
  };
44
52
  const HeadingPreview = ({ block }) => {
45
53
  const level = Math.min(Math.max(block.data.level || 2, 1), 6);
46
54
  const text = block.data.text || '';
47
- const defaultClassName = getClassName(level);
55
+ const style = getStyle(level);
56
+ const className = getClassName(level);
48
57
  switch (level) {
49
- case 1:
50
- return _jsx("h1", { className: defaultClassName, children: text });
51
- case 3:
52
- return _jsx("h3", { className: defaultClassName, children: text });
53
- case 4:
54
- return _jsx("h4", { className: defaultClassName, children: text });
55
- case 5:
56
- return _jsx("h5", { className: defaultClassName, children: text });
57
- case 6:
58
- return _jsx("h6", { className: defaultClassName, children: text });
59
- default:
60
- return _jsx("h2", { className: defaultClassName, children: text });
58
+ case 1: return _jsx("h1", { style: style, className: className, children: text });
59
+ case 3: return _jsx("h3", { style: style, className: className, children: text });
60
+ case 4: return _jsx("h4", { style: style, className: className, children: text });
61
+ case 5: return _jsx("h5", { style: style, className: className, children: text });
62
+ case 6: return _jsx("h6", { style: style, className: className, children: text });
63
+ default: return _jsx("h2", { style: style, className: className, children: text });
61
64
  }
62
65
  };
63
66
  export const HeadingBlock = {
@@ -1 +1 @@
1
- {"version":3,"file":"Hero.d.ts","sourceRoot":"","sources":["../../../src/plugin-blog/blocks/Hero.tsx"],"names":[],"mappings":"AAEA,OAAO,KAA8B,MAAM,OAAO,CAAC;AAGnD,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAe9F;;GAEG;AACH,eAAO,MAAM,QAAQ,EAAE,KAAK,CAAC,EAAE,CAAC,cAAc,CAsI7C,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,WAAW,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,GAAG;IAAE,aAAa,CAAC,EAAE;QAAE,EAAE,CAAC,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,CA6CvG,CAAC;AAEF,eAAO,MAAM,SAAS,EAAE,qBAkBvB,CAAC"}
1
+ {"version":3,"file":"Hero.d.ts","sourceRoot":"","sources":["../../../src/plugin-blog/blocks/Hero.tsx"],"names":[],"mappings":"AAEA,OAAO,KAA8B,MAAM,OAAO,CAAC;AAGnD,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAgB9F;;GAEG;AACH,eAAO,MAAM,QAAQ,EAAE,KAAK,CAAC,EAAE,CAAC,cAAc,CAyI7C,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,WAAW,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,GAAG;IAAE,aAAa,CAAC,EAAE;QAAE,EAAE,CAAC,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,CAwDvG,CAAC;AAEF,eAAO,MAAM,SAAS,EAAE,qBAkBvB,CAAC"}
@@ -7,15 +7,17 @@ import { useCategories } from '@jhits/plugin-blog';
7
7
  import { ImagePicker, Image as PluginImage } from '@jhits/plugin-images';
8
8
  import { CategoryDropdown } from './CategoryDropdown';
9
9
  import { createApiUrl } from '../api-config';
10
+ import { colors } from '../colors';
10
11
  /**
11
12
  * 2. Hero Edit Component
12
13
  */
13
- export const HeroEdit = ({ block, onUpdate }) => {
14
+ export const HeroEdit = ({ block, onUpdate, context }) => {
14
15
  const [showImagePicker, setShowImagePicker] = useState(false);
15
16
  const [currentBrightness, setCurrentBrightness] = useState(100);
16
17
  const [currentBlur, setCurrentBlur] = useState(0);
17
18
  const [mounted, setMounted] = useState(false);
18
- const { categories } = useCategories();
19
+ // Pass context locale to useCategories for localized suggestions
20
+ const { categories } = useCategories(context === null || context === void 0 ? void 0 : context.locale);
19
21
  // Handle SSR - ensure we only render portal on client
20
22
  useEffect(() => {
21
23
  setMounted(true);
@@ -46,7 +48,7 @@ export const HeroEdit = ({ block, onUpdate }) => {
46
48
  const updateField = (updates) => {
47
49
  onUpdate(Object.assign(Object.assign({}, data), updates));
48
50
  };
49
- return (_jsxs("section", { className: "relative pt-32 pb-20 overflow-hidden group/hero", children: [_jsxs("div", { className: "max-w-7xl mx-auto px-6 lg:px-12 grid lg:grid-cols-2 gap-16 items-center", children: [_jsxs("div", { className: "order-2 lg:order-1 text-left flex flex-col items-start", children: [_jsx(CategoryDropdown, { value: category, onChange: (newCategory) => updateField({ category: newCategory }), existingCategories: categories, placeholder: "CATEGORIE" }), _jsx("textarea", { value: title, onChange: (e) => updateField({ title: e.target.value }), placeholder: "Artikel Titel...", rows: 2, className: "w-full text-5xl lg:text-7xl font-serif text-forest mb-8 leading-[1.1] tracking-tight bg-transparent border-none outline-none focus:ring-0 p-0 resize-none placeholder:text-gray-200 overflow-hidden" }), _jsx("textarea", { value: summary, onChange: (e) => updateField({ summary: e.target.value }), placeholder: "Klik om een samenvatting te typen...", rows: 3, className: "w-full text-lg lg:text-xl text-gray-600 leading-relaxed font-light bg-transparent border-none outline-none focus:ring-0 p-0 resize-none placeholder:text-gray-300" })] }), _jsx("div", { className: "order-1 lg:order-2 w-full", children: _jsxs("div", { className: "relative rounded-3xl overflow-hidden shadow-2xl aspect-[4/5] lg:aspect-[3/4] bg-gray-50 border border-gray-100 cursor-pointer group/imgcontainer", onClick: () => setShowImagePicker(true), children: [imageId ? (_jsx(PluginImage, { id: imageId, alt: title || 'Hero', fill: true, sizes: "(max-width: 1024px) 100vw, 50vw", className: "object-cover w-full h-full transition-transform duration-700 group-hover/imgcontainer:scale-105", editable: false })) : (_jsxs("div", { className: "absolute inset-0 flex flex-col items-center justify-center text-gray-400", children: [_jsx(ImageIcon, { size: 48, className: "mb-2" }), _jsx("p", { className: "text-sm font-bold", children: "Add Image" })] })), _jsx("div", { className: "absolute inset-0 bg-black/40 opacity-0 group-hover/imgcontainer:opacity-100 transition-opacity flex items-center justify-center z-10", children: _jsxs("div", { className: "bg-white px-4 py-2 rounded-full flex items-center gap-2", children: [_jsx(ImageIcon, { size: 16, className: "text-primary" }), _jsx("span", { className: "text-sm font-bold text-neutral-900", children: "Edit Image" })] }) })] }) })] }), showImagePicker && mounted && createPortal(_jsx("div", { className: "fixed inset-0 z-[100] flex items-center justify-center bg-black/60 backdrop-blur-sm p-4", children: _jsxs("div", { className: "bg-white dark:bg-neutral-900 rounded-2xl w-full max-w-2xl p-6 shadow-2xl max-h-[90vh] overflow-y-auto", children: [_jsxs("div", { className: "flex justify-between items-center mb-4", children: [_jsx("h3", { className: "font-bold text-neutral-900 dark:text-white", children: "Hero Image Settings" }), _jsx("button", { onClick: () => setShowImagePicker(false), className: "p-2 hover:bg-neutral-100 dark:hover:bg-neutral-800 rounded-lg transition-colors text-neutral-700 dark:text-neutral-300 hover:text-neutral-900 dark:hover:text-white", "aria-label": "Close", children: _jsxs("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("line", { x1: "5", y1: "5", x2: "15", y2: "15" }), _jsx("line", { x1: "15", y1: "5", x2: "5", y2: "15" })] }) })] }), _jsx(ImagePicker, { value: imageId, onChange: (img) => {
51
+ return (_jsxs("section", { className: "relative pt-32 pb-20 overflow-hidden group/hero", children: [_jsxs("div", { className: "max-w-7xl mx-auto px-6 lg:px-12 grid lg:grid-cols-2 gap-16 items-center", children: [_jsxs("div", { className: "order-2 lg:order-1 text-left flex flex-col items-start", children: [_jsx(CategoryDropdown, { value: category, onChange: (newCategory) => updateField({ category: newCategory }), existingCategories: categories, placeholder: "CATEGORIE" }), _jsx("textarea", { value: title, onChange: (e) => updateField({ title: e.target.value }), placeholder: "Artikel Titel...", rows: 2, style: { color: colors.forest }, className: "w-full text-5xl lg:text-7xl font-serif mb-8 leading-[1.1] tracking-tight bg-transparent border-none outline-none focus:ring-0 p-0 resize-none placeholder:text-gray-200 overflow-hidden" }), _jsx("textarea", { value: summary, onChange: (e) => updateField({ summary: e.target.value }), placeholder: "Klik om een samenvatting te typen...", rows: 3, className: "w-full text-lg lg:text-xl text-gray-600 leading-relaxed font-light bg-transparent border-none outline-none focus:ring-0 p-0 resize-none placeholder:text-gray-300" })] }), _jsx("div", { className: "order-1 lg:order-2 w-full", children: _jsxs("div", { className: "relative rounded-3xl overflow-hidden shadow-2xl aspect-[4/5] lg:aspect-[3/4] bg-gray-50 border border-gray-100 cursor-pointer group/imgcontainer", onClick: () => setShowImagePicker(true), children: [imageId ? (_jsx(PluginImage, { id: imageId, alt: title || 'Hero', fill: true, sizes: "(max-width: 1024px) 100vw, 50vw", className: "object-cover w-full h-full transition-transform duration-700 group-hover/imgcontainer:scale-105", editable: false })) : (_jsxs("div", { className: "absolute inset-0 flex flex-col items-center justify-center text-gray-400", children: [_jsx(ImageIcon, { size: 48, className: "mb-2" }), _jsx("p", { className: "text-sm font-bold", children: "Add Image" })] })), _jsx("div", { className: "absolute inset-0 bg-black/40 opacity-0 group-hover/imgcontainer:opacity-100 transition-opacity flex items-center justify-center z-10", children: _jsxs("div", { className: "bg-white px-4 py-2 rounded-full flex items-center gap-2", children: [_jsx(ImageIcon, { size: 16, className: "text-primary" }), _jsx("span", { className: "text-sm font-bold text-neutral-900", children: "Edit Image" })] }) })] }) })] }), showImagePicker && mounted && createPortal(_jsx("div", { className: "fixed inset-0 z-[100] flex items-center justify-center bg-black/60 backdrop-blur-sm p-4", children: _jsxs("div", { className: "bg-white dark:bg-neutral-900 rounded-2xl w-full max-w-2xl p-6 shadow-2xl max-h-[90vh] overflow-y-auto", children: [_jsxs("div", { className: "flex justify-between items-center mb-4", children: [_jsx("h3", { className: "font-bold text-neutral-900 dark:text-white", children: "Hero Image Settings" }), _jsx("button", { onClick: () => setShowImagePicker(false), className: "p-2 hover:bg-neutral-100 dark:hover:bg-neutral-800 rounded-lg transition-colors text-neutral-700 dark:text-neutral-300 hover:text-neutral-900 dark:hover:text-white", "aria-label": "Close", children: _jsxs("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("line", { x1: "5", y1: "5", x2: "15", y2: "15" }), _jsx("line", { x1: "15", y1: "5", x2: "5", y2: "15" })] }) })] }), _jsx(ImagePicker, { value: imageId, onChange: (img) => {
50
52
  updateField({ imageId: img === null || img === void 0 ? void 0 : img.id });
51
53
  setTimeout(() => setShowImagePicker(false), 100);
52
54
  }, showEffects: true, brightness: currentBrightness, blur: currentBlur, onBrightnessChange: (b) => { setCurrentBrightness(b); updateField({ brightness: b }); }, onBlurChange: (bl) => { setCurrentBlur(bl); updateField({ blur: bl }); } })] }) }), document.body)] }));
@@ -61,7 +63,13 @@ export const HeroPreview = ({ block, fallbackImage, context }) => {
61
63
  // Logic: Use data.imageId, then fallback.id, then fallback.src
62
64
  const activeImageId = data.imageId || (effectiveFallback === null || effectiveFallback === void 0 ? void 0 : effectiveFallback.id) || (effectiveFallback === null || effectiveFallback === void 0 ? void 0 : effectiveFallback.src);
63
65
  const paragraphs = (data.summary || '').split('\n\n').filter(p => p.trim());
64
- return (_jsx("section", { className: "relative pt-32 pb-20", children: _jsxs("div", { className: "max-w-7xl mx-auto px-6 lg:px-12 grid lg:grid-cols-2 gap-16 items-center", children: [_jsxs("div", { className: "order-2 lg:order-1", children: [_jsx("span", { className: "inline-flex px-4 py-1.5 rounded-full border border-sage/20 bg-sage/5 text-sage text-xs font-bold uppercase tracking-widest mb-8", children: data.category || 'Plant Care' }), _jsx("h1", { className: "text-5xl lg:text-7xl font-serif text-forest mb-8 leading-[1.1]", children: data.title || 'Zonder Titel' }), paragraphs.map((p, i) => (_jsx("p", { className: "text-lg lg:text-xl text-gray-600 font-light mt-4", children: p }, i)))] }), _jsx("div", { className: "order-1 lg:order-2 w-full", children: _jsx("div", { className: "relative w-full aspect-[4/5] lg:aspect-[3/4] rounded-3xl overflow-hidden shadow-2xl bg-neutral-100", children: activeImageId && (_jsx(PluginImage, { id: activeImageId, alt: data.title || 'Hero', fill: true, sizes: "(max-width: 1024px) 100vw, 50vw", className: "object-cover w-full h-full", editable: false })) }) })] }) }));
66
+ return (_jsx("section", { className: "relative pt-32 pb-20", children: _jsxs("div", { className: "max-w-7xl mx-auto px-6 lg:px-12 grid lg:grid-cols-2 gap-16 items-center", children: [_jsxs("div", { className: "order-2 lg:order-1", children: [_jsx("span", { className: "inline-flex px-4 py-1.5 rounded-full text-xs font-bold uppercase tracking-widest mb-8", style: {
67
+ color: colors.sage,
68
+ borderWidth: 1,
69
+ borderStyle: 'solid',
70
+ borderColor: `${colors.sage}33`,
71
+ backgroundColor: `${colors.sage}0D`,
72
+ }, children: data.category || 'Plant Care' }), _jsx("h1", { className: "text-5xl lg:text-7xl font-serif mb-8 leading-[1.1]", style: { color: colors.forest }, children: data.title || 'Zonder Titel' }), paragraphs.map((p, i) => (_jsx("p", { className: "text-lg lg:text-xl text-gray-600 font-light mt-4", children: p }, i)))] }), _jsx("div", { className: "order-1 lg:order-2 w-full", children: _jsx("div", { className: "relative w-full aspect-[4/5] lg:aspect-[3/4] rounded-3xl overflow-hidden shadow-2xl bg-neutral-100", children: activeImageId && (_jsx(PluginImage, { id: activeImageId, alt: data.title || 'Hero', fill: true, sizes: "(max-width: 1024px) 100vw, 50vw", className: "object-cover w-full h-full", editable: false })) }) })] }) }));
65
73
  };
66
74
  export const heroBlock = {
67
75
  type: 'hero',
@@ -1 +1 @@
1
- {"version":3,"file":"List.d.ts","sourceRoot":"","sources":["../../../src/plugin-blog/blocks/List.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAqC,qBAAqB,EAA6D,MAAM,oBAAoB,CAAC;AAsRzJ,eAAO,MAAM,SAAS,EAAE,qBA0BvB,CAAC"}
1
+ {"version":3,"file":"List.d.ts","sourceRoot":"","sources":["../../../src/plugin-blog/blocks/List.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAqC,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AA0H9F,eAAO,MAAM,SAAS,EAAE,qBAiBvB,CAAC"}
@@ -1,212 +1,81 @@
1
+ 'use client';
1
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { RichTextEditor, RichTextPreview } from "@jhits/plugin-blog";
3
+ import { useRef, useCallback } from 'react';
3
4
  import { ListChecks } from "lucide-react";
4
- import React from "react";
5
- /**
6
- * Rich Text Formatting Configuration for List Items
7
- * Matches BotanicsAndYou's design system
8
- */
9
- const listFormatting = {
10
- bold: true,
11
- italic: true,
12
- underline: true,
13
- links: true,
14
- colors: ['text-forest', 'text-sage', 'text-primary'],
15
- styles: {
16
- bold: 'font-bold text-black',
17
- italic: 'italic',
18
- underline: 'underline decoration-primary/30',
19
- link: 'text-primary underline decoration-primary/50 hover:text-primary/80 hover:decoration-primary/70 transition-colors',
20
- colorClasses: {
21
- 'text-forest': 'text-forest',
22
- 'text-sage': 'text-sage',
23
- 'text-primary': 'text-primary',
24
- },
25
- },
26
- };
27
- /**
28
- * 1. Shared UI Wrapper
29
- */
30
5
  const ListContainer = ({ children, type }) => {
31
- // Determine the tailwind class for bullet styling
32
6
  const getListClass = () => {
33
7
  if (type === "ol")
34
8
  return "list-decimal pl-5";
35
9
  if (type === "checklist")
36
10
  return "list-none pl-0";
37
- return "list-disc pl-5"; // default ul
11
+ return "list-disc pl-5";
38
12
  };
39
- return (_jsx("div", { className: "bg-white rounded-2xl border border-gray-100 shadow-sm relative", children: _jsx("div", { className: "px-4 pb-4 mt-4", children: _jsx("ul", { className: `${getListClass()} space-y-3 pr-4 text-gray-800`, children: children }) }) }));
13
+ return (_jsx("div", { className: "rounded-2xl border border-gray-100 shadow-sm relative", children: _jsx("div", { className: "px-4 pb-4 mt-4", children: _jsx("ul", { className: `${getListClass()} space-y-3 pr-4 text-[color:var(--content-text-color,#374151)] font-[family:var(--content-font-family,inherit)]`, children: children }) }) }));
40
14
  };
41
- /**
42
- * 2. Editor Component
43
- */
44
15
  const ListEdit = ({ block, onUpdate }) => {
45
- // Ensure items are treated consistently as ItemObj[]
46
- // Handle both string[] (legacy) and ItemObj[] formats
47
- const containerRef = React.useRef(null);
48
16
  const rawItems = Array.isArray(block.data.items) ? block.data.items : [];
49
17
  const items = rawItems.map((item) => {
50
- if (typeof item === 'string') {
51
- return { text: item, html: item };
52
- }
18
+ if (typeof item === 'string')
19
+ return item;
53
20
  if (typeof item === 'object' && item !== null && 'text' in item) {
54
- const itemObj = item;
55
- return {
56
- text: itemObj.text || '',
57
- html: itemObj.html || itemObj.text || ''
58
- };
21
+ return item.text || '';
59
22
  }
60
- return { text: '', html: '' };
23
+ return '';
61
24
  });
62
25
  const type = (block.data.type || "ul");
63
- const handleAddItem = (index) => {
26
+ const inputRefs = useRef([]);
27
+ const handleKeyDown = useCallback((e, idx) => {
28
+ if (e.key === 'Enter') {
29
+ e.preventDefault();
30
+ const newItems = [...items];
31
+ newItems.splice(idx + 1, 0, '');
32
+ onUpdate({ items: newItems });
33
+ setTimeout(() => { var _a; return (_a = inputRefs.current[idx + 1]) === null || _a === void 0 ? void 0 : _a.focus(); }, 0);
34
+ }
35
+ if (e.key === 'Backspace' && items[idx] === '' && items.length > 1) {
36
+ e.preventDefault();
37
+ const newItems = items.filter((_, i) => i !== idx);
38
+ onUpdate({ items: newItems });
39
+ if (idx > 0) {
40
+ setTimeout(() => { var _a; return (_a = inputRefs.current[idx - 1]) === null || _a === void 0 ? void 0 : _a.focus(); }, 0);
41
+ }
42
+ }
43
+ }, [items, onUpdate]);
44
+ const handleChange = useCallback((idx, value) => {
64
45
  const newItems = [...items];
65
- newItems.splice(index + 1, 0, { text: '', html: '' });
46
+ newItems[idx] = value;
66
47
  onUpdate({ items: newItems });
67
- };
68
- return (_jsx("div", { ref: containerRef, children: _jsx(ListContainer, { type: type, children: items.map((item, idx) => {
69
- const html = item.html || item.text || '';
70
- const isEmpty = !html || html.replace(/<[^>]*>/g, '').trim() === '';
71
- return (_jsxs("li", { className: `${type === "checklist" ? "flex items-start gap-3" : ""}`, children: [type === "checklist" && (_jsx("input", { type: "checkbox", disabled: true, className: "mt-1.5 w-4 h-4 accent-gray-400 cursor-not-allowed shrink-0" })), _jsxs("div", { className: "flex-1 min-w-0", children: [_jsx(RichTextEditor, { value: html, onChange: (htmlContent) => {
72
- const textContent = htmlContent.replace(/<[^>]*>/g, '').replace(/&nbsp;/g, ' ');
73
- const newItems = [...items];
74
- newItems[idx] = {
75
- text: textContent,
76
- html: htmlContent
77
- };
78
- onUpdate({ items: newItems });
79
- }, onKeyDown: (e) => {
80
- const contentEditable = e.target;
81
- if (!contentEditable || contentEditable.contentEditable !== 'true')
82
- return;
83
- const currentHtml = contentEditable.innerHTML || '';
84
- const currentText = currentHtml.replace(/<[^>]*>/g, '').replace(/&nbsp;/g, ' ').trim();
85
- const isEmpty = !currentText || currentText === '';
86
- // Intercept Enter key to create new list item instead of line break
87
- if (e.key === 'Enter' && !e.shiftKey) {
88
- e.preventDefault();
89
- e.stopPropagation();
90
- // Update current item with trimmed content
91
- const newItems = [...items];
92
- newItems[idx] = {
93
- text: currentText,
94
- html: currentText
95
- };
96
- // Add new item after current one
97
- const newItem = { text: '', html: '' };
98
- newItems.splice(idx + 1, 0, newItem);
99
- onUpdate({ items: newItems });
100
- // Focus the next editor after re-render
101
- setTimeout(() => {
102
- // Find all contentEditable elements and get the next one
103
- const allEditors = document.querySelectorAll('[contenteditable="true"]');
104
- const currentEditorIndex = Array.from(allEditors).indexOf(contentEditable);
105
- const nextEditor = allEditors[currentEditorIndex + 1];
106
- if (nextEditor) {
107
- nextEditor.focus();
108
- // Place cursor at the start
109
- const range = document.createRange();
110
- range.selectNodeContents(nextEditor);
111
- range.collapse(true);
112
- const selection = window.getSelection();
113
- selection === null || selection === void 0 ? void 0 : selection.removeAllRanges();
114
- selection === null || selection === void 0 ? void 0 : selection.addRange(range);
115
- }
116
- }, 0);
117
- return;
118
- }
119
- // Handle Backspace/Delete to remove empty list items
120
- if ((e.key === 'Backspace' || e.key === 'Delete') && isEmpty && items.length > 1) {
121
- const selection = window.getSelection();
122
- if (!selection || selection.rangeCount === 0)
123
- return;
124
- const range = selection.getRangeAt(0);
125
- const isAtStart = () => {
126
- if (!range.collapsed)
127
- return false;
128
- const startRange = range.cloneRange();
129
- startRange.selectNodeContents(contentEditable);
130
- startRange.setEnd(range.startContainer, range.startOffset);
131
- return startRange.toString().trim() === '';
132
- };
133
- if ((e.key === 'Backspace' && isAtStart())) {
134
- e.preventDefault();
135
- e.stopPropagation();
136
- // Calculate focus index BEFORE updating state
137
- const focusIndex = idx > 0 ? idx - 1 : 0;
138
- const newItems = items.filter((_, i) => i !== idx);
139
- onUpdate({ items: newItems });
140
- setTimeout(() => {
141
- // SCOPED SEARCH: Look only inside this specific list container
142
- if (containerRef.current) {
143
- const listEditors = containerRef.current.querySelectorAll('[contenteditable="true"]');
144
- const targetEditor = listEditors[focusIndex];
145
- if (targetEditor) {
146
- targetEditor.focus();
147
- const newRange = document.createRange();
148
- newRange.selectNodeContents(targetEditor);
149
- newRange.collapse(false); // Go to end of previous item
150
- const newSel = window.getSelection();
151
- newSel === null || newSel === void 0 ? void 0 : newSel.removeAllRanges();
152
- newSel === null || newSel === void 0 ? void 0 : newSel.addRange(newRange);
153
- }
154
- }
155
- }, 0);
156
- }
157
- }
158
- }, placeholder: "Typ een item...", formatting: listFormatting, className: "leading-relaxed min-h-[1.5em] w-full text-gray-800", isFocused: false }), isEmpty && idx === items.length - 1 && (_jsx("button", { type: "button", onClick: () => handleAddItem(idx), className: "mt-2 text-xs text-gray-400 hover:text-gray-600", children: "+ Voeg item toe" }))] })] }, idx));
159
- }) }) }));
48
+ }, [items, onUpdate]);
49
+ return (_jsx(ListContainer, { type: type, children: items.map((item, idx) => (_jsxs("li", { className: `${type === "checklist" ? "flex items-start gap-3" : ""}`, children: [type === "checklist" && (_jsx("input", { type: "checkbox", disabled: true, className: "mt-1.5 w-4 h-4 accent-gray-400 cursor-not-allowed shrink-0" })), _jsx("input", { ref: (el) => { inputRefs.current[idx] = el; }, type: "text", value: item, onChange: (e) => handleChange(idx, e.target.value), onKeyDown: (e) => handleKeyDown(e, idx), placeholder: "Typ een item...", className: "flex-1 leading-relaxed min-h-[1.5em] w-full text-[color:var(--content-text-color,#374151)] bg-transparent border-none outline-none focus:ring-0 p-0" })] }, idx))) }));
160
50
  };
161
- /**
162
- * 3. Preview Component
163
- */
164
51
  const ListPreview = ({ block }) => {
165
- // Handle both string[] (legacy) and ItemObj[] formats
166
52
  const rawItems = Array.isArray(block.data.items) ? block.data.items : [];
167
53
  const items = rawItems.map((item) => {
168
- if (typeof item === 'string') {
169
- return { text: item, html: item };
170
- }
54
+ if (typeof item === 'string')
55
+ return item;
171
56
  if (typeof item === 'object' && item !== null && 'text' in item) {
172
- const itemObj = item;
173
- return {
174
- text: itemObj.text || '',
175
- html: itemObj.html || itemObj.text || ''
176
- };
57
+ return item.text || '';
177
58
  }
178
- return { text: '', html: '' };
59
+ return '';
179
60
  });
180
61
  const type = (block.data.type || "ul");
181
62
  return (_jsx(ListContainer, { type: type, children: items.map((item, idx) => {
182
- const html = item.html || item.text || '';
183
- if (!html || html.replace(/<[^>]*>/g, '').trim() === '')
63
+ if (!item.trim())
184
64
  return null;
185
- return (_jsxs("li", { className: `${type === "checklist" ? "flex items-start gap-3" : ""}`, children: [type === "checklist" && (_jsx("input", { type: "checkbox", className: "mt-1.5 w-4 h-4 accent-gray-600 shrink-0" })), _jsx("div", { className: "leading-relaxed min-h-[1.5em] w-full", children: _jsx(RichTextPreview, { content: html, formatting: listFormatting, className: "text-gray-800" }) })] }, idx));
65
+ return (_jsxs("li", { className: `${type === "checklist" ? "flex items-start gap-3" : ""}`, children: [type === "checklist" && (_jsx("input", { type: "checkbox", className: "mt-1.5 w-4 h-4 accent-gray-600 shrink-0" })), _jsx("span", { className: "leading-relaxed text-[color:var(--content-text-color,#374151)]", children: item })] }, idx));
186
66
  }) }));
187
67
  };
188
68
  export const listBlock = {
189
69
  type: 'list',
190
70
  name: 'Lijst',
191
- description: '',
71
+ description: 'Opsomming met items',
192
72
  icon: ListChecks,
193
73
  defaultData: {
194
- title: '',
195
- items: [{ text: '' }],
74
+ items: [''],
75
+ type: 'ul',
196
76
  },
197
77
  category: 'text',
198
- validate: (data) => {
199
- return typeof data.title === 'string' &&
200
- Array.isArray(data.items) &&
201
- data.items.every((item) => {
202
- if (typeof item === 'string')
203
- return true;
204
- if (typeof item === 'object' && item !== null && 'text' in item) {
205
- return typeof item.text === 'string';
206
- }
207
- return false;
208
- });
209
- },
78
+ validate: (data) => Array.isArray(data.items),
210
79
  components: {
211
80
  Edit: ListEdit,
212
81
  Preview: ListPreview,
@@ -1 +1 @@
1
- {"version":3,"file":"Paragraph.d.ts","sourceRoot":"","sources":["../../../src/plugin-blog/blocks/Paragraph.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAqC,qBAAqB,EAA6D,MAAM,oBAAoB,CAAC;AAkEzJ,eAAO,MAAM,cAAc,EAAE,qBAkB5B,CAAC"}
1
+ {"version":3,"file":"Paragraph.d.ts","sourceRoot":"","sources":["../../../src/plugin-blog/blocks/Paragraph.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAqC,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAiD9F,eAAO,MAAM,cAAc,EAAE,qBAgB5B,CAAC"}