tags-input:
* add a "copy" button * fix some css class (adding a prefix to avoid side effects) * other minor fixes
This commit is contained in:
parent
591e0ad3fd
commit
618dc6aeae
3
assets/images/copy.license
Normal file
3
assets/images/copy.license
Normal file
@ -0,0 +1,3 @@
|
||||
SPDX-FileCopyrightText: 2024 John Livingston <https://www.john-livingston.fr/>
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
150
assets/images/copy.svg
Normal file
150
assets/images/copy.svg
Normal file
@ -0,0 +1,150 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 4.2333332 4.2333334"
|
||||
version="1.1"
|
||||
id="svg1428"
|
||||
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
|
||||
sodipodi:docname="copy.svg"
|
||||
inkscape:export-xdpi="9.6000004"
|
||||
inkscape:export-ydpi="9.6000004"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<defs
|
||||
id="defs1422">
|
||||
<linearGradient
|
||||
id="linearGradient5158"
|
||||
inkscape:swatch="solid">
|
||||
<stop
|
||||
style="stop-color:#7d7d7d;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop5156" />
|
||||
</linearGradient>
|
||||
<filter
|
||||
style="color-interpolation-filters:sRGB"
|
||||
inkscape:label="Drop Shadow"
|
||||
id="filter1150">
|
||||
<feFlood
|
||||
flood-opacity="1"
|
||||
flood-color="rgb(0,0,0)"
|
||||
result="flood"
|
||||
id="feFlood1140" />
|
||||
<feComposite
|
||||
in="flood"
|
||||
in2="SourceGraphic"
|
||||
operator="in"
|
||||
result="composite1"
|
||||
id="feComposite1142" />
|
||||
<feGaussianBlur
|
||||
in="composite1"
|
||||
stdDeviation="0.2"
|
||||
result="blur"
|
||||
id="feGaussianBlur1144" />
|
||||
<feOffset
|
||||
dx="0"
|
||||
dy="0"
|
||||
result="offset"
|
||||
id="feOffset1146" />
|
||||
<feComposite
|
||||
in="SourceGraphic"
|
||||
in2="offset"
|
||||
operator="over"
|
||||
result="composite2"
|
||||
id="feComposite1148" />
|
||||
</filter>
|
||||
<filter
|
||||
style="color-interpolation-filters:sRGB"
|
||||
inkscape:label="Drop Shadow"
|
||||
id="filter1174">
|
||||
<feFlood
|
||||
flood-opacity="1"
|
||||
flood-color="rgb(0,0,0)"
|
||||
result="flood"
|
||||
id="feFlood1164" />
|
||||
<feComposite
|
||||
in="flood"
|
||||
in2="SourceGraphic"
|
||||
operator="in"
|
||||
result="composite1"
|
||||
id="feComposite1166" />
|
||||
<feGaussianBlur
|
||||
in="composite1"
|
||||
stdDeviation="0.2"
|
||||
result="blur"
|
||||
id="feGaussianBlur1168" />
|
||||
<feOffset
|
||||
dx="0"
|
||||
dy="0"
|
||||
result="offset"
|
||||
id="feOffset1170" />
|
||||
<feComposite
|
||||
in="SourceGraphic"
|
||||
in2="offset"
|
||||
operator="over"
|
||||
result="composite2"
|
||||
id="feComposite1172" />
|
||||
</filter>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="32"
|
||||
inkscape:cx="5.375"
|
||||
inkscape:cy="9.15625"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="g1910"
|
||||
inkscape:document-rotation="0"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1842"
|
||||
inkscape:window-height="1011"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="1082"
|
||||
inkscape:window-maximized="1"
|
||||
units="px"
|
||||
showguides="true"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1" />
|
||||
<metadata
|
||||
id="metadata1425">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Calque 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<g
|
||||
id="g1910"
|
||||
transform="matrix(0.45207703,0,0,0.45207703,-0.52758049,1.2946618)"
|
||||
style="stroke-width:1.00021;stroke-miterlimit:4;stroke-dasharray:none">
|
||||
<path
|
||||
id="rect1876-6"
|
||||
style="opacity:0.998;fill:none;fill-opacity:1;stroke:#deddda;stroke-width:1.17052;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 4.0844838,4.0458314 3.467999,4.0611713 2.8228277,4.0569294 C 2.3008575,4.0534975 1.8806228,3.6367061 1.8806228,3.1147245 v -4.3981761 c 0,-0.5219815 0.4202234,-0.9422049 0.9422049,-0.9422049 H 7.221004 c 0.5219815,0 0.9422049,0.4202234 0.9422049,0.9422049 l -0.00609,0.33403788 -0.079441,0.96263648"
|
||||
sodipodi:nodetypes="ccsssssscc" />
|
||||
<path
|
||||
id="rect1876-6-3"
|
||||
style="opacity:0.998;fill:none;fill-opacity:1;stroke:#deddda;stroke-width:1.17052;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 8.4339763,5.8496964 c -0.421831,0.00883 -1.3377873,0.00925 -1.7597066,0.011098 -0.7330224,0.00321 -2.1990882,0 -2.1990882,0 -0.5219815,0 -0.9422049,-0.4202233 -0.9422049,-0.9422049 V 0.52041339 c 0,-0.5219815 0.4202234,-0.9422049 0.9422049,-0.9422049 H 8.873358 c 0.5219815,0 0.9422055,0.4202234 0.9422055,0.9422049 0,0 0.00454,1.46607281 0,2.19908821 -0.00259,0.4185566 -0.018291,1.2519554 -0.00588,1.6703359 0.02409,0.811826 -0.382054,1.4390656 -1.375711,1.4598589 z"
|
||||
sodipodi:nodetypes="sssssssssss" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 5.0 KiB |
@ -88,7 +88,8 @@ livechat-help-button {
|
||||
input[type="reset"],
|
||||
button[type="submit"],
|
||||
button[type="reset"],
|
||||
.peertube-plugin-livechat-configuration-actions button {
|
||||
.peertube-plugin-livechat-configuration-actions button,
|
||||
.peertube-plugin-livechat-tags-input-copy {
|
||||
// Peertube rounded-line-height-1-5 mixins
|
||||
line-height: $button-calc-line-height;
|
||||
|
||||
@ -104,7 +105,8 @@ livechat-help-button {
|
||||
|
||||
input[type="submit"],
|
||||
button[type="submit"],
|
||||
.peertube-plugin-livechat-configuration-actions button {
|
||||
.peertube-plugin-livechat-configuration-actions button,
|
||||
.peertube-plugin-livechat-tags-input-copy {
|
||||
// Peertube orange-button mixin
|
||||
&,
|
||||
&:active,
|
||||
@ -331,6 +333,13 @@ livechat-tags-input {
|
||||
flex-wrap: wrap;
|
||||
max-width: 320px;
|
||||
|
||||
.livechat-tags-container {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
// stylelint trying to rearrange unrelated root blocks because of smaller blocks in them
|
||||
/* stylelint-disable-next-line no-descending-specificity */
|
||||
input {
|
||||
@ -347,46 +356,50 @@ livechat-tags-input {
|
||||
}
|
||||
}
|
||||
|
||||
#tags,
|
||||
#tags-searched {
|
||||
.livechat-tags,
|
||||
.livechat-tags-searched {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
padding: 0;
|
||||
margin: var(--tag-padding-vertical) 0;
|
||||
max-height: 150px;
|
||||
overflow-y: auto;
|
||||
border-bottom: 1px dashed var(--greyForegroundColor);
|
||||
transition-property: height, margin;
|
||||
transition-duration: 0.3s;
|
||||
|
||||
&.empty {
|
||||
height: 0;
|
||||
margin: 0;
|
||||
border: none;
|
||||
}
|
||||
|
||||
@supports (scrollbar-width: auto) {
|
||||
scrollbar-color: var(--greyForegroundColor) transparent;
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
}
|
||||
|
||||
#tags-searched {
|
||||
.livechat-tags-container,
|
||||
.livechat-tags-searched {
|
||||
border-bottom: 1px dashed var(--greyForegroundColor);
|
||||
|
||||
&.livechat-empty {
|
||||
height: 0;
|
||||
margin: 0;
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
.livechat-tags-searched {
|
||||
&::after {
|
||||
content: "\1F50D";
|
||||
flex-grow: 1;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
&.empty {
|
||||
&.livechat-empty {
|
||||
&::after {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tag,
|
||||
.tag-searched {
|
||||
.livechat-tag,
|
||||
.livechat-tag-searched {
|
||||
width: auto;
|
||||
height: 24px;
|
||||
display: flex;
|
||||
@ -400,7 +413,7 @@ livechat-tags-input {
|
||||
margin: 0 3px 3px 0;
|
||||
transition: 0.3s filter;
|
||||
|
||||
.tag-close {
|
||||
.livechat-tag-close {
|
||||
display: block;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
@ -424,7 +437,7 @@ livechat-tags-input {
|
||||
color: #fff;
|
||||
background-color: var(--mainColor);
|
||||
|
||||
.tag-close {
|
||||
.livechat-tag-close {
|
||||
color: var(--mainColor);
|
||||
}
|
||||
}
|
||||
@ -433,7 +446,7 @@ livechat-tags-input {
|
||||
color: #fff;
|
||||
background-color: var(--mainHoverColor);
|
||||
|
||||
.tag-close {
|
||||
.livechat-tag-close {
|
||||
color: var(--mainHoverColor);
|
||||
}
|
||||
}
|
||||
@ -444,12 +457,12 @@ livechat-tags-input {
|
||||
color: #fff;
|
||||
background-color: var(--inputBorderColor);
|
||||
|
||||
.tag-close {
|
||||
.livechat-tag-close {
|
||||
color: var(--inputBorderColor);
|
||||
}
|
||||
}
|
||||
|
||||
.tag-name {
|
||||
.livechat-tag-name {
|
||||
margin-top: 3px;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
@ -458,7 +471,7 @@ livechat-tags-input {
|
||||
}
|
||||
}
|
||||
|
||||
#tags.unfocused .tag {
|
||||
.livechat-tags.livechat-unfocused .livechat-tag {
|
||||
filter: opacity(50%) grayscale(80%);
|
||||
}
|
||||
}
|
||||
|
1
client/@types/global.d.ts
vendored
1
client/@types/global.d.ts
vendored
@ -23,6 +23,7 @@ declare const LOC_SHOW_SCROLLBARR: string
|
||||
declare const LOC_TRANSPARENT_BACKGROUND: string
|
||||
declare const LOC_TIPS_FOR_STREAMERS: string
|
||||
declare const LOC_COPY: string
|
||||
declare const LOC_COPIED: string
|
||||
declare const LOC_LINK_COPIED: string
|
||||
declare const LOC_ERROR: string
|
||||
declare const LOC_OPEN: string
|
||||
|
@ -2,14 +2,30 @@
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { html } from 'lit'
|
||||
import { customElement, property, state } from 'lit/decorators.js'
|
||||
import { LivechatElement } from './livechat'
|
||||
import { ptTr } from '../directives/translation'
|
||||
import { html } from 'lit'
|
||||
import { unsafeHTML } from 'lit/directives/unsafe-html.js'
|
||||
import { customElement, property, state } from 'lit/decorators.js'
|
||||
import { ifDefined } from 'lit/directives/if-defined.js'
|
||||
import { classMap } from 'lit/directives/class-map.js'
|
||||
import { animate, fadeOut, fadeIn } from '@lit-labs/motion'
|
||||
import { repeat } from 'lit/directives/repeat.js'
|
||||
|
||||
// FIXME: find a better way to store this image.
|
||||
// This content comes from the file assets/images/copy.svg, after svgo cleaning.
|
||||
// To get the formated content, you can do:
|
||||
// xmllint dist/client/images/copy.svg --format
|
||||
// Then replace the main color by «currentColor»
|
||||
const copySVG = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 4.233 4.233">
|
||||
<g style="stroke-width:1.00021;stroke-miterlimit:4;stroke-dasharray:none">` +
|
||||
// eslint-disable-next-line max-len
|
||||
'<path style="opacity:.998;fill:none;fill-opacity:1;stroke:currentColor;stroke-width:1.17052;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="m4.084 4.046-.616.015-.645-.004a.942.942 0 0 1-.942-.942v-4.398a.94.94 0 0 1 .942-.943H7.22a.94.94 0 0 1 .942.943l-.006.334-.08.962" transform="matrix(.45208 0 0 .45208 -.528 1.295)"/>' +
|
||||
// eslint-disable-next-line max-len
|
||||
'<path style="opacity:.998;fill:none;fill-opacity:1;stroke:currentColor;stroke-width:1.17052;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="M8.434 5.85c-.422.009-1.338.009-1.76.01-.733.004-2.199 0-2.199 0a.94.94 0 0 1-.942-.941V.52a.94.94 0 0 1 .942-.942h4.398a.94.94 0 0 1 .943.942s.004 1.466 0 2.2c-.003.418-.019 1.251-.006 1.67.024.812-.382 1.439-1.376 1.46z" transform="matrix(.45208 0 0 .45208 -.528 1.295)"/>' +
|
||||
`</g>
|
||||
</svg>`
|
||||
|
||||
@customElement('livechat-tags-input')
|
||||
export class TagsInputElement extends LivechatElement {
|
||||
@property({ attribute: false })
|
||||
@ -71,30 +87,55 @@ export class TagsInputElement extends LivechatElement {
|
||||
}
|
||||
|
||||
protected override render = (): unknown => {
|
||||
return html`<ul
|
||||
id="tags"
|
||||
class=${classMap({
|
||||
empty: !this.value.length,
|
||||
unfocused: this._searchedTagsIndex.length
|
||||
})}>
|
||||
${repeat(this.value, tag => tag,
|
||||
(tag, index) => html`<li key=${index} class="tag" title=${tag} ${animate({
|
||||
keyframeOptions: {
|
||||
duration: this.animDuration,
|
||||
fill: 'both'
|
||||
},
|
||||
in: fadeIn,
|
||||
out: fadeOut
|
||||
})}>
|
||||
<span class='tag-name'>${tag}</span>
|
||||
<span class='tag-close'
|
||||
@click=${() => this._handleDeleteTag(index)}></span>
|
||||
</li>`
|
||||
)}
|
||||
</ul>
|
||||
<ul id="tags-searched" class=${classMap({ empty: !this._searchedTagsIndex.length })}>
|
||||
return html`
|
||||
<div class=${classMap({
|
||||
'livechat-empty': !this.value.length,
|
||||
'livechat-tags-container': true
|
||||
})}
|
||||
>
|
||||
<ul
|
||||
class=${classMap({
|
||||
'livechat-empty': !this.value.length,
|
||||
'livechat-unfocused': this._searchedTagsIndex.length,
|
||||
'livechat-tags': true
|
||||
})}>
|
||||
${repeat(this.value, tag => tag,
|
||||
(tag, index) => html`<li key=${index} class="livechat-tag" title=${tag} ${animate({
|
||||
keyframeOptions: {
|
||||
duration: this.animDuration,
|
||||
fill: 'both'
|
||||
},
|
||||
in: fadeIn,
|
||||
out: fadeOut
|
||||
})}>
|
||||
<span class='livechat-tag-name'>${tag}</span>
|
||||
<span class='livechat-tag-close'
|
||||
@click=${() => this._handleDeleteTag(index)}></span>
|
||||
</li>`
|
||||
)}
|
||||
</ul>
|
||||
${
|
||||
this.value?.length === 0
|
||||
? ''
|
||||
: html`<button
|
||||
type="button"
|
||||
class="peertube-plugin-livechat-tags-input-copy"
|
||||
title=${ptTr(LOC_COPY) as any}
|
||||
@click=${async (ev: Event) => {
|
||||
ev.preventDefault()
|
||||
await navigator.clipboard.writeText(this.value.join(this.separator))
|
||||
this.ptNotifier.success(await this.ptTranslate(LOC_COPIED))
|
||||
}}
|
||||
>${unsafeHTML(copySVG)}</button>`
|
||||
}
|
||||
</div>
|
||||
<ul class=${classMap({
|
||||
'livechat-empty': !this._searchedTagsIndex.length,
|
||||
'livechat-tags-searched': true
|
||||
})}
|
||||
>
|
||||
${repeat(this._searchedTagsIndex, index => index,
|
||||
(index) => html`<li key=${index} class="tag-searched" title=${this.value[index]} ${animate({
|
||||
(index) => html`<li key=${index} class="livechat-tag-searched" title=${this.value[index]} ${animate({
|
||||
keyframeOptions: {
|
||||
duration: this.animDuration,
|
||||
fill: 'both'
|
||||
@ -102,8 +143,8 @@ export class TagsInputElement extends LivechatElement {
|
||||
in: fadeIn,
|
||||
out: fadeOut
|
||||
})}>
|
||||
<span class='tag-name'>${this.value[index]}</span>
|
||||
<span class='tag-close'
|
||||
<span class='livechat-tag-name'>${this.value[index]}</span>
|
||||
<span class='livechat-tag-close'
|
||||
@click=${() => this._handleDeleteTag(index)}>
|
||||
</span>
|
||||
</li>`
|
||||
|
@ -12,6 +12,7 @@ transparent_background: "Transparent background (for stream integration, with OB
|
||||
tips_for_streamers: "Tips for streamers: To add the chat to your OBS, generate a read-only
|
||||
link and use it as a browser source."
|
||||
copy: "Copy"
|
||||
copied: "Copied"
|
||||
link_copied: "Link copied"
|
||||
error: "Error"
|
||||
open: "Open"
|
||||
|
Loading…
Reference in New Issue
Block a user