From 300e0183a317315a7246d5ba4882c9f096cebcc7 Mon Sep 17 00:00:00 2001 From: brandonkelly Date: Thu, 5 Sep 2024 10:30:37 -0700 Subject: [PATCH] Markdown UI element --- bootstrap/bootstrap.php | 1 + scripts/copyicons.php | 1 + src/fieldlayoutelements/Markdown.php | 113 ++++++++++++++++++++++++ src/helpers/StringHelper.php | 12 +++ src/icons/brands/markdown.svg | 1 + src/models/FieldLayout.php | 2 + src/translations/en/app.php | 1 + tests/unit/helpers/StringHelperTest.php | 45 ++++++++++ 8 files changed, 176 insertions(+) create mode 100644 src/fieldlayoutelements/Markdown.php create mode 100644 src/icons/brands/markdown.svg diff --git a/bootstrap/bootstrap.php b/bootstrap/bootstrap.php index acaebc3bf11..6bbe169c789 100644 --- a/bootstrap/bootstrap.php +++ b/bootstrap/bootstrap.php @@ -224,6 +224,7 @@ // Other Craft::setAlias('@appicons/github.svg', "$brandIconsPath/github.svg"); +Craft::setAlias('@appicons/markdown.svg', "$brandIconsPath/markdown.svg"); Craft::setAlias('@appicons/globe.svg', "$regularIconsPath/globe.svg"); // Renamed icon aliases diff --git a/scripts/copyicons.php b/scripts/copyicons.php index 0f1c407a4b9..b5d23bf12f6 100644 --- a/scripts/copyicons.php +++ b/scripts/copyicons.php @@ -7,6 +7,7 @@ $icons = [ 'brands/github.svg', + 'brands/markdown.svg', 'custom-icons/asterisk-slash.svg', 'custom-icons/diamond-slash.svg', 'custom-icons/element-card-slash.svg', diff --git a/src/fieldlayoutelements/Markdown.php b/src/fieldlayoutelements/Markdown.php new file mode 100644 index 00000000000..4148230fc6b --- /dev/null +++ b/src/fieldlayoutelements/Markdown.php @@ -0,0 +1,113 @@ + + * @since 5.5.0 + */ +class Markdown extends BaseUiElement +{ + /** + * @var string The Markdown content + */ + public string $content = ''; + + /** + * @var bool Whether the content should be displayed in a pane. + */ + public bool $displayInPane = true; + + /** + * @inheritdoc + */ + protected function selectorLabel(): string + { + return StringHelper::firstLine($this->content) ?: 'Markdown'; + } + + /** + * @inheritdoc + */ + protected function selectorIcon(): ?string + { + return 'markdown'; + } + + /** + * @inheritdoc + */ + protected function selectorLabelAttributes(): array + { + $attr = parent::selectorLabelAttributes(); + if ($this->content) { + $attr['class'][] = 'code'; + } + return $attr; + } + + /** + * @inheritdoc + */ + public function hasCustomWidth(): bool + { + return true; + } + + /** + * @inheritdoc + */ + public function hasSettings() + { + return true; + } + + /** + * @inheritdoc + */ + protected function settingsHtml(): ?string + { + return + Cp::textareaFieldHtml([ + 'label' => Craft::t('app', 'Content'), + 'class' => ['code', 'nicetext'], + 'id' => 'content', + 'name' => 'content', + 'value' => $this->content, + ]) . + Cp::lightswitchFieldHtml([ + 'label' => Craft::t('app', 'Display content in a pane'), + 'id' => 'display-in-pane', + 'name' => 'displayInPane', + 'on' => $this->displayInPane, + ]); + } + + /** + * @inheritdoc + */ + public function formHtml(?ElementInterface $element = null, bool $static = false): ?string + { + $content = MarkdownHelper::process(Html::encode($this->content)); + if ($this->displayInPane) { + $content = Html::tag('div', $content, [ + 'class' => 'pane', + ]); + } + return Html::tag('div', $content, $this->containerAttributes($element, $static)); + } +} diff --git a/src/helpers/StringHelper.php b/src/helpers/StringHelper.php index 397ec54168f..e7469082450 100644 --- a/src/helpers/StringHelper.php +++ b/src/helpers/StringHelper.php @@ -918,6 +918,18 @@ public static function lines(string $str): array return array_map(fn(BaseStringy $line) => (string)$line, $lines); } + /** + * Returns the first line of a string. + * + * @param string $str + * @return string + * @since 5.5.0 + */ + public static function firstLine(string $str): string + { + return (string)BaseStringy::create($str)->lines()[0]; + } + /** * Converts the first character of the supplied string to lower case. * diff --git a/src/icons/brands/markdown.svg b/src/icons/brands/markdown.svg new file mode 100644 index 00000000000..f1166de13a6 --- /dev/null +++ b/src/icons/brands/markdown.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/models/FieldLayout.php b/src/models/FieldLayout.php index 8f6e9701ea7..871da240459 100644 --- a/src/models/FieldLayout.php +++ b/src/models/FieldLayout.php @@ -22,6 +22,7 @@ use craft\fieldlayoutelements\Heading; use craft\fieldlayoutelements\HorizontalRule; use craft\fieldlayoutelements\LineBreak; +use craft\fieldlayoutelements\Markdown; use craft\fieldlayoutelements\Template; use craft\fieldlayoutelements\Tip; use craft\helpers\ArrayHelper; @@ -457,6 +458,7 @@ public function getAvailableUiElements(): array new Heading(), new Tip(['style' => Tip::STYLE_TIP]), new Tip(['style' => Tip::STYLE_WARNING]), + new Markdown(), new Template(), ]; diff --git a/src/translations/en/app.php b/src/translations/en/app.php index 404676e89f1..7d19593e29c 100644 --- a/src/translations/en/app.php +++ b/src/translations/en/app.php @@ -552,6 +552,7 @@ 'Display Settings' => 'Display Settings', 'Display as cards' => 'Display as cards', 'Display as thumbnails' => 'Display as thumbnails', + 'Display content in a pane' => 'Display content in a pane', 'Display hierarchically' => 'Display hierarchically', 'Display in a structured table' => 'Display in a structured table', 'Display in a table' => 'Display in a table', diff --git a/tests/unit/helpers/StringHelperTest.php b/tests/unit/helpers/StringHelperTest.php index 64de0c12932..ee12ccc89b5 100644 --- a/tests/unit/helpers/StringHelperTest.php +++ b/tests/unit/helpers/StringHelperTest.php @@ -738,6 +738,16 @@ public function testLines(int $expected, string $string): void self::assertCount($expected, $actual); } + /** + * @dataProvider firstLineDataProvider + * @param string $expected + * @param string $string + */ + public function testFirstLine(string $expected, string $string): void + { + self::assertEquals($expected, StringHelper::firstLine($string)); + } + /** * */ @@ -2173,6 +2183,41 @@ public static function linesDataProvider(): array + ', + ], + ]; + } + + /** + * @return array + */ + public static function firstLineDataProvider(): array + { + return [ + [ + 'test', + 'test + + + test', + ], + ['test
test', 'test
test'], + ['thesearetabs notspaces', 'thesearetabs notspaces'], + [ + '😂', '😂 + 😁', + ], + [ + '', ' + + + + + + + + + ', ], ];