diff --git a/.maestro/enrichedMarkdownText/flows/basic/block_elements/ordered_list_start_test.yaml b/.maestro/enrichedMarkdownText/flows/basic/block_elements/ordered_list_start_test.yaml new file mode 100644 index 000000000..f56fd9bb6 --- /dev/null +++ b/.maestro/enrichedMarkdownText/flows/basic/block_elements/ordered_list_start_test.yaml @@ -0,0 +1,23 @@ +appId: swmansion.enriched.markdown.example +tags: + - block + - list + - text +--- +- launchApp + +- runFlow: + file: '../../../../subflows/move_to_playground.yaml' + +- runFlow: + file: '../../../subflows/set_enriched_text_value.yaml' + env: + VALUE: | + 3. Add eggs and vanilla extract + 4. Pour into a greased tin + 5. Bake for 30 minutes + +- runFlow: + file: '../../../subflows/capture_or_assert_screenshot.yaml' + env: + SCREENSHOT_NAME: 'ordered_list_start_display' diff --git a/.maestro/enrichedMarkdownText/screenshots/android/ordered_list_start_display.png b/.maestro/enrichedMarkdownText/screenshots/android/ordered_list_start_display.png new file mode 100644 index 000000000..78cb0e42d Binary files /dev/null and b/.maestro/enrichedMarkdownText/screenshots/android/ordered_list_start_display.png differ diff --git a/.maestro/enrichedMarkdownText/screenshots/ios/ordered_list_start_display.png b/.maestro/enrichedMarkdownText/screenshots/ios/ordered_list_start_display.png new file mode 100644 index 000000000..1955eea14 Binary files /dev/null and b/.maestro/enrichedMarkdownText/screenshots/ios/ordered_list_start_display.png differ diff --git a/packages/core/cpp/parser/MD4CParser.cpp b/packages/core/cpp/parser/MD4CParser.cpp index 075580c80..64c38722e 100644 --- a/packages/core/cpp/parser/MD4CParser.cpp +++ b/packages/core/cpp/parser/MD4CParser.cpp @@ -19,6 +19,7 @@ class MD4CParser::Impl { static const std::string ATTR_LANGUAGE; static const std::string ATTR_IS_TASK; static const std::string ATTR_TASK_CHECKED; + static const std::string ATTR_START; void reset(size_t estimatedDepth) { root = std::make_shared(NodeType::Document); @@ -114,7 +115,14 @@ class MD4CParser::Impl { } case MD_BLOCK_OL: { - impl->pushNode(std::make_shared(NodeType::OrderedList)); + auto node = std::make_shared(NodeType::OrderedList); + if (detail) { + auto *ol = static_cast(detail); + if (ol->start != 1) { + node->setAttribute(ATTR_START, std::to_string(ol->start)); + } + } + impl->pushNode(node); break; } @@ -621,5 +629,6 @@ const std::string MD4CParser::Impl::ATTR_FENCE_CHAR = "fenceChar"; const std::string MD4CParser::Impl::ATTR_LANGUAGE = "language"; const std::string MD4CParser::Impl::ATTR_IS_TASK = "isTask"; const std::string MD4CParser::Impl::ATTR_TASK_CHECKED = "taskChecked"; +const std::string MD4CParser::Impl::ATTR_START = "start"; } // namespace Markdown diff --git a/packages/react-native-enriched-markdown/android/src/main/java/com/swmansion/enriched/markdown/renderer/BlockStyleContext.kt b/packages/react-native-enriched-markdown/android/src/main/java/com/swmansion/enriched/markdown/renderer/BlockStyleContext.kt index a3d707866..68baf8fbc 100644 --- a/packages/react-native-enriched-markdown/android/src/main/java/com/swmansion/enriched/markdown/renderer/BlockStyleContext.kt +++ b/packages/react-native-enriched-markdown/android/src/main/java/com/swmansion/enriched/markdown/renderer/BlockStyleContext.kt @@ -107,8 +107,10 @@ class BlockStyleContext { listItemNumber++ } - fun resetListItemNumber() { - listItemNumber = 0 + fun resetListItemNumber(startNumber: Int = 1) { + // ListItemRenderer pre-increments, so the counter starts one below the + // first rendered number. + listItemNumber = startNumber - 1 } fun pushOrderedListItemNumber() { diff --git a/packages/react-native-enriched-markdown/android/src/main/java/com/swmansion/enriched/markdown/renderer/ListContextManager.kt b/packages/react-native-enriched-markdown/android/src/main/java/com/swmansion/enriched/markdown/renderer/ListContextManager.kt index 567cc79dc..cb3d2aa54 100644 --- a/packages/react-native-enriched-markdown/android/src/main/java/com/swmansion/enriched/markdown/renderer/ListContextManager.kt +++ b/packages/react-native-enriched-markdown/android/src/main/java/com/swmansion/enriched/markdown/renderer/ListContextManager.kt @@ -36,6 +36,7 @@ class ListContextManager( fun enterList( listType: BlockStyleContext.ListType, style: ListStyle, + startNumber: Int = 1, ): ListEntryState { val previousDepth = context.listDepth val isNested = previousDepth > 0 @@ -58,7 +59,7 @@ class ListContextManager( context.setUnorderedListStyle(style) } } - context.resetListItemNumber() + context.resetListItemNumber(startNumber) return ListEntryState( previousDepth = previousDepth, diff --git a/packages/react-native-enriched-markdown/android/src/main/java/com/swmansion/enriched/markdown/renderer/ListRenderer.kt b/packages/react-native-enriched-markdown/android/src/main/java/com/swmansion/enriched/markdown/renderer/ListRenderer.kt index e3e5f4f03..d47887798 100644 --- a/packages/react-native-enriched-markdown/android/src/main/java/com/swmansion/enriched/markdown/renderer/ListRenderer.kt +++ b/packages/react-native-enriched-markdown/android/src/main/java/com/swmansion/enriched/markdown/renderer/ListRenderer.kt @@ -22,8 +22,15 @@ class ListRenderer( val listStyle = config.style.listStyle val listType = if (isOrdered) BlockStyleContext.ListType.ORDERED else BlockStyleContext.ListType.UNORDERED + val startNumber = + if (isOrdered) { + node.getAttribute("start")?.toIntOrNull()?.coerceAtLeast(0) ?: 1 + } else { + 1 + } + val contextManager = ListContextManager(factory.blockStyleContext) - val entryState = contextManager.enterList(listType, listStyle) + val entryState = contextManager.enterList(listType, listStyle, startNumber) // For top-level lists, insert marginTop spacer before rendering content if (entryState.previousDepth == 0) { diff --git a/packages/react-native-enriched-markdown/ios/renderer/ListRenderer.m b/packages/react-native-enriched-markdown/ios/renderer/ListRenderer.m index 237082df4..4848f32eb 100644 --- a/packages/react-native-enriched-markdown/ios/renderer/ListRenderer.m +++ b/packages/react-native-enriched-markdown/ios/renderer/ListRenderer.m @@ -43,7 +43,14 @@ - (void)renderNodeContent:(MarkdownASTNode *)node context.listDepth = prevDepth + 1; context.listType = _isOrdered ? ListTypeOrdered : ListTypeUnordered; - context.listItemNumber = 0; // Reset counter for this specific list level + NSInteger startNumber = 1; + NSString *startAttr = node.attributes[@"start"]; + if (_isOrdered && startAttr != nil) { + startNumber = MAX((NSInteger)0, (NSInteger)startAttr.integerValue); + } + // ListItemRenderer pre-increments, so the counter starts one below the + // first rendered number. + context.listItemNumber = startNumber - 1; [context setBlockStyle:_isOrdered ? BlockTypeOrderedList : BlockTypeUnorderedList font:_config.listStyleFont