diff --git a/BlazorBootstrap.Demo.RCL/Components/Pages/Grid/11-translations/Grid_Demo_01_Translations.razor b/BlazorBootstrap.Demo.RCL/Components/Pages/Grid/11-translations/Grid_Demo_01_Translations.razor index 1af66a71d..e55abdadb 100644 --- a/BlazorBootstrap.Demo.RCL/Components/Pages/Grid/11-translations/Grid_Demo_01_Translations.razor +++ b/BlazorBootstrap.Demo.RCL/Components/Pages/Grid/11-translations/Grid_Demo_01_Translations.razor @@ -49,6 +49,7 @@ filtersTranslation.Add(new(">=", "Groter dan of gelijk aan", FilterOperator.GreaterThanOrEquals)); // string filtersTranslation.Add(new("*a*", "Bevat", FilterOperator.Contains)); + filtersTranslation.Add(new("!*a*", "Bevat niet", FilterOperator.DoesNotContain)); filtersTranslation.Add(new("a**", "Begint met", FilterOperator.StartsWith)); filtersTranslation.Add(new("**a", "Eindigt met", FilterOperator.EndsWith)); filtersTranslation.Add(new("=", "gelijk aan", FilterOperator.Equals)); diff --git a/BlazorBootstrap.Demo.RCL/Components/Pages/Index.razor b/BlazorBootstrap.Demo.RCL/Components/Pages/Index.razor index e63d9fc44..6f3c55ce5 100644 --- a/BlazorBootstrap.Demo.RCL/Components/Pages/Index.razor +++ b/BlazorBootstrap.Demo.RCL/Components/Pages/Index.razor @@ -132,7 +132,7 @@
#
for a heading. Add more #
characters for subheadings, up to six levels.
+>
before it. Use more >
characters to nest quotes. For blocks of text, use >
at the start of each line.
+---
.
+*text*
or _text_
**text**
~~text~~
|
to separate cells.|
with \|
if used within a cell.<br />
for new lines within a cell.-
."); + listStack.Push("blockquote"); + indentStack.Push(indentLevel); + } + else + { + while (listStack.Count > 0 && indentStack.Peek() > indentLevel) + { + htmlLines.Add($"{listStack.Pop()}>"); + indentStack.Pop(); + } + + if (listStack.Count == 0 || listStack.Peek() != "blockquote") + { + htmlLines.Add($""); + listStack.Push("blockquote"); + indentStack.Push(indentLevel); + } + } + + htmlLines.Add($"{Regex.Replace(trimmerLine, PATTERN_BLOCKQUOTES, "$1")}"); + listStack.Push("p"); + } + else if (indentStack.Peek() >= indentLevel) + { + htmlLines.Add($"
{Regex.Replace(trimmerLine, PATTERN_BLOCKQUOTES, "$1")}"); + } + } + else + { + // Close any open lists + while (listStack.Count > 0) + { + htmlLines.Add($"{listStack.Pop()}>"); - if (lines[lines.Count - 1] == "") - lines.RemoveAt(lines.Count - 1); + if (indentStack.Count > 0) + indentStack.Pop(); + } + + htmlLines.Add(line); + htmlLines.Add("\n"); + } } - var markup = ApplyRules(lines); - html = ApplyFullMarkupRules(markup); + // Close any remaining open blockquotes + while (listStack.Count > 0) + { + htmlLines.Add($"{listStack.Pop()}>"); + + if (indentStack.Count > 0) + indentStack.Pop(); + } + + return string.Join("", htmlLines); } - private string ApplyRules(Listlines) + // HorizontalRules + private string ConvertMarkdownHorizontalRulesToHtml(string markup) { - var patterns = GetRules(); - foreach (var pattern in patterns) + var lines = markup.Split("\n"); + var parsedLines = new List (); + + foreach (var line in lines) { - for (var i = 0; i < lines.Count; i++) - lines[i] = Regex.Replace(lines[i], pattern.Rule, pattern.Template); + if (string.IsNullOrWhiteSpace(line)) + { + parsedLines.Add(line); + parsedLines.Add("\n"); + continue; + } + + if (Regex.IsMatch(line.Trim(), PATTERN_HORIZONTAL_RULES)) + { + RemoveLastLineBreak(parsedLines); + parsedLines.Add(Regex.Replace(line.Trim(), PATTERN_HORIZONTAL_RULES, "
")); + } + else + { + parsedLines.Add(line); + parsedLines.Add("\n"); + } } - return string.Join("\n", lines); + RemoveLastLineBreak(parsedLines); + + return string.Join("", parsedLines); } - private string ApplyFullMarkupRules(string markup) + // Emphasis + private string ConvertMarkdownEmphasisToHtml(string markup) { - var patterns = GetFullMarkupRules(); + var lines = markup.Split("\n"); + var parsedLines = new List(); - foreach (var pattern in patterns) - markup = Regex.Replace(markup, pattern.Rule, pattern.Template); + for (var i = 0; i < lines.Count(); i++) + { + if (string.IsNullOrWhiteSpace(lines[i])) + { + parsedLines.Add(lines[i]); + continue; + } - return markup; + if (Regex.IsMatch(lines[i].Trim(), @"\*\*(.*?)\*\*") + || Regex.IsMatch(lines[i].Trim().Trim(), @"__(.*?)__") + || Regex.IsMatch(lines[i].Trim(), @"\*(.*?)\*") + || Regex.IsMatch(lines[i].Trim(), @"_(.*?)_") + || Regex.IsMatch(lines[i].Trim(), @"~~(.*?)~~")) + { + lines[i] = Regex.Replace(lines[i].Trim(), @"\*\*(.*?)\*\*", "$1"); + lines[i] = Regex.Replace(lines[i].Trim(), @"__(.*?)__", "$1"); + lines[i] = Regex.Replace(lines[i].Trim(), @"\*(.*?)\*", "$1"); + lines[i] = Regex.Replace(lines[i].Trim(), @"_(.*?)_", "$1"); + lines[i] = Regex.Replace(lines[i].Trim(), @"~~(.*?)~~", " $1"); + } + + parsedLines.Add(lines[i]); + } + + return string.Join("\n", parsedLines); } - ListGetLines() + // Code Highlighting + private string ConvertMarkdownCodeHighlightingToHtml(string markup) { - var inputs = new List (); + var lines = markup.Split("\n"); + var parsedLines = new List (); + var isCodeBlockInprogress = false; - if (ChildContent is not null) + for (var i = 0; i < lines.Count(); i++) { - var builder = new RenderTreeBuilder(); - ChildContent.Invoke(builder); + if (Regex.IsMatch(lines[i].Trim(), @"\```(\w+)")) + { + if (!isCodeBlockInprogress) + isCodeBlockInprogress = true; - var frames = builder.GetFrames().Array; - foreach (var frame in frames) + lines[i] = Regex.Replace(lines[i].Trim(), @"\```(\w+)", " "); + parsedLines.Add(lines[i]); + } + else if (isCodeBlockInprogress) + { + parsedLines.Add(lines[i]); + parsedLines.Add($"{CODE_HIGHLIGHTING_LINE_SEPERATOR}"); + } + else + { + parsedLines.Add(lines[i]); + parsedLines.Add("\n"); + } + } + + RemoveLastLineBreak(parsedLines); + + return string.Join("", parsedLines); + } + + // Lists + private string ConvertMarkdownListToHtml(string markup) + { + var lines = markup.Split("\n"); + var htmlLines = new List"); + parsedLines.Add(lines[i]); + } + else if (Regex.IsMatch(lines[i].Trim().Trim(), @"```")) { - if (frame.MarkupContent is not null) + if (isCodeBlockInprogress) + isCodeBlockInprogress = false; + + lines[i] = Regex.Replace(lines[i].Trim(), @"```", "
(); + var listStack = new Stack (); + var indentStack = new Stack (); + + foreach (var line in lines) + { + var indentLevel = line.TakeWhile(char.IsWhiteSpace).Count(); + + if (Regex.IsMatch(line, PATTERN_ORDERED_LIST)) + { + // Ordered list + if (listStack.Count == 0 || listStack.Peek() != "ol" || indentStack.Peek() < indentLevel) + { + if (listStack.Count > 0 && indentStack.Peek() < indentLevel) + { + htmlLines.Add(" "); + listStack.Push("ol"); + indentStack.Push(indentLevel); + } + else + { + while (listStack.Count > 0 && indentStack.Peek() > indentLevel) + { + htmlLines.Add($"{listStack.Pop()}>"); + indentStack.Pop(); + } + + if (listStack.Count == 0 || listStack.Peek() != "ol") + { + htmlLines.Add("
"); + listStack.Push("ol"); + indentStack.Push(indentLevel); + } + } + htmlLines.Add($"
- {Regex.Replace(line, PATTERN_ORDERED_LIST, "")}"); + } + else if (indentStack.Peek() > indentLevel) + { + htmlLines.Add($"{listStack.Pop()}>"); + indentStack.Pop(); + + htmlLines.Add($"
- {Regex.Replace(line, PATTERN_ORDERED_LIST, "")}"); + } + else if (indentStack.Peek() == indentLevel) + { + htmlLines.Add($"
- {Regex.Replace(line, PATTERN_ORDERED_LIST, "")}"); + } + } + else if (Regex.IsMatch(line, PATTERN_UNORDERED_LIST)) + { + // Unordered list + if (listStack.Count == 0 || listStack.Peek() != "ul" || indentStack.Peek() < indentLevel) + { + if (listStack.Count > 0 && indentStack.Peek() < indentLevel) + { + htmlLines.Add("
"); + listStack.Push("ul"); + indentStack.Push(indentLevel); + } + else + { + while (listStack.Count > 0 && indentStack.Peek() > indentLevel) + { + htmlLines.Add($"{listStack.Pop()}>"); + indentStack.Pop(); + } + + if (listStack.Count == 0 || listStack.Peek() != "ul") + { + htmlLines.Add("
"); + listStack.Push("ul"); + indentStack.Push(indentLevel); + } + } + htmlLines.Add($"
- {Regex.Replace(line, PATTERN_UNORDERED_LIST, "")}"); + } + else if (indentStack.Peek() > indentLevel) + { + htmlLines.Add($"{listStack.Pop()}>"); + indentStack.Pop(); + + htmlLines.Add($"
- {Regex.Replace(line, PATTERN_UNORDERED_LIST, "")}"); + } + else if (indentStack.Peek() == indentLevel) { - var lines = frame.MarkupContent.Split("\r\n"); - foreach (var line in lines) - inputs.Add(line.Trim()); + htmlLines.Add($"
- {Regex.Replace(line, PATTERN_UNORDERED_LIST, "")}"); } } + else + { + // Close any open lists + while (listStack.Count > 0) + { + htmlLines.Add($"{listStack.Pop()}>"); + indentStack.Pop(); + } + + htmlLines.Add(line); + htmlLines.Add("\n"); + } } - return inputs; + // Close any remaining open lists + while (listStack.Count > 0) + { + htmlLines.Add($"{listStack.Pop()}>"); + indentStack.Pop(); + } + + // Close any open list items + for (int i = 0; i < htmlLines.Count; i++) + { + if (htmlLines[i].StartsWith("
- ") && (i == htmlLines.Count - 1 || htmlLines[i + 1].StartsWith("
- ") || htmlLines[i + 1].StartsWith(""))) + { + htmlLines[i] += "
"; + } + } + + RemoveLastLineBreak(htmlLines); + + return string.Join("", htmlLines); + } + + // Tables + private string ConvertMarkdownTableToHtml(string markup) + { + var lines = markup.Split("\n"); + var parsedLines = new List(); + var htmlLines = new List (); + + var isTableStart = false; + var isTableHeadingAdded = false; + + // Read lines starting with '|' + foreach (var line in lines) + { + if (string.IsNullOrWhiteSpace(line)) + { + parsedLines.Add(line); + continue; + } + + // Trim row with spaces + var trimmedLine = line.Trim(); + if (trimmedLine.StartsWith("| ")) + { + if (!isTableStart) + isTableStart = true; + + // Remove '|' from the start and end of the line + trimmedLine = trimmedLine.TrimStart('|').TrimEnd('|'); + + // Convert markdown syntax to HTML + var cells = trimmedLine.Split("|", StringSplitOptions.RemoveEmptyEntries); + var tableRow = " "; + + foreach (var cell in cells) + { + var tableCellTagName = !isTableHeadingAdded ? "th" : "td"; + var tableCell = $"<{tableCellTagName}>{cell.Trim()}{tableCellTagName}>"; + tableRow += tableCell; + } + + tableRow += " "; + htmlLines.Add(tableRow); + } + else if (trimmedLine.StartsWith("|--") || trimmedLine.StartsWith("|:--")) + { + // Table heading row is over + if (!isTableHeadingAdded) + { + isTableHeadingAdded = true; + htmlLines.Add(""); + htmlLines.Add(""); + } + } + else + { + if (isTableStart) + { + isTableStart = false; + parsedLines.Add($"{string.Join("", htmlLines)}
"); + htmlLines.Clear(); + } + else + { + parsedLines.Add(line); + } + } + } + + if (isTableStart && htmlLines.Any()) + { + isTableStart = false; + parsedLines.Add($"{string.Join("", htmlLines)}
"); + htmlLines.Clear(); + } + + return string.Join("\n", parsedLines); } - private ListGetRules() + // Paragraphs + private string ConvertMarkdownParagraphsToHtml(string markup) { - return new List + var lines = markup.Split("\n\n\n"); + var parsedLines = new List (); + + if (lines.Length == 1) + return markup; + + foreach (var line in lines) { - // Headers - new(@"^#{6}\s?([^\n]+)", " $1
"), - new(@"^#{5}\s?([^\n]+)", "$1
"), - new(@"^#{4}\s?([^\n]+)", "$1
"), - new(@"^#{3}\s?([^\n]+)", "$1
"), - new(@"^#{2}\s?([^\n]+)", "$1
"), - new(@"^#{1}\s?([^\n]+)", "$1
"), + if (string.IsNullOrWhiteSpace(line)) + { + parsedLines.Add(line); + continue; + } + + parsedLines.Add($"{line}
"); + } - // Blockquotes - new(@"^>{1}\s(.*)$", "$1"), - //new(@"^(>>)+ (.*)$", ""), + return string.Join("", parsedLines); + } - // Horizontal rules - new(@"^\-{3,}$", "$2
"), + // Line breaks + private string ConvertMarkdownLineBreaksToHtml(string markup) => markup.Replace("\n", "
"); - // Emphasis (bold, italics, strikethrough) - new(@"\*\*(.*?)\*\*", "$1"), - new(@"__(.*?)__", "$1"), - new(@"\*(.*?)\*", "$1"), - new(@"_(.*?)_", "$1"), - new(@"~~(.*?)~~", "$1"), + // Image + private string ConvertMarkdownImageToHtml(string markup) + { + // Pattern to match Markdown image syntax: ![alt text](url "optional title" =WIDTHxHEIGHT) + var pattern = @"!\[(.*?)\]\((.*?)(\s*""[^""]*"")?(\s*=\s*(\d*)x?(\d*))?\)"; - // Code highlighting - new(@"\```(\w+)", ""), + // Replace Markdown image syntax with HTML tag + var html = Regex.Replace(markup, pattern, match => + { + var altText = match.Groups[1].Value; + var url = match.Groups[2].Value; + var title = match.Groups[3].Value; + var width = match.Groups[5].Value; + var height = match.Groups[6].Value; - // Tables + var imgTag = $""; - // Anchor links + return imgTag; + }); - // Images + return html; + } - // Checklist or task list + // Links + private string ConvertMarkdownLinksToHtml(string markup) + { + // Pattern to match Markdown link syntax: [Link Text](Link URL) + var pattern = @"\[(.*?)\]\((.*?)\)"; - // Emoji + // Replace Markdown link syntax with HTML tag + var html = Regex.Replace(markup, pattern, match => + { + var linkText = match.Groups[1].Value; + var linkUrl = match.Groups[2].Value; - // Mathematical notation and characters + return $"{linkText}"; + }); - // Mermaid diagrams - }; + return html; } - private List"), - new(@"```", "
GetFullMarkupRules() + // Inline code + private string ConvertMarkdownInlineCodeToHtml(string markup) { - return new List + // Pattern to match inline Markdown code syntax: `code` + var pattern = @"`([^`]+)`"; + + // Replace inline Markdown code syntax with HTML tag + var html = Regex.Replace(markup, pattern, match => { - // Paragraphs and line breaks - new(@"(?"), - new(@"([^\n\n]+\n?)", "
$1
"), - }; + var codeText = match.Groups[1].Value; + + return $"{codeText}
"; + }); + + return html; + } + + //private string ConvertMarkdownChecklistToHtml(string markup) + //{ + // // Pattern to match Markdown checklist syntax: - [ ] or - [x] or 1. [ ] or 1. [x] + // var pattern = @"^(\s*[-\d]+\.\s*\[([ xX])\])\s*(.*)"; + + // // Replace Markdown checklist syntax with HTML tag + // var html = Regex.Replace(markup, pattern, match => + // { + // var checkbox = match.Groups[2].Value.Trim().ToLower() == "x" ? "checked" : ""; + // var content = match.Groups[3].Value; + + // return $"- {content}
"; + // }, RegexOptions.Multiline); + + // // Wrap the checkboxes in aor
tag + // html = Regex.Replace(html, @"((
- .*?
\s*)+)", "$1
", RegexOptions.Singleline); + + // return html; + //} + + + // Emoji + + // Mathematical notation and characters + + // Mermaid diagrams + + private static void RemoveLastLineBreak(ListhtmlLines) + { + // remove last line break + if (htmlLines.Any() && htmlLines[^1] == "\n") + htmlLines.RemoveAt(htmlLines.Count - 1); } } diff --git a/docs/docs/05-components/markdown.mdx b/docs/docs/05-components/markdown.mdx new file mode 100644 index 000000000..dc7d426bc --- /dev/null +++ b/docs/docs/05-components/markdown.mdx @@ -0,0 +1,308 @@ +--- +title: Blazor Markdown Component **Preview** +description: Use Blazor Bootstrap Markdown component to add formatting, tables, images, and more to your project pages. +image: https://i.imgur.com/FhN1caj.png + +sidebar_label: Markdown +sidebar_position: 14 +--- + +import CarbonAd from '/carbon-ad.mdx' + +# Blazor Markdown Preview
+ +Use Blazor Bootstrap Markdown component to add formatting, tables, images, and more to your project pages. + ++ +## Parameters + +| Name | Type | Default | Required | Description | Added Version | +|:--|:--|:--|:--|:--|:--| +| TableCssClass | string? | `table` | | Gets or sets the CSS class for table. | 3.1.0 | +| BlockquotesCssClass | string? | `blockquote` | | Gets or sets the CSS class for blockquotes. | 3.1.0 | + +## Live preview + + + +[See demo here.](https://demos.blazorbootstrap.com/markdown#live-preview) + +## Examples + +### Headers + +Use headers to structure your content. Start a line with `#` for a heading. Add more `#` characters for subheadings, up to six levels. + + + +```cshtml {} showLineNumbers + +# This is a H1 header +## This is a H2 header +### This is a H3 header +#### This is a H4 header +##### This is a H5 header +###### This is a H6 header + +``` + +[See demo here.](https://demos.blazorbootstrap.com/markdown#headers) + +### Paragraphs and line breaks + +Break your text into paragraphs or line breaks for easier reading. + + + +```cshtml {} showLineNumbers ++Add lines between your text with the **Enter** key. +Your text gets better spaced and makes it easier to read. + + +Add lines between your text with the **Enter** key. +Your text gets better spaced and makes it easier to read. + +``` + +[See demo here.](https://demos.blazorbootstrap.com/markdown#paragraphs-and-line-breaks) + +### Blockquotes + +Quote text with `>` before it. Use more `>` characters to nest quotes. For blocks of text, use `>` at the start of each line. + + + +```cshtml {} showLineNumbers ++> A well-known quote, contained in a blockquote element. + + ++> Single line quote +>> Nested quote +>> multiple line +>> quote + +``` + +[See demo here.](https://demos.blazorbootstrap.com/markdown#blockquotes) + +### Horizontal rules + +Add a horizontal rule with a line of `---`. + + + +```cshtml {} showLineNumbers ++above +--- +below + +``` + +[See demo here.](https://demos.blazorbootstrap.com/markdown#horizontal-rules) + +### Emphasis (bold, italics, strikethrough) + +Emphasize text with bold, italics, or strikethrough: +- Italics: `*text*` or `_text_` +- Bold: `**text**` +- Strikethrough: `~~text~~` +- Combine for more emphasis. + + + +```cshtml {} showLineNumbers ++Use _emphasis_ in comments to express **strong** opinions and point out ~~corrections~~ +**_Bold, italicized text_** +**~~Bold, strike-through text~~** + +``` + +[See demo here.](https://demos.blazorbootstrap.com/markdown#emphasis-bold-italics-strikethrough) + +### Code highlighting + + + +[See demo here.](https://demos.blazorbootstrap.com/markdown#code-highlighting) + +### Tables + +Tables help organize structured data. +- Use `|` to separate cells. +- Escape `|` with `\|` if used within a cell. +- Use `
` for new lines within a cell. +- End each row with a carriage return (CR) or line feed (LF). + + + +```cshtml {} showLineNumbers ++| Heading 1 | Heading 2 | Heading 3 | +|--|--|--| +| Cell A1 | Cell A2 | Cell A3 | +| Cell B1 | Cell B2 | Cell B3 +``` + + + +```cshtml {} showLineNumbers +
second line of text | ++| Heading 1 | Heading 2 | Heading 3 | +|--|--|--| +| Cell A1 | Cell A2 | Cell A3 | +| Cell B1 | Cell B2 | Cell B3 | +| Cell C1 | Cell C2 | Cell C3 | +| Cell D1 | Cell D2 | Cell D3 | +| Cell E1 | Cell E2 | Cell E3 | + +``` + +[See demo here.](https://demos.blazorbootstrap.com/markdown#) + +### Lists + +Use lists to organize related items: +- Ordered lists: start with a number followed by a period. +- Unordered lists: start with a `-`. +- Begin each list item on a new line. + + + +[See demo here.](https://demos.blazorbootstrap.com/markdown#lists) + +#### Ordered list + + + +```cshtml {} showLineNumbers ++1. First item +1. Second item +1. Third item + +``` + +[See demo here.](https://demos.blazorbootstrap.com/markdown#ordered-list) + +#### Unordered list + + + +```cshtml {} showLineNumbers ++- Item 1 +- Item 2 +- Item 3 + +``` + +[See demo here.](https://demos.blazorbootstrap.com/markdown#unordered-list) + +#### Nested list + + + +```cshtml {} showLineNumbers ++1. First item + 1. Item 1 + 1. Item 2 + 1. Item 3 +1. Second item + 1. Nested item 1 + 1. Further nested item 1 + 1. Further nested item 2 + 1. Further nested item 3 + 1. Nested item 2 + 1. Nested item 3 +1. Third item + +``` + + + +```cshtml {} showLineNumbers ++- First item + - Item 1 + - Item 2 + - Item 3 +- Second item + - Nested item 1 + - Further nested item 1 + - Further nested item 2 + - Further nested item 3 + - Nested item 2 + - Nested item 3 +- Third item + +``` + + + +```cshtml {} showLineNumbers ++1. First item + - Item 1 + - Item 2 + - Item 3 +1. Second item + - Nested item 1 + - Further nested item 1 + - Further nested item 2 + - Further nested item 3 + - Nested item 2 + - Nested item 3 +1. Third item + +``` + +[See demo here.](https://demos.blazorbootstrap.com/markdown#nested-list) + +### Links + + + +```cshtml {} showLineNumbers ++[BlazorBootstrap - Docs](https://docs.blazorbootstrap.com/) +[BlazorBootstrap - Demos](https://demos.blazorbootstrap.com/) + +``` + +[See demo here.](https://demos.blazorbootstrap.com/markdown#links) + +### Images + + + +```cshtml {} showLineNumbers ++![BlazorBootstrap](https://demos.blazorbootstrap.com/images/logo/logo-color.svg) + +``` + + + +```cshtml {} showLineNumbers ++![BlazorBootstrap](https://demos.blazorbootstrap.com/images/logo/logo-color.svg =200x200) + +``` + + + +```cshtml {} showLineNumbers ++![BlazorBootstrap](https://demos.blazorbootstrap.com/images/logo/logo-color.svg =50) + +``` + +[See demo here.](https://demos.blazorbootstrap.com/markdown#)