diff --git a/ios/Sources/GutenbergKit/Sources/EditorJSMessage.swift b/ios/Sources/GutenbergKit/Sources/EditorJSMessage.swift index 894074738..cd3934565 100644 --- a/ios/Sources/GutenbergKit/Sources/EditorJSMessage.swift +++ b/ios/Sources/GutenbergKit/Sources/EditorJSMessage.swift @@ -67,7 +67,6 @@ struct EditorJSMessage { } struct ShowBlockInserterBody: Decodable { - let blocks: [EditorBlock] - let destinationBlockName: String? + let sections: [BlockInserterSection] } } diff --git a/ios/Sources/GutenbergKit/Sources/EditorViewController.swift b/ios/Sources/GutenbergKit/Sources/EditorViewController.swift index daf3e347b..e52a6a938 100644 --- a/ios/Sources/GutenbergKit/Sources/EditorViewController.swift +++ b/ios/Sources/GutenbergKit/Sources/EditorViewController.swift @@ -174,6 +174,12 @@ public final class EditorViewController: UIViewController, GutenbergEditorContro try await webView.evaluateJavaScript("editor.getContent();") as! String } + public struct EditorTitleAndContent: Decodable { + public let title: String + public let content: String + public let changed: Bool + } + /// Returns the current editor title and content. public func getTitleAndContent() async throws -> EditorTitleAndContent { let result = try await webView.evaluateJavaScript("editor.getTitleAndContent();") @@ -250,12 +256,11 @@ public final class EditorViewController: UIViewController, GutenbergEditorContro let host = UIHostingController(rootView: NavigationStack { BlockInserterView( - blocks: data.blocks, - destinationBlockName: data.destinationBlockName, + sections: data.sections, mediaPicker: mediaPicker, presentationContext: context, onBlockSelected: { [weak self] block in - self?.insertBlockFromInserter(block.name) + self?.insertBlockFromInserter(block.id) }, onMediaSelected: { print("insert media:", $0) @@ -268,8 +273,8 @@ public final class EditorViewController: UIViewController, GutenbergEditorContro present(host, animated: true) } - private func insertBlockFromInserter(_ blockName: String) { - evaluate("window.blockInserter.insertBlock('\(blockName)')") + private func insertBlockFromInserter(_ blockID: String) { + evaluate("window.blockInserter.insertBlock('\(blockID)')") } private func openMediaLibrary(_ config: OpenMediaLibraryAction) { diff --git a/ios/Sources/GutenbergKit/Sources/Helpers/BlockIconCache.swift b/ios/Sources/GutenbergKit/Sources/Helpers/BlockIconCache.swift index 33d1fb1e8..4a7cafd30 100644 --- a/ios/Sources/GutenbergKit/Sources/Helpers/BlockIconCache.swift +++ b/ios/Sources/GutenbergKit/Sources/Helpers/BlockIconCache.swift @@ -5,7 +5,7 @@ import SVGKit final class BlockIconCache: ObservableObject { var icons: [String: Result] = [:] - func getIcon(for block: EditorBlock) -> SVGKImage? { + func getIcon(for block: BlockType) -> SVGKImage? { if let result = icons[block.id] { return try? result.get() } @@ -14,7 +14,7 @@ final class BlockIconCache: ObservableObject { return try? result.get() } - private func _getIcon(for block: EditorBlock) throws -> SVGKImage { + private func _getIcon(for block: BlockType) throws -> SVGKImage { guard let svg = block.icon, !svg.isEmpty, let source = SVGKSourceString.source(fromContentsOf: svg), diff --git a/ios/Sources/GutenbergKit/Sources/Views/BlockInserter/BlockIconView.swift b/ios/Sources/GutenbergKit/Sources/Views/BlockInserter/BlockIconView.swift index d59162445..edd845740 100644 --- a/ios/Sources/GutenbergKit/Sources/Views/BlockInserter/BlockIconView.swift +++ b/ios/Sources/GutenbergKit/Sources/Views/BlockInserter/BlockIconView.swift @@ -2,7 +2,7 @@ import SwiftUI import SVGKit struct BlockIconView: View { - let block: EditorBlock + let block: BlockType let size: CGFloat @EnvironmentObject private var cache: BlockIconCache diff --git a/ios/Sources/GutenbergKit/Sources/Views/BlockInserter/BlockInserterBlockView.swift b/ios/Sources/GutenbergKit/Sources/Views/BlockInserter/BlockInserterBlockView.swift index ed6990f40..62b3dc0a7 100644 --- a/ios/Sources/GutenbergKit/Sources/Views/BlockInserter/BlockInserterBlockView.swift +++ b/ios/Sources/GutenbergKit/Sources/Views/BlockInserter/BlockInserterBlockView.swift @@ -2,7 +2,7 @@ import SwiftUI import SVGKit struct BlockInserterBlockView: View { - let block: EditorBlock + let block: BlockType let action: () -> Void @State private var isPressed = false @@ -29,6 +29,7 @@ struct BlockInserterBlockView: View { .padding(.horizontal, 4) } .buttonStyle(.plain) + .disabled(block.isDisabled) .frame(maxWidth: .infinity, alignment: .center) .contextMenu { Button { @@ -57,7 +58,7 @@ struct BlockInserterBlockView: View { } private struct BlockDetailedView: View { - let block: EditorBlock + let block: BlockType var body: some View { HStack(spacing: 16) { diff --git a/ios/Sources/GutenbergKit/Sources/Views/BlockInserter/BlockInserterSectionView.swift b/ios/Sources/GutenbergKit/Sources/Views/BlockInserter/BlockInserterSectionView.swift index d39758866..d69a39d92 100644 --- a/ios/Sources/GutenbergKit/Sources/Views/BlockInserter/BlockInserterSectionView.swift +++ b/ios/Sources/GutenbergKit/Sources/Views/BlockInserter/BlockInserterSectionView.swift @@ -1,18 +1,32 @@ import SwiftUI -struct BlockInserterSection: Identifiable { +struct BlockInserterSection: Identifiable, Decodable { var id: String { category } let category: String let name: String? - let blocks: [EditorBlock] + let blocks: [BlockType] } struct BlockInserterSectionView: View { let section: BlockInserterSection - let onBlockSelected: (EditorBlock) -> Void + let onBlockSelected: (BlockType) -> Void @ScaledMetric(relativeTo: .largeTitle) private var miniumSize = 80 @ScaledMetric(relativeTo: .largeTitle) private var padding = 20 + @State private var isExpanded = false + + private let initialDisplayCount = 16 + + private var displayedBlocks: [BlockType] { + if !isExpanded && section.blocks.count > initialDisplayCount { + return Array(section.blocks.prefix(initialDisplayCount)) + } + return section.blocks + } + + private var hasMoreBlocks: Bool { + section.blocks.count > initialDisplayCount + } var body: some View { VStack(alignment: .leading, spacing: 20) { @@ -24,6 +38,9 @@ struct BlockInserterSectionView: View { .frame(maxWidth: .infinity, alignment: .leading) } grid + if hasMoreBlocks { + toggleButton + } } .padding(.top, section.name != nil ? 20 : 24) .padding(.bottom, 10) @@ -32,7 +49,7 @@ struct BlockInserterSectionView: View { private var grid: some View { LazyVGrid(columns: [GridItem(.adaptive(minimum: miniumSize, maximum: miniumSize * 1.5), spacing: 0)]) { - ForEach(section.blocks) { block in + ForEach(displayedBlocks) { block in BlockInserterBlockView(block: block) { onBlockSelected(block) } @@ -40,4 +57,27 @@ struct BlockInserterSectionView: View { } .padding(.horizontal, 12) } + + private var toggleButton: some View { + Button { + withAnimation { + isExpanded.toggle() + } + } label: { + HStack { + // TODO: CMM-874 add localization + Text(isExpanded ? "Show Less" : "Show More") + .font(.subheadline) + .fontWeight(.medium) + .foregroundStyle(Color.secondary) + Image(systemName: isExpanded ? "chevron.up" : "chevron.down") + .font(.caption) + .foregroundStyle(Color.primary) + } + .frame(maxWidth: .infinity) + .padding(.vertical, 8) + } + .buttonStyle(.plain) + .padding(.horizontal, 12) + } } diff --git a/ios/Sources/GutenbergKit/Sources/Views/BlockInserter/BlockInserterView+PreviewData.swift b/ios/Sources/GutenbergKit/Sources/Views/BlockInserter/BlockInserterView+PreviewData.swift index d6c025aa6..bad931f7d 100644 --- a/ios/Sources/GutenbergKit/Sources/Views/BlockInserter/BlockInserterView+PreviewData.swift +++ b/ios/Sources/GutenbergKit/Sources/Views/BlockInserter/BlockInserterView+PreviewData.swift @@ -1,10 +1,11 @@ #if DEBUG import Foundation -extension EditorBlock { - static let mocks: [EditorBlock] = [ +extension BlockType { + static let mocks: [BlockType] = [ // Text blocks - EditorBlock( + BlockType( + id: "core/paragraph", name: "core/paragraph", title: "Paragraph", description: "Start with the basic building block of all narrative.", @@ -12,7 +13,8 @@ extension EditorBlock { keywords: ["text", "paragraph"], icon: paragraphSVG ), - EditorBlock( + BlockType( + id: "core/heading", name: "core/heading", title: "Heading", description: "Introduce new sections and organize content to help visitors find what they need.", @@ -20,7 +22,8 @@ extension EditorBlock { keywords: ["title", "heading"], icon: headingSVG ), - EditorBlock( + BlockType( + id: "core/list", name: "core/list", title: "List", description: "Create a bulleted or numbered list.", @@ -28,7 +31,8 @@ extension EditorBlock { keywords: ["bullet", "number", "list"], icon: listSVG ), - EditorBlock( + BlockType( + id: "core/quote", name: "core/quote", title: "Quote", description: "Give quoted text visual emphasis.", @@ -36,7 +40,8 @@ extension EditorBlock { keywords: ["quote", "citation"], icon: quoteSVG ), - EditorBlock( + BlockType( + id: "core/code", name: "core/code", title: "Code", description: "Display code snippets that respect your spacing and tabs.", @@ -44,7 +49,8 @@ extension EditorBlock { keywords: ["code", "programming"], icon: codeSVG ), - EditorBlock( + BlockType( + id: "core/preformatted", name: "core/preformatted", title: "Preformatted", description: "Add text that respects your spacing and tabs, and also allows styling.", @@ -52,7 +58,8 @@ extension EditorBlock { keywords: ["preformatted", "monospace"], icon: nil ), - EditorBlock( + BlockType( + id: "core/pullquote", name: "core/pullquote", title: "Pullquote", description: "Give special visual emphasis to a quote from your text.", @@ -60,7 +67,8 @@ extension EditorBlock { keywords: ["pullquote", "quote"], icon: quoteSVG ), - EditorBlock( + BlockType( + id: "core/verse", name: "core/verse", title: "Verse", description: "Insert poetry. Use special spacing formats. Or quote song lyrics.", @@ -68,7 +76,8 @@ extension EditorBlock { keywords: ["poetry", "verse"], icon: nil ), - EditorBlock( + BlockType( + id: "core/table", name: "core/table", title: "Table", description: "Create structured content in rows and columns to display information.", @@ -78,7 +87,8 @@ extension EditorBlock { ), // Media blocks - EditorBlock( + BlockType( + id: "core/image", name: "core/image", title: "Image", description: "Insert an image to make a visual statement.", @@ -86,7 +96,8 @@ extension EditorBlock { keywords: ["photo", "picture"], icon: imageSVG ), - EditorBlock( + BlockType( + id: "core/gallery", name: "core/gallery", title: "Gallery", description: "Display multiple images in a rich gallery.", @@ -94,7 +105,8 @@ extension EditorBlock { keywords: ["images", "photos"], icon: imageSVG ), - EditorBlock( + BlockType( + id: "core/audio", name: "core/audio", title: "Audio", description: "Embed a simple audio player.", @@ -102,7 +114,8 @@ extension EditorBlock { keywords: ["music", "sound", "podcast"], icon: nil ), - EditorBlock( + BlockType( + id: "core/video", name: "core/video", title: "Video", description: "Embed a video from your media library or upload a new one.", @@ -110,7 +123,8 @@ extension EditorBlock { keywords: ["movie", "film"], icon: videoSVG ), - EditorBlock( + BlockType( + id: "core/cover", name: "core/cover", title: "Cover", description: "Add an image or video with a text overlay.", @@ -118,7 +132,8 @@ extension EditorBlock { keywords: ["banner", "hero", "cover"], icon: nil ), - EditorBlock( + BlockType( + id: "core/file", name: "core/file", title: "File", description: "Add a link to a downloadable file.", @@ -126,7 +141,8 @@ extension EditorBlock { keywords: ["download", "pdf", "document"], icon: nil ), - EditorBlock( + BlockType( + id: "core/media-text", name: "core/media-text", title: "Media & Text", description: "Set media and words side-by-side for a richer layout.", @@ -136,7 +152,8 @@ extension EditorBlock { ), // Design blocks - EditorBlock( + BlockType( + id: "core/columns", name: "core/columns", title: "Columns", description: "Display content in multiple columns.", @@ -144,7 +161,8 @@ extension EditorBlock { keywords: ["layout", "columns"], icon: nil ), - EditorBlock( + BlockType( + id: "core/group", name: "core/group", title: "Group", description: "Gather blocks in a container.", @@ -152,7 +170,8 @@ extension EditorBlock { keywords: ["container", "wrapper", "group"], icon: nil ), - EditorBlock( + BlockType( + id: "core/separator", name: "core/separator", title: "Separator", description: "Create a break between ideas or sections.", @@ -160,7 +179,8 @@ extension EditorBlock { keywords: ["divider", "hr"], icon: nil ), - EditorBlock( + BlockType( + id: "core/spacer", name: "core/spacer", title: "Spacer", description: "Add white space between blocks.", @@ -168,7 +188,8 @@ extension EditorBlock { keywords: ["space", "gap"], icon: nil ), - EditorBlock( + BlockType( + id: "core/buttons", name: "core/buttons", title: "Buttons", description: "Prompt visitors to take action with a group of button-style links.", @@ -176,7 +197,8 @@ extension EditorBlock { keywords: ["button", "link", "cta"], icon: buttonSVG ), - EditorBlock( + BlockType( + id: "core/more", name: "core/more", title: "More", description: "Content before this block will be shown in the excerpt on your archives page.", @@ -186,14 +208,16 @@ extension EditorBlock { ), // Widget blocks - EditorBlock( + BlockType( + id: "core/search", name: "core/search", title: "Search", description: "Help visitors find your content.", category: "widgets", - keywords: ["find", "search"], + keywords: ["find", "search"] ), - EditorBlock( + BlockType( + id: "core/archives", name: "core/archives", title: "Archives", description: "Display a date archive of your posts.", @@ -201,7 +225,8 @@ extension EditorBlock { keywords: ["archive", "history"], icon: nil ), - EditorBlock( + BlockType( + id: "core/categories", name: "core/categories", title: "Categories", description: "Display a list of all categories.", @@ -211,7 +236,8 @@ extension EditorBlock { ), // Embed blocks - EditorBlock( + BlockType( + id: "core-embed/youtube", name: "core-embed/youtube", title: "YouTube", description: "Embed a YouTube video.", @@ -219,7 +245,8 @@ extension EditorBlock { keywords: ["video", "youtube"], icon: nil ), - EditorBlock( + BlockType( + id: "core-embed/twitter", name: "core-embed/twitter", title: "Twitter", description: "Embed a tweet.", @@ -227,7 +254,8 @@ extension EditorBlock { keywords: ["tweet", "twitter"], icon: nil ), - EditorBlock( + BlockType( + id: "core-embed/vimeo", name: "core-embed/vimeo", title: "Vimeo", description: "Embed a Vimeo video.", @@ -235,7 +263,8 @@ extension EditorBlock { keywords: ["video", "vimeo"], icon: nil ), - EditorBlock( + BlockType( + id: "core-embed/instagram", name: "core-embed/instagram", title: "Instagram", description: "Embed an Instagram post.", @@ -245,7 +274,8 @@ extension EditorBlock { ), // Additional common blocks - EditorBlock( + BlockType( + id: "core/html", name: "core/html", title: "Custom HTML", description: "Add custom HTML code and preview it as you edit.", @@ -253,7 +283,8 @@ extension EditorBlock { keywords: ["html", "code", "custom"], icon: codeSVG ), - EditorBlock( + BlockType( + id: "core/shortcode", name: "core/shortcode", title: "Shortcode", description: "Insert additional custom elements with WordPress shortcodes.", @@ -261,7 +292,8 @@ extension EditorBlock { keywords: ["shortcode", "custom"], icon: nil ), - EditorBlock( + BlockType( + id: "core/social-links", name: "core/social-links", title: "Social Icons", description: "Display icons linking to your social media profiles.", diff --git a/ios/Sources/GutenbergKit/Sources/Views/BlockInserter/BlockInserterView.swift b/ios/Sources/GutenbergKit/Sources/Views/BlockInserter/BlockInserterView.swift index dc0d6b3e6..663c98c54 100644 --- a/ios/Sources/GutenbergKit/Sources/Views/BlockInserter/BlockInserterView.swift +++ b/ios/Sources/GutenbergKit/Sources/Views/BlockInserter/BlockInserterView.swift @@ -3,11 +3,10 @@ import PhotosUI import UIKit struct BlockInserterView: View { - let blocks: [EditorBlock] - let destinationBlockName: String? + let sections: [BlockInserterSection] let mediaPicker: MediaPickerController? let presentationContext: MediaPickerPresentationContext - let onBlockSelected: (EditorBlock) -> Void + let onBlockSelected: (BlockType) -> Void let onMediaSelected: ([MediaInfo]) -> Void @StateObject private var viewModel: BlockInserterViewModel @@ -18,21 +17,19 @@ struct BlockInserterView: View { @Environment(\.dismiss) private var dismiss init( - blocks: [EditorBlock], - destinationBlockName: String?, + sections: [BlockInserterSection], mediaPicker: MediaPickerController?, presentationContext: MediaPickerPresentationContext, - onBlockSelected: @escaping (EditorBlock) -> Void, + onBlockSelected: @escaping (BlockType) -> Void, onMediaSelected: @escaping ([MediaInfo]) -> Void ) { - self.blocks = blocks - self.destinationBlockName = destinationBlockName + self.sections = sections self.mediaPicker = mediaPicker self.presentationContext = presentationContext self.onBlockSelected = onBlockSelected self.onMediaSelected = onMediaSelected - let viewModel = BlockInserterViewModel(blocks: blocks, destinationBlockName: destinationBlockName) + let viewModel = BlockInserterViewModel(sections: sections) self._viewModel = StateObject(wrappedValue: viewModel) } @@ -85,7 +82,7 @@ struct BlockInserterView: View { // MARK: - Actions - private func insertBlock(_ block: EditorBlock) { + private func insertBlock(_ block: BlockType) { dismiss() onBlockSelected(block) } @@ -97,8 +94,9 @@ struct BlockInserterView: View { #Preview { NavigationStack { BlockInserterView( - blocks: EditorBlock.mocks, - destinationBlockName: nil, + sections: [ + BlockInserterSection(category: "text", name: "Text", blocks: BlockType.mocks) + ], mediaPicker: MockMediaPickerController(), presentationContext: MediaPickerPresentationContext(), onBlockSelected: { diff --git a/ios/Sources/GutenbergKit/Sources/Views/BlockInserter/BlockInserterViewModel.swift b/ios/Sources/GutenbergKit/Sources/Views/BlockInserter/BlockInserterViewModel.swift index a38416611..6eb0e80be 100644 --- a/ios/Sources/GutenbergKit/Sources/Views/BlockInserter/BlockInserterViewModel.swift +++ b/ios/Sources/GutenbergKit/Sources/Views/BlockInserter/BlockInserterViewModel.swift @@ -7,21 +7,16 @@ class BlockInserterViewModel: ObservableObject { @Published var searchText = "" @Published private(set) var sections: [BlockInserterSection] = [] - private let blocks: [EditorBlock] private let allSections: [BlockInserterSection] private var cancellables = Set() - init(blocks: [EditorBlock], destinationBlockName: String?) { - let blocks = blocks.filter { $0.name != "core/missing" } - - self.blocks = blocks - - self.allSections = BlockInserterViewModel.createSections(from: blocks, destinationBlockName: destinationBlockName) - self.sections = allSections + init(sections: [BlockInserterSection]) { + self.allSections = sections + self.sections = sections setupSearchObserver() } - + private func setupSearchObserver() { $searchText .debounce(for: .milliseconds(200), scheduler: RunLoop.main) @@ -36,7 +31,7 @@ class BlockInserterViewModel: ObservableObject { sections = allSections } else { sections = allSections.compactMap { section in - let filtered = SearchEngine() + let filtered = SearchEngine() .search(query: searchText, in: section.blocks) return filtered.isEmpty ? nil : BlockInserterSection( category: section.category, @@ -46,113 +41,4 @@ class BlockInserterViewModel: ObservableObject { } } } - - private static func createSections(from blocks: [EditorBlock], destinationBlockName: String?) -> [BlockInserterSection] { - var sections: [BlockInserterSection] = [] - - // Separate contextual blocks (specifically allowed in current parent block) - // A block is contextual if the destination block name is in its parents array - let contextualBlocks = blocks.filter { block in - guard let destinationBlockName = destinationBlockName else { return false } - return !block.parents.isEmpty && block.parents.contains(destinationBlockName) - } - - // Add contextual section at the top if there are contextual blocks - if !contextualBlocks.isEmpty { - sections.append(BlockInserterSection(category: "gbk-contextual", name: nil, blocks: contextualBlocks)) - } - - // Group regular blocks by category - let blocksByCategory = Dictionary(grouping: blocks) { - $0.category?.lowercased() ?? "common" - } - - let categories = Constants.orderedCategories - - // Add known categories in a predefined order - for (category, name) in categories { - if let blocks = blocksByCategory[category] { - let sortedBlocks = orderBlocks(blocks, category: category) - // Use nil for text category, otherwise use the display name - let displayName = (category == "text" && contextualBlocks.isEmpty) ? nil : name - sections.append(BlockInserterSection(category: category, name: displayName, blocks: sortedBlocks)) - } - } - - // Add any remaining categories - for (category, blocks) in blocksByCategory { - let isStandardCategory = categories.contains { $0.key == category } - if !isStandardCategory { - sections.append(BlockInserterSection(category: category, name: category.capitalized, blocks: blocks)) - } - } - - return sections - } -} - -// MARK: Ordering - -private func orderBlocks(_ blocks: [EditorBlock], category: String) -> [EditorBlock] { - switch category { - case "text": - return _orderBlocks(blocks, order: [ - "core/paragraph", - "core/heading", - "core/list", - "core/list-item", - "core/quote", - "core/code", - "core/preformatted", - "core/verse", - "core/table" - ]) - case "media": - return _orderBlocks(blocks, order: [ - "core/image", - "core/video", - "core/gallery", - "core/embed", - "core/audio", - "core/file" - ]) - case "design": - return _orderBlocks(blocks, order: [ - "core/separator", - "core/spacer", - "core/columns", - "core/column" - ]) - default: - return blocks - } -} - -private func _orderBlocks(_ blocks: [EditorBlock], order: [String]) -> [EditorBlock] { - var orderedBlocks: [EditorBlock] = [] - - // Add blocks in a predefined order - for name in order { - if let block = blocks.first(where: { $0.name == name }) { - orderedBlocks.append(block) - } - } - - // Add remaining blocks in their original order - let remainingBlocks = blocks.filter { block in - !order.contains(block.name) - } - - return orderedBlocks + remainingBlocks -} - -private enum Constants { - static let orderedCategories: [(key: String, displayName: String)] = [ - ("text", "Text"), - ("media", "Media"), - ("design", "Design"), - ("widgets", "Widgets"), - ("theme", "Theme"), - ("embed", "Embeds") - ] } diff --git a/ios/Sources/GutenbergKit/Sources/EditorTypes.swift b/ios/Sources/GutenbergKit/Sources/Views/BlockInserter/BlockType.swift similarity index 74% rename from ios/Sources/GutenbergKit/Sources/EditorTypes.swift rename to ios/Sources/GutenbergKit/Sources/Views/BlockInserter/BlockType.swift index e51e91948..d616ddf8f 100644 --- a/ios/Sources/GutenbergKit/Sources/EditorTypes.swift +++ b/ios/Sources/GutenbergKit/Sources/Views/BlockInserter/BlockType.swift @@ -1,8 +1,11 @@ import Foundation -struct EditorBlock: Decodable, Identifiable { - var id: String { name } - +struct BlockType: Decodable, Identifiable { + /// Unique identifier for this block variant. Note that this is NOT the same as `name`. + /// Multiple blocks can share the same `name` but have different `id` values to represent + /// different variants with different initial attributes (e.g., core/embed variants for + /// YouTube, Vimeo, etc.). + let id: String let name: String let title: String? let description: String? @@ -14,13 +17,7 @@ struct EditorBlock: Decodable, Identifiable { var parents: [String] = [] } -public struct EditorTitleAndContent: Decodable { - public let title: String - public let content: String - public let changed: Bool -} - -extension EditorBlock: Searchable { +extension BlockType: Searchable { /// Sets the searchable fields in the order of priority func searchableFields() -> [SearchableField] { var fields: [SearchableField] = [] diff --git a/src/components/native-block-inserter-button/index.jsx b/src/components/native-block-inserter-button/index.jsx index f521204b9..2efb15f0e 100644 --- a/src/components/native-block-inserter-button/index.jsx +++ b/src/components/native-block-inserter-button/index.jsx @@ -32,7 +32,7 @@ import useInsertionPoint from '@wordpress/block-editor/build-module/components/i import useBlockTypesState from '@wordpress/block-editor/build-module/components/inserter/hooks/use-block-types-state'; import { store as blockEditorStore } from '@wordpress/block-editor'; import { debug } from '../../utils/logger'; -import { serializeBlocksForNative } from '../../utils/blocks'; +import { preprocessBlockTypesForNativeInserter } from '../../utils/blocks'; import { showBlockInserter } from '../../utils/bridge'; /** @@ -74,28 +74,30 @@ export default function NativeBlockInserterButton() { selectBlockOnInsert: true, } ); - const [ inserterItems, , , onSelectItem ] = useBlockTypesState( + const [ inserterItems, categories, , onSelectItem ] = useBlockTypesState( destinationRootClientId, onInsertBlocks, false // isQuick ); - // Serialize blocks for native consumption - const blocks = serializeBlocksForNative( inserterItems ); + // Preprocess blocks into sections for native consumption + // Categories are passed to get localized category names + const sections = preprocessBlockTypesForNativeInserter( + inserterItems, + destinationBlockName, + categories + ); // Expose the current inserter state globally for native access // This automatically stays in sync with editor state via hooks useEffect( () => { window.blockInserter = { - blocks, - destinationBlockName, - insertBlock: ( blockName ) => { - const item = inserterItems.find( - ( i ) => i.name === blockName - ); + sections, + insertBlock: ( blockId ) => { + const item = inserterItems.find( ( i ) => i.id === blockId ); if ( ! item ) { debug( - `Block "${ blockName }" not found in inserter items` + `Block with ID "${ blockId }" not found in inserter items` ); return false; } @@ -114,7 +116,7 @@ export default function NativeBlockInserterButton() { return () => { delete window.blockInserter; }; - }, [ blocks, destinationBlockName, inserterItems, onSelectItem ] ); + }, [ sections, inserterItems, categories, onSelectItem ] ); return (