Browse Source
- Add TbIotHubMarkdownComponent (home/components/iot-hub) wrapping tb-markdown with applyDefaultMarkdownStyle=false, the IoT Hub item link compile module, and forwarded lineNumbers / fallbackToPlainMarkdown coerced-boolean inputs. - Carry the readme/description typography (h1..h6, p, ul/ol, li, a, img, code, code-wrapper / clipboard-btn, pre[language] with Prism tokens and line-numbers, tables) so callers don't have to override the default markdown styles. Tunes ported from the design: list margins, 6px heading bottom padding, table widths and padding, clipboard offsets, etc. - Replace tb-markdown with tb-iot-hub-markdown in the item detail dialog (description + readme + changelog), the install / update dialog error blocks, and the solution install dialog. Drop the inline .dlg-readme markdown overrides and the .dlg-description ::ng-deep block, leaving only the dlg-readme layout tweak and a padding:0 reset for .dlg-description. - Solution install dialog drops the now-redundant itemLinkCompileModules field + IotHubItemLinkModule import and the .table-wrapper ::ng-deep override; the iot-hub-markdown component carries those styles itself.pull/15615/head
12 changed files with 512 additions and 195 deletions
@ -0,0 +1,23 @@ |
|||||
|
<!-- |
||||
|
|
||||
|
Copyright © 2016-2026 The Thingsboard Authors |
||||
|
|
||||
|
Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
you may not use this file except in compliance with the License. |
||||
|
You may obtain a copy of the License at |
||||
|
|
||||
|
http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
|
||||
|
Unless required by applicable law or agreed to in writing, software |
||||
|
distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
See the License for the specific language governing permissions and |
||||
|
limitations under the License. |
||||
|
|
||||
|
--> |
||||
|
<tb-markdown [data]="data" |
||||
|
[applyDefaultMarkdownStyle]="false" |
||||
|
[additionalCompileModules]="itemLinkCompileModules" |
||||
|
[lineNumbers]="lineNumbers" |
||||
|
[fallbackToPlainMarkdown]="fallbackToPlainMarkdown"> |
||||
|
</tb-markdown> |
||||
@ -0,0 +1,378 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2026 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
|
||||
|
// Shared markdown styles for IoT Hub readme/description content. |
||||
|
// Originally in iot-hub-item-detail-dialog.component.scss (.dlg-readme). |
||||
|
:host { |
||||
|
display: block; |
||||
|
font-size: 14px; |
||||
|
line-height: 24px; |
||||
|
letter-spacing: 0.2px; |
||||
|
color: rgba(0, 0, 0, 0.76); |
||||
|
|
||||
|
::ng-deep tb-markdown .tb-markdown-view { |
||||
|
padding: 16px 24px 24px; |
||||
|
&.table-wrapper { |
||||
|
padding: 0; |
||||
|
} |
||||
|
&:not(.table-wrapper) { |
||||
|
> :first-child { |
||||
|
margin-top: 0; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Headings: design 20px SemiBold, line-height 24, tracking 0.1. |
||||
|
h1, h2, h3, h4, h5, h6 { |
||||
|
font-size: 20px; |
||||
|
font-weight: 600; |
||||
|
line-height: 24px; |
||||
|
letter-spacing: 0.1px; |
||||
|
color: rgba(0, 0, 0, 0.76); |
||||
|
margin: 0; |
||||
|
padding: 0; |
||||
|
} |
||||
|
|
||||
|
> h1, > h2, > h3, > h4, > h5, > h6 { |
||||
|
padding: 0 0 6px; |
||||
|
margin-top: 20px; |
||||
|
} |
||||
|
|
||||
|
h1 { |
||||
|
font-size: 24px; |
||||
|
line-height: 32px; |
||||
|
padding-right: 0; |
||||
|
} |
||||
|
|
||||
|
// Paragraphs: design 14px Regular, tracking 0.2, line-height 24. |
||||
|
p { |
||||
|
font-size: 14px; |
||||
|
font-weight: 400; |
||||
|
line-height: 24px; |
||||
|
letter-spacing: 0.2px; |
||||
|
color: rgba(0, 0, 0, 0.76); |
||||
|
margin: 0; |
||||
|
} |
||||
|
|
||||
|
> p, > div { |
||||
|
padding-left: 0; |
||||
|
padding-right: 0; |
||||
|
} |
||||
|
|
||||
|
p + p { |
||||
|
margin-top: 8px; |
||||
|
} |
||||
|
|
||||
|
h1 + p, h2 + p, h3 + p, |
||||
|
h1 + ul, h2 + ul, h3 + ul, |
||||
|
h1 + ol, h2 + ol, h3 + ol { |
||||
|
margin-top: 8px; |
||||
|
} |
||||
|
|
||||
|
// Lists: design ms-21 per li, 14px, line-height 24. |
||||
|
ul, ol { |
||||
|
padding-left: 21px; |
||||
|
padding-right: 0; |
||||
|
margin: 0; |
||||
|
font-size: 14px; |
||||
|
line-height: 24px; |
||||
|
letter-spacing: 0.2px; |
||||
|
color: rgba(0, 0, 0, 0.76); |
||||
|
|
||||
|
+ h1, + h2, + h3, + h4, + h5, + h6 { |
||||
|
padding-top: 0; |
||||
|
margin-top: 20px; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
ul { |
||||
|
margin-top: 8px; |
||||
|
margin-bottom: 8px; |
||||
|
} |
||||
|
|
||||
|
li { |
||||
|
padding-bottom: 4px; |
||||
|
margin-bottom: 0; |
||||
|
line-height: 24px; |
||||
|
} |
||||
|
|
||||
|
// Images — ported from markdown.component.scss. |
||||
|
img { |
||||
|
max-width: 100%; |
||||
|
} |
||||
|
|
||||
|
// Links — ported from markdown.component.scss. |
||||
|
a { |
||||
|
font-weight: 500; |
||||
|
color: #2a7dec; |
||||
|
text-decoration: none; |
||||
|
border: none; |
||||
|
|
||||
|
&:hover { |
||||
|
color: #2a7dec; |
||||
|
text-decoration: underline; |
||||
|
border: none; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Inline code — ported from markdown.component.scss. |
||||
|
code:not([class*=language-]) { |
||||
|
color: #eb5757; |
||||
|
font-family: monospace; |
||||
|
font-size: 14px; |
||||
|
} |
||||
|
|
||||
|
// Code-wrapper + clipboard button — ported from markdown.component.scss |
||||
|
// so the inline copy affordance and hover state work without |
||||
|
// depending on the default markdown styles. |
||||
|
div.code-wrapper { |
||||
|
position: relative; |
||||
|
|
||||
|
button.clipboard-btn { |
||||
|
pointer-events: none; |
||||
|
outline: none; |
||||
|
position: absolute; |
||||
|
width: 206px; |
||||
|
height: 32px; |
||||
|
top: 0; |
||||
|
right: 0; |
||||
|
background: 0 0; |
||||
|
border: none; |
||||
|
user-select: none; |
||||
|
|
||||
|
&.multiline { |
||||
|
right: 6px; |
||||
|
} |
||||
|
|
||||
|
p { |
||||
|
padding: 6px 8px 0; |
||||
|
top: 1px; |
||||
|
transition: .2s; |
||||
|
color: #2a7dec; |
||||
|
background: rgba(255, 255, 255, .85); |
||||
|
backdrop-filter: blur(4px); |
||||
|
opacity: 0; |
||||
|
font-weight: 500; |
||||
|
right: 32px; |
||||
|
position: absolute; |
||||
|
} |
||||
|
|
||||
|
div { |
||||
|
background-color: #fff; |
||||
|
position: absolute; |
||||
|
width: 38px; |
||||
|
height: 28px; |
||||
|
top: 3px; |
||||
|
right: 3px; |
||||
|
padding: 8px 10px 0; |
||||
|
|
||||
|
img { |
||||
|
position: initial; |
||||
|
width: 18px; |
||||
|
height: 18px; |
||||
|
filter: invert(51%) sepia(6%) saturate(172%) hue-rotate(177deg) brightness(94%) contrast(92%); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
&:hover { |
||||
|
cursor: pointer; |
||||
|
|
||||
|
pre[class*="language-"] { |
||||
|
border: solid 1px #2a7dec; |
||||
|
} |
||||
|
|
||||
|
button.clipboard-btn { |
||||
|
p { |
||||
|
opacity: 1; |
||||
|
} |
||||
|
|
||||
|
div img { |
||||
|
filter: invert(49%) sepia(97%) saturate(3730%) hue-rotate(200deg) brightness(95%) contrast(95%); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
th, td { |
||||
|
div.code-wrapper { |
||||
|
display: inline-block; |
||||
|
width: 100%; |
||||
|
|
||||
|
button.clipboard-btn { |
||||
|
top: -6px; |
||||
|
padding: 0 3px; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Code blocks (Prism) — ported from markdown.component.scss. |
||||
|
pre[class*="language-"] { |
||||
|
font-size: 14px; |
||||
|
border: 1px solid rgba(42, 125, 236, .2); |
||||
|
border-radius: 4px; |
||||
|
background: 0 0; |
||||
|
padding: 8px 16px; |
||||
|
color: #212529; |
||||
|
|
||||
|
.token.atrule, .token.attr-value, .token.keyword { |
||||
|
color: #2a7dec; |
||||
|
} |
||||
|
|
||||
|
.token.selector, .token.attr-name, .token.string, .token.char, .token.builtin, .token.inserted { |
||||
|
color: #eb5757; |
||||
|
} |
||||
|
|
||||
|
.token.punctuation { |
||||
|
color: #212529; |
||||
|
} |
||||
|
|
||||
|
&.line-numbers { |
||||
|
padding-left: 66px; |
||||
|
|
||||
|
> code span.line-numbers-rows { |
||||
|
top: -11px; |
||||
|
bottom: -11px; |
||||
|
left: -66px; |
||||
|
width: 50px; |
||||
|
border: none; |
||||
|
padding: 8px 12px 8px 18px; |
||||
|
text-align: right; |
||||
|
background: #f9fbff; |
||||
|
|
||||
|
> span:before { |
||||
|
color: rgba(33, 37, 41, .6); |
||||
|
padding-right: 0; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
&.no-line-numbers { |
||||
|
padding-left: 16px; |
||||
|
|
||||
|
> code span.line-numbers-rows { |
||||
|
display: none; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Tables — ported from markdown.component.scss since |
||||
|
// applyDefaultMarkdownStyle is false on tb-markdown. |
||||
|
> table { |
||||
|
width: 100%; |
||||
|
border: 1px solid rgba(42, 125, 236, .2); |
||||
|
border-radius: 4px; |
||||
|
border-collapse: unset; |
||||
|
border-spacing: 0; |
||||
|
margin-top: 20px; |
||||
|
margin-bottom: 20px; |
||||
|
overflow: hidden; |
||||
|
table-layout: fixed; |
||||
|
|
||||
|
&.auto { |
||||
|
table-layout: auto; |
||||
|
} |
||||
|
|
||||
|
> thead { |
||||
|
background-color: #f9fbff; |
||||
|
color: rgba(33, 37, 41, .6); |
||||
|
|
||||
|
> tr > th { |
||||
|
border-bottom: 1px solid rgba(42, 125, 236, .2); |
||||
|
font-size: 14px; |
||||
|
padding: 8px 12px; |
||||
|
text-align: left; |
||||
|
margin: 0; |
||||
|
@media screen and (max-width: 400px) { |
||||
|
font-size: 12px; |
||||
|
padding: 8px 4px; |
||||
|
code:not([class*=language-]) { |
||||
|
font-size: 12px; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
> tbody { |
||||
|
> tr:not(:last-child) > td { |
||||
|
border-bottom: 1px solid rgba(42, 125, 236, .2); |
||||
|
} |
||||
|
|
||||
|
> tr > td { |
||||
|
font-size: 14px; |
||||
|
padding: 8px 12px; |
||||
|
text-align: left; |
||||
|
margin: 0; |
||||
|
color: rgba(15, 22, 29, 0.8); |
||||
|
@media screen and (max-width: 400px) { |
||||
|
font-size: 12px; |
||||
|
padding: 8px 4px; |
||||
|
code:not([class*=language-]) { |
||||
|
font-size: 12px; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
th, td { |
||||
|
font-size: 14px; |
||||
|
padding: 8px; |
||||
|
margin: 0; |
||||
|
text-align: left; |
||||
|
} |
||||
|
|
||||
|
td[align=center], th[align=center] { |
||||
|
text-align: center; |
||||
|
} |
||||
|
|
||||
|
td[align=right], th[align=right] { |
||||
|
text-align: right; |
||||
|
} |
||||
|
|
||||
|
tr td div { |
||||
|
padding-left: 0; |
||||
|
padding-right: 0; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.tb-doc-link-btn { |
||||
|
display: inline-flex; |
||||
|
align-items: center; |
||||
|
gap: 8px; |
||||
|
padding: 8px 16px; |
||||
|
margin: 4px 8px 4px 0; |
||||
|
border: 1px solid rgba(0, 0, 0, 0.12); |
||||
|
border-radius: 6px; |
||||
|
background: #fff; |
||||
|
color: rgba(0, 0, 0, 0.76); |
||||
|
font-size: 14px; |
||||
|
font-weight: 500; |
||||
|
line-height: 20px; |
||||
|
text-decoration: none; |
||||
|
cursor: pointer; |
||||
|
|
||||
|
i.material-icons { |
||||
|
font-size: 18px; |
||||
|
line-height: 18px; |
||||
|
color: rgba(0, 0, 0, 0.54); |
||||
|
} |
||||
|
|
||||
|
&:hover { |
||||
|
background: rgba(0, 0, 0, 0.04); |
||||
|
border-color: rgba(0, 0, 0, 0.24); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,40 @@ |
|||||
|
///
|
||||
|
/// Copyright © 2016-2026 The Thingsboard Authors
|
||||
|
///
|
||||
|
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
|
/// you may not use this file except in compliance with the License.
|
||||
|
/// You may obtain a copy of the License at
|
||||
|
///
|
||||
|
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
///
|
||||
|
/// Unless required by applicable law or agreed to in writing, software
|
||||
|
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
/// See the License for the specific language governing permissions and
|
||||
|
/// limitations under the License.
|
||||
|
///
|
||||
|
|
||||
|
import { Component, Input, Type } from '@angular/core'; |
||||
|
import { coerceBoolean } from '@shared/decorators/coercion'; |
||||
|
import { IotHubItemLinkModule } from './iot-hub-item-link-card/iot-hub-item-link.module'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'tb-iot-hub-markdown', |
||||
|
standalone: false, |
||||
|
templateUrl: './iot-hub-markdown.component.html', |
||||
|
styleUrls: ['./iot-hub-markdown.component.scss'] |
||||
|
}) |
||||
|
export class TbIotHubMarkdownComponent { |
||||
|
|
||||
|
@Input() data: string | undefined; |
||||
|
|
||||
|
@Input() |
||||
|
@coerceBoolean() |
||||
|
lineNumbers = false; |
||||
|
|
||||
|
@Input() |
||||
|
@coerceBoolean() |
||||
|
fallbackToPlainMarkdown = false; |
||||
|
|
||||
|
readonly itemLinkCompileModules: Type<any>[] = [IotHubItemLinkModule]; |
||||
|
} |
||||
Loading…
Reference in new issue