mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2024-12-20 19:06:06 +01:00
Add:Rich text editor for podcast episode description
This commit is contained in:
parent
a394f38fe9
commit
8b12508b0c
@ -29,6 +29,17 @@
|
||||
padding-inline-start: 40px;
|
||||
}
|
||||
|
||||
.default-style ol {
|
||||
display: block;
|
||||
list-style: decimal;
|
||||
list-style-type: decimal;
|
||||
margin-block-start: 1em;
|
||||
margin-block-end: 1em;
|
||||
margin-inline-start: 0px;
|
||||
margin-inline-end: 0px;
|
||||
padding-inline-start: 40px;
|
||||
}
|
||||
|
||||
.default-style li {
|
||||
display: list-item;
|
||||
text-align: -webkit-match-parent;
|
||||
|
563
client/assets/trix.css
Normal file
563
client/assets/trix.css
Normal file
@ -0,0 +1,563 @@
|
||||
@charset "UTF-8";
|
||||
|
||||
/*
|
||||
Trix 1.3.1
|
||||
Copyright © 2020 Basecamp, LLC
|
||||
http://trix-editor.org/*/
|
||||
trix-editor {
|
||||
border: 1px solid rgb(75, 85, 99);
|
||||
border-radius: 3px;
|
||||
background: rgb(35, 35, 35);
|
||||
margin: 0;
|
||||
padding: 0.4em 0.6em;
|
||||
min-height: 5em;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
trix-toolbar * {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
trix-toolbar .trix-button-row {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
justify-content: space-between;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
trix-toolbar .trix-button-group {
|
||||
display: flex;
|
||||
margin-bottom: 10px;
|
||||
border: 1px solid rgb(75, 85, 99);
|
||||
border-top-color: rgb(75, 85, 99);
|
||||
border-bottom-color: rgb(75, 85, 99);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
trix-toolbar .trix-button-group:not(:first-child) {
|
||||
margin-left: 1.5vw;
|
||||
}
|
||||
|
||||
@media (max-device-width: 768px) {
|
||||
trix-toolbar .trix-button-group:not(:first-child) {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
trix-toolbar .trix-button-group-spacer {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
@media (max-device-width: 768px) {
|
||||
trix-toolbar .trix-button-group-spacer {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
trix-toolbar .trix-button {
|
||||
position: relative;
|
||||
float: left;
|
||||
color: rgba(0, 0, 0, 0.6);
|
||||
font-size: 0.75em;
|
||||
font-weight: 600;
|
||||
white-space: nowrap;
|
||||
padding: 0 0.5em;
|
||||
margin: 0;
|
||||
outline: none;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
trix-toolbar .trix-button:not(:first-child) {
|
||||
border-left: 1px solid rgb(75, 85, 99);
|
||||
}
|
||||
|
||||
trix-toolbar .trix-button.trix-active {
|
||||
background: #bbb;
|
||||
color: black;
|
||||
}
|
||||
|
||||
trix-toolbar .trix-button:not(:disabled) {
|
||||
cursor: pointer;
|
||||
background: rgb(35, 35, 35);
|
||||
}
|
||||
|
||||
trix-toolbar .trix-button:disabled {
|
||||
color: rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
|
||||
@media (max-device-width: 768px) {
|
||||
trix-toolbar .trix-button {
|
||||
letter-spacing: -0.01em;
|
||||
padding: 0 0.3em;
|
||||
}
|
||||
}
|
||||
|
||||
trix-toolbar .trix-button--icon {
|
||||
font-size: inherit;
|
||||
width: 2.6em;
|
||||
height: 1.6em;
|
||||
max-width: calc(0.8em + 4vw);
|
||||
text-indent: -9999px;
|
||||
}
|
||||
|
||||
@media (max-device-width: 768px) {
|
||||
trix-toolbar .trix-button--icon {
|
||||
height: 2em;
|
||||
max-width: calc(0.8em + 3.5vw);
|
||||
}
|
||||
}
|
||||
|
||||
trix-toolbar .trix-button--icon::before {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
opacity: 0.6;
|
||||
content: "";
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: contain;
|
||||
filter: invert(100%);
|
||||
}
|
||||
|
||||
@media (max-device-width: 768px) {
|
||||
trix-toolbar .trix-button--icon::before {
|
||||
right: 6%;
|
||||
left: 6%;
|
||||
}
|
||||
}
|
||||
|
||||
trix-toolbar .trix-button--icon.trix-active::before {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
trix-toolbar .trix-button--icon:disabled::before {
|
||||
opacity: 0.125;
|
||||
}
|
||||
|
||||
trix-toolbar .trix-button--icon-attach::before {
|
||||
background-image: url(data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%3E%3Cpath%20d%3D%22M16.5%206v11.5a4%204%200%201%201-8%200V5a2.5%202.5%200%200%201%205%200v10.5a1%201%200%201%201-2%200V6H10v9.5a2.5%202.5%200%200%200%205%200V5a4%204%200%201%200-8%200v12.5a5.5%205.5%200%200%200%2011%200V6h-1.5z%22%2F%3E%3C%2Fsvg%3E);
|
||||
top: 8%;
|
||||
bottom: 4%;
|
||||
}
|
||||
|
||||
trix-toolbar .trix-button--icon-bold::before {
|
||||
background-image: url(data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%3E%3Cpath%20d%3D%22M15.6%2011.8c1-.7%201.6-1.8%201.6-2.8a4%204%200%200%200-4-4H7v14h7c2.1%200%203.7-1.7%203.7-3.8%200-1.5-.8-2.8-2.1-3.4zM10%207.5h3a1.5%201.5%200%201%201%200%203h-3v-3zm3.5%209H10v-3h3.5a1.5%201.5%200%201%201%200%203z%22%2F%3E%3C%2Fsvg%3E);
|
||||
}
|
||||
|
||||
trix-toolbar .trix-button--icon-italic::before {
|
||||
background-image: url(data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%3E%3Cpath%20d%3D%22M10%205v3h2.2l-3.4%208H6v3h8v-3h-2.2l3.4-8H18V5h-8z%22%2F%3E%3C%2Fsvg%3E);
|
||||
}
|
||||
|
||||
trix-toolbar .trix-button--icon-link::before {
|
||||
background-image: url(data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%3E%3Cpath%20d%3D%22M9.88%2013.7a4.3%204.3%200%200%201%200-6.07l3.37-3.37a4.26%204.26%200%200%201%206.07%200%204.3%204.3%200%200%201%200%206.06l-1.96%201.72a.91.91%200%201%201-1.3-1.3l1.97-1.71a2.46%202.46%200%200%200-3.48-3.48l-3.38%203.37a2.46%202.46%200%200%200%200%203.48.91.91%200%201%201-1.3%201.3z%22%2F%3E%3Cpath%20d%3D%22M4.25%2019.46a4.3%204.3%200%200%201%200-6.07l1.93-1.9a.91.91%200%201%201%201.3%201.3l-1.93%201.9a2.46%202.46%200%200%200%203.48%203.48l3.37-3.38c.96-.96.96-2.52%200-3.48a.91.91%200%201%201%201.3-1.3%204.3%204.3%200%200%201%200%206.07l-3.38%203.38a4.26%204.26%200%200%201-6.07%200z%22%2F%3E%3C%2Fsvg%3E);
|
||||
}
|
||||
|
||||
trix-toolbar .trix-button--icon-strike::before {
|
||||
background-image: url(data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%3E%3Cpath%20d%3D%22M12.73%2014l.28.14c.26.15.45.3.57.44.12.14.18.3.18.5%200%20.3-.15.56-.44.75-.3.2-.76.3-1.39.3A13.52%2013.52%200%200%201%207%2014.95v3.37a10.64%2010.64%200%200%200%204.84.88c1.26%200%202.35-.19%203.28-.56.93-.37%201.64-.9%202.14-1.57s.74-1.45.74-2.32c0-.26-.02-.51-.06-.75h-5.21zm-5.5-4c-.08-.34-.12-.7-.12-1.1%200-1.29.52-2.3%201.58-3.02%201.05-.72%202.5-1.08%204.34-1.08%201.62%200%203.28.34%204.97%201l-1.3%202.93c-1.47-.6-2.73-.9-3.8-.9-.55%200-.96.08-1.2.26-.26.17-.38.38-.38.64%200%20.27.16.52.48.74.17.12.53.3%201.05.53H7.23zM3%2013h18v-2H3v2z%22%2F%3E%3C%2Fsvg%3E);
|
||||
}
|
||||
|
||||
trix-toolbar .trix-button--icon-quote::before {
|
||||
background-image: url(data:image/svg+xml,%3Csvg%20version%3D%221%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%3E%3Cpath%20d%3D%22M6%2017h3l2-4V7H5v6h3zm8%200h3l2-4V7h-6v6h3z%22%2F%3E%3C%2Fsvg%3E);
|
||||
}
|
||||
|
||||
trix-toolbar .trix-button--icon-heading-1::before {
|
||||
background-image: url(data:image/svg+xml,%3Csvg%20version%3D%221%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%3E%3Cpath%20d%3D%22M12%209v3H9v7H6v-7H3V9h9zM8%204h14v3h-6v12h-3V7H8V4z%22%2F%3E%3C%2Fsvg%3E);
|
||||
}
|
||||
|
||||
trix-toolbar .trix-button--icon-code::before {
|
||||
background-image: url(data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%3E%3Cpath%20d%3D%22M18.2%2012L15%2015.2l1.4%201.4L21%2012l-4.6-4.6L15%208.8l3.2%203.2zM5.8%2012L9%208.8%207.6%207.4%203%2012l4.6%204.6L9%2015.2%205.8%2012z%22%2F%3E%3C%2Fsvg%3E);
|
||||
}
|
||||
|
||||
trix-toolbar .trix-button--icon-bullet-list::before {
|
||||
background-image: url(data:image/svg+xml,%3Csvg%20version%3D%221%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%3E%3Cpath%20d%3D%22M4%204a2%202%200%201%200%200%204%202%202%200%200%200%200-4zm0%206a2%202%200%201%200%200%204%202%202%200%200%200%200-4zm0%206a2%202%200%201%200%200%204%202%202%200%200%200%200-4zm4%203h14v-2H8v2zm0-6h14v-2H8v2zm0-8v2h14V5H8z%22%2F%3E%3C%2Fsvg%3E);
|
||||
}
|
||||
|
||||
trix-toolbar .trix-button--icon-number-list::before {
|
||||
background-image: url(data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%3E%3Cpath%20d%3D%22M2%2017h2v.5H3v1h1v.5H2v1h3v-4H2v1zm1-9h1V4H2v1h1v3zm-1%203h1.8L2%2013.1v.9h3v-1H3.2L5%2010.9V10H2v1zm5-6v2h14V5H7zm0%2014h14v-2H7v2zm0-6h14v-2H7v2z%22%2F%3E%3C%2Fsvg%3E);
|
||||
}
|
||||
|
||||
trix-toolbar .trix-button--icon-undo::before {
|
||||
background-image: url(data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%3E%3Cpath%20d%3D%22M12.5%208c-2.6%200-5%201-6.9%202.6L2%207v9h9l-3.6-3.6A8%208%200%200%201%2020%2016l2.4-.8a10.5%2010.5%200%200%200-10-7.2z%22%2F%3E%3C%2Fsvg%3E);
|
||||
}
|
||||
|
||||
trix-toolbar .trix-button--icon-redo::before {
|
||||
background-image: url(data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%3E%3Cpath%20d%3D%22M18.4%2010.6a10.5%2010.5%200%200%200-16.9%204.6L4%2016a8%208%200%200%201%2012.7-3.6L13%2016h9V7l-3.6%203.6z%22%2F%3E%3C%2Fsvg%3E);
|
||||
}
|
||||
|
||||
trix-toolbar .trix-button--icon-decrease-nesting-level::before {
|
||||
background-image: url(data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%3E%3Cpath%20d%3D%22M3%2019h19v-2H3v2zm7-6h12v-2H10v2zm-8.3-.3l2.8%202.9L6%2014.2%204%2012l2-2-1.4-1.5L1%2012l.7.7zM3%205v2h19V5H3z%22%2F%3E%3C%2Fsvg%3E);
|
||||
}
|
||||
|
||||
trix-toolbar .trix-button--icon-increase-nesting-level::before {
|
||||
background-image: url(data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%3E%3Cpath%20d%3D%22M3%2019h19v-2H3v2zm7-6h12v-2H10v2zm-6.9-1L1%2014.2l1.4%201.4L6%2012l-.7-.7-2.8-2.8L1%209.9%203.1%2012zM3%205v2h19V5H3z%22%2F%3E%3C%2Fsvg%3E);
|
||||
}
|
||||
|
||||
trix-toolbar .trix-dialogs {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
trix-toolbar .trix-dialog {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
font-size: 0.75em;
|
||||
padding: 15px 10px;
|
||||
background: rgb(48, 48, 48);
|
||||
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
||||
border: 1px solid rgb(112, 112, 112);
|
||||
border-radius: 5px;
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
trix-toolbar .trix-input--dialog {
|
||||
font-size: inherit;
|
||||
font-weight: normal;
|
||||
padding: 0.5em 0.8em;
|
||||
margin: 0 10px 0 0;
|
||||
border-radius: 3px;
|
||||
border: 1px solid #bbb;
|
||||
background-color: rgb(95, 95, 95);
|
||||
box-shadow: none;
|
||||
outline: none;
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
}
|
||||
|
||||
trix-toolbar .trix-input--dialog.validate:invalid {
|
||||
box-shadow: #F00 0px 0px 1.5px 1px;
|
||||
}
|
||||
|
||||
trix-toolbar .trix-button--dialog {
|
||||
font-size: inherit;
|
||||
padding: 0.5em;
|
||||
border-bottom: none;
|
||||
color: #eee;
|
||||
}
|
||||
|
||||
trix-toolbar .trix-dialog--link {
|
||||
max-width: 600px;
|
||||
}
|
||||
|
||||
trix-toolbar .trix-dialog__link-fields {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
trix-toolbar .trix-dialog__link-fields .trix-input {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
trix-toolbar .trix-dialog__link-fields .trix-button-group {
|
||||
flex: 0 0 content;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
trix-editor [data-trix-mutable]:not(.attachment__caption-editor) {
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
trix-editor [data-trix-mutable]::-moz-selection,
|
||||
trix-editor [data-trix-cursor-target]::-moz-selection,
|
||||
trix-editor [data-trix-mutable] ::-moz-selection {
|
||||
background: none;
|
||||
}
|
||||
|
||||
trix-editor [data-trix-mutable]::selection,
|
||||
trix-editor [data-trix-cursor-target]::selection,
|
||||
trix-editor [data-trix-mutable] ::selection {
|
||||
background: none;
|
||||
}
|
||||
|
||||
trix-editor [data-trix-mutable].attachment__caption-editor:focus::-moz-selection {
|
||||
background: highlight;
|
||||
}
|
||||
|
||||
trix-editor [data-trix-mutable].attachment__caption-editor:focus::selection {
|
||||
background: highlight;
|
||||
}
|
||||
|
||||
trix-editor [data-trix-mutable].attachment.attachment--file {
|
||||
box-shadow: 0 0 0 2px highlight;
|
||||
border-color: transparent;
|
||||
}
|
||||
|
||||
trix-editor [data-trix-mutable].attachment img {
|
||||
box-shadow: 0 0 0 2px highlight;
|
||||
}
|
||||
|
||||
trix-editor .attachment {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
trix-editor .attachment:hover {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
trix-editor .attachment--preview .attachment__caption:hover {
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
trix-editor .attachment__progress {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
height: 20px;
|
||||
top: calc(50% - 10px);
|
||||
left: 5%;
|
||||
width: 90%;
|
||||
opacity: 0.9;
|
||||
transition: opacity 200ms ease-in;
|
||||
}
|
||||
|
||||
trix-editor .attachment__progress[value="100"] {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
trix-editor .attachment__caption-editor {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-size: inherit;
|
||||
font-family: inherit;
|
||||
line-height: inherit;
|
||||
color: inherit;
|
||||
text-align: center;
|
||||
vertical-align: top;
|
||||
border: none;
|
||||
outline: none;
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
}
|
||||
|
||||
trix-editor .attachment__toolbar {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
top: -0.9em;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
trix-editor .trix-button-group {
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
trix-editor .trix-button {
|
||||
position: relative;
|
||||
float: left;
|
||||
color: #666;
|
||||
white-space: nowrap;
|
||||
font-size: 80%;
|
||||
padding: 0 0.8em;
|
||||
margin: 0;
|
||||
outline: none;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
trix-editor .trix-button:not(:first-child) {
|
||||
border-left: 1px solid #ccc;
|
||||
}
|
||||
|
||||
trix-editor .trix-button.trix-active {
|
||||
background: #cbeefa;
|
||||
}
|
||||
|
||||
trix-editor .trix-button:not(:disabled) {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
trix-editor .trix-button--remove {
|
||||
text-indent: -9999px;
|
||||
display: inline-block;
|
||||
padding: 0;
|
||||
outline: none;
|
||||
width: 1.8em;
|
||||
height: 1.8em;
|
||||
line-height: 1.8em;
|
||||
border-radius: 50%;
|
||||
background-color: #fff;
|
||||
border: 2px solid highlight;
|
||||
box-shadow: 1px 1px 6px rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
|
||||
trix-editor .trix-button--remove::before {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
opacity: 0.7;
|
||||
content: "";
|
||||
background-image: url(data:image/svg+xml,%3Csvg%20height%3D%2224%22%20width%3D%2224%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M19%206.4L17.6%205%2012%2010.6%206.4%205%205%206.4l5.6%205.6L5%2017.6%206.4%2019l5.6-5.6%205.6%205.6%201.4-1.4-5.6-5.6z%22%2F%3E%3Cpath%20d%3D%22M0%200h24v24H0z%22%20fill%3D%22none%22%2F%3E%3C%2Fsvg%3E);
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 90%;
|
||||
}
|
||||
|
||||
trix-editor .trix-button--remove:hover {
|
||||
border-color: #333;
|
||||
}
|
||||
|
||||
trix-editor .trix-button--remove:hover::before {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
trix-editor .attachment__metadata-container {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
trix-editor .attachment__metadata {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 2em;
|
||||
transform: translate(-50%, 0);
|
||||
max-width: 90%;
|
||||
padding: 0.1em 0.6em;
|
||||
font-size: 0.8em;
|
||||
color: #fff;
|
||||
background-color: rgba(0, 0, 0, 0.7);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
trix-editor .attachment__metadata .attachment__name {
|
||||
display: inline-block;
|
||||
max-width: 100%;
|
||||
vertical-align: bottom;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
trix-editor .attachment__metadata .attachment__size {
|
||||
margin-left: 0.2em;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.trix-content {
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.trix-content * {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.trix-content h1 {
|
||||
font-size: 1.2em;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.trix-content blockquote {
|
||||
border: 0 solid #ccc;
|
||||
border-left-width: 0.3em;
|
||||
margin-left: 0.3em;
|
||||
padding-left: 0.6em;
|
||||
}
|
||||
|
||||
.trix-content [dir=rtl] blockquote,
|
||||
.trix-content blockquote[dir=rtl] {
|
||||
border-width: 0;
|
||||
border-right-width: 0.3em;
|
||||
margin-right: 0.3em;
|
||||
padding-right: 0.6em;
|
||||
}
|
||||
|
||||
.trix-content li {
|
||||
margin-left: 1em;
|
||||
}
|
||||
|
||||
.trix-content [dir=rtl] li {
|
||||
margin-right: 1em;
|
||||
}
|
||||
|
||||
.trix-content pre {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
vertical-align: top;
|
||||
font-family: monospace;
|
||||
font-size: 0.9em;
|
||||
padding: 0.5em;
|
||||
white-space: pre;
|
||||
background-color: #eee;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.trix-content img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.trix-content .attachment {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.trix-content .attachment a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.trix-content .attachment a:hover,
|
||||
.trix-content .attachment a:visited:hover {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.trix-content .attachment__caption {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.trix-content .attachment__caption .attachment__name+.attachment__size::before {
|
||||
content: ' · ';
|
||||
}
|
||||
|
||||
.trix-content .attachment--preview {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.trix-content .attachment--preview .attachment__caption {
|
||||
color: #666;
|
||||
font-size: 0.9em;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.trix-content .attachment--file {
|
||||
color: #333;
|
||||
line-height: 1;
|
||||
margin: 0 2px 2px 2px;
|
||||
padding: 0.4em 1em;
|
||||
border: 1px solid #bbb;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.trix-content .attachment-gallery {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.trix-content .attachment-gallery .attachment {
|
||||
flex: 1 0 33%;
|
||||
padding: 0 0.5em;
|
||||
max-width: 33%;
|
||||
}
|
||||
|
||||
.trix-content .attachment-gallery.attachment-gallery--2 .attachment,
|
||||
.trix-content .attachment-gallery.attachment-gallery--4 .attachment {
|
||||
flex-basis: 50%;
|
||||
max-width: 50%;
|
||||
}
|
@ -5,7 +5,7 @@
|
||||
<p class="font-book text-3xl text-white truncate">{{ title }}</p>
|
||||
</div>
|
||||
</template>
|
||||
<div ref="wrapper" class="p-4 w-full text-sm py-2 rounded-lg bg-bg shadow-lg border border-black-300 relative overflow-hidden">
|
||||
<div ref="wrapper" class="p-4 w-full text-sm py-2 rounded-lg bg-bg shadow-lg border border-black-300 relative overflow-y-auto" style="max-height: 80vh">
|
||||
<div class="flex flex-wrap">
|
||||
<div class="w-1/5 p-1">
|
||||
<ui-text-input-with-label v-model="newEpisode.season" label="Season" />
|
||||
@ -25,8 +25,8 @@
|
||||
<div class="w-full p-1">
|
||||
<ui-textarea-with-label v-model="newEpisode.subtitle" label="Subtitle" :rows="3" />
|
||||
</div>
|
||||
<div class="w-full p-1">
|
||||
<ui-textarea-with-label v-model="newEpisode.description" label="Description" :rows="8" />
|
||||
<div class="w-full p-1 default-style">
|
||||
<ui-rich-text-editor v-if="show" label="Description" v-model="newEpisode.description" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-end pt-4">
|
||||
|
75
client/components/ui/RichTextEditor.vue
Normal file
75
client/components/ui/RichTextEditor.vue
Normal file
@ -0,0 +1,75 @@
|
||||
<template>
|
||||
<div>
|
||||
<p v-if="label" class="px-1 text-sm font-semibold" :class="{ 'text-gray-400': disabled }">
|
||||
{{ label }}
|
||||
</p>
|
||||
<ui-vue-trix v-model="content" :config="config" :disabled-editor="disabled" @trix-file-accept="trixFileAccept" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
value: String,
|
||||
label: String,
|
||||
disabled: Boolean
|
||||
},
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
computed: {
|
||||
content: {
|
||||
get() {
|
||||
return this.value
|
||||
},
|
||||
set(val) {
|
||||
this.$emit('input', val)
|
||||
}
|
||||
},
|
||||
config() {
|
||||
return {
|
||||
toolbar: {
|
||||
getDefaultHTML: () => ` <div class="trix-button-row">
|
||||
<span class="trix-button-group trix-button-group--text-tools" data-trix-button-group="text-tools">
|
||||
<button type="button" class="trix-button trix-button--icon trix-button--icon-bold" data-trix-attribute="bold" data-trix-key="b" title="#{lang.bold}" tabindex="-1">#{lang.bold}</button>
|
||||
<button type="button" class="trix-button trix-button--icon trix-button--icon-italic" data-trix-attribute="italic" data-trix-key="i" title="#{lang.italic}" tabindex="-1">#{lang.italic}</button>
|
||||
<button type="button" class="trix-button trix-button--icon trix-button--icon-strike" data-trix-attribute="strike" title="#{lang.strike}" tabindex="-1">#{lang.strike}</button>
|
||||
<button type="button" class="trix-button trix-button--icon trix-button--icon-link" data-trix-attribute="href" data-trix-action="link" data-trix-key="k" title="#{lang.link}" tabindex="-1">#{lang.link}</button>
|
||||
</span>
|
||||
<span class="trix-button-group trix-button-group--block-tools" data-trix-button-group="block-tools">
|
||||
<button type="button" class="trix-button trix-button--icon trix-button--icon-bullet-list" data-trix-attribute="bullet" title="#{lang.bullets}" tabindex="-1">#{lang.bullets}</button>
|
||||
<button type="button" class="trix-button trix-button--icon trix-button--icon-number-list" data-trix-attribute="number" title="#{lang.numbers}" tabindex="-1">#{lang.numbers}</button>
|
||||
</span>
|
||||
|
||||
<span class="trix-button-group-spacer"></span>
|
||||
<span class="trix-button-group trix-button-group--history-tools" data-trix-button-group="history-tools">
|
||||
<button type="button" class="trix-button trix-button--icon trix-button--icon-undo" data-trix-action="undo" data-trix-key="z" title="#{lang.undo}" tabindex="-1">#{lang.undo}</button>
|
||||
<button type="button" class="trix-button trix-button--icon trix-button--icon-redo" data-trix-action="redo" data-trix-key="shift+z" title="#{lang.redo}" tabindex="-1">#{lang.redo}</button>
|
||||
</span>
|
||||
</div>
|
||||
<div class="trix-dialogs" data-trix-dialogs>
|
||||
<div class="trix-dialog trix-dialog--link" data-trix-dialog="href" data-trix-dialog-attribute="href">
|
||||
<div class="trix-dialog__link-fields">
|
||||
<input type="url" name="href" class="trix-input trix-input--dialog" placeholder="#{lang.urlPlaceholder}" aria-label="#{lang.url}" required data-trix-input>
|
||||
<div class="trix-button-group">
|
||||
<input type="button" class="trix-button trix-button--dialog" value="#{lang.link}" data-trix-method="setAttribute">
|
||||
<input type="button" class="trix-button trix-button--dialog" value="#{lang.unlink}" data-trix-method="removeAttribute">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>`
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
trixFileAccept(e) {
|
||||
e.preventDefault()
|
||||
}
|
||||
},
|
||||
mounted() {},
|
||||
beforeDestroy() {
|
||||
console.log('Before destroy')
|
||||
}
|
||||
}
|
||||
</script>
|
284
client/components/ui/VueTrix.vue
Normal file
284
client/components/ui/VueTrix.vue
Normal file
@ -0,0 +1,284 @@
|
||||
<template>
|
||||
<div>
|
||||
<trix-editor :contenteditable="!disabledEditor" :class="['trix-content']" ref="trix" :input="computedId" :placeholder="placeholder" @trix-change="handleContentChange" @trix-initialize="handleInitialize" @trix-focus="processTrixFocus" @trix-blur="processTrixBlur" />
|
||||
<input type="hidden" :name="inputName" :id="computedId" :value="editorContent" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/*
|
||||
ORIGINAL SOURCE: https://github.com/hanhdt/vue-trix
|
||||
|
||||
modified for audiobookshelf
|
||||
*/
|
||||
import Trix from 'trix'
|
||||
import '@/assets/trix.css'
|
||||
|
||||
export default {
|
||||
name: 'vue-trix',
|
||||
model: {
|
||||
prop: 'srcContent',
|
||||
event: 'update'
|
||||
},
|
||||
props: {
|
||||
/**
|
||||
* This prop will put the editor in read-only mode
|
||||
*/
|
||||
disabledEditor: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default() {
|
||||
return false
|
||||
}
|
||||
},
|
||||
/**
|
||||
* This is referenced `id` of the hidden input field defined.
|
||||
* It is optional and will be a random string by default.
|
||||
*/
|
||||
inputId: {
|
||||
type: String,
|
||||
required: false,
|
||||
default() {
|
||||
return ''
|
||||
}
|
||||
},
|
||||
/**
|
||||
* This is referenced `name` of the hidden input field defined,
|
||||
* default value is `content`.
|
||||
*/
|
||||
inputName: {
|
||||
type: String,
|
||||
required: false,
|
||||
default() {
|
||||
return 'content'
|
||||
}
|
||||
},
|
||||
/**
|
||||
* The placeholder attribute specifies a short hint
|
||||
* that describes the expected value of a editor.
|
||||
*/
|
||||
placeholder: {
|
||||
type: String,
|
||||
required: false,
|
||||
default() {
|
||||
return ''
|
||||
}
|
||||
},
|
||||
/**
|
||||
* The source content is associcated to v-model directive.
|
||||
*/
|
||||
srcContent: {
|
||||
type: String,
|
||||
required: false,
|
||||
default() {
|
||||
return ''
|
||||
}
|
||||
},
|
||||
/**
|
||||
* The boolean attribute allows saving editor state into browser's localStorage
|
||||
* (optional, default is `false`).
|
||||
*/
|
||||
localStorage: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default() {
|
||||
return false
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Focuses cursor in the editor when attached to the DOM
|
||||
* (optional, default is `false`).
|
||||
*/
|
||||
autofocus: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default() {
|
||||
return false
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Object to override default editor configuration
|
||||
*/
|
||||
config: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default() {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
editorContent: this.srcContent,
|
||||
isActived: null
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
editorContent: {
|
||||
handler: 'emitEditorState'
|
||||
},
|
||||
initialContent: {
|
||||
handler: 'handleInitialContentChange'
|
||||
},
|
||||
isDisabled: {
|
||||
handler: 'decorateDisabledEditor'
|
||||
},
|
||||
config: {
|
||||
handler: 'overrideConfig',
|
||||
immediate: true,
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
/**
|
||||
* Compute a random id of hidden input
|
||||
* when it haven't been specified.
|
||||
*/
|
||||
generateId() {
|
||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
||||
var r = (Math.random() * 16) | 0
|
||||
var v = c === 'x' ? r : (r & 0x3) | 0x8
|
||||
return v.toString(16)
|
||||
})
|
||||
},
|
||||
computedId() {
|
||||
return this.inputId || this.generateId
|
||||
},
|
||||
initialContent() {
|
||||
return this.srcContent
|
||||
},
|
||||
isDisabled() {
|
||||
return this.disabledEditor
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
processTrixFocus(event) {
|
||||
if (this.$refs.trix) {
|
||||
this.isActived = true
|
||||
this.$emit('trix-focus', this.$refs.trix.editor, event)
|
||||
}
|
||||
},
|
||||
processTrixBlur(event) {
|
||||
if (this.$refs.trix) {
|
||||
this.isActived = false
|
||||
this.$emit('trix-blur', this.$refs.trix.editor, event)
|
||||
}
|
||||
},
|
||||
handleContentChange(event) {
|
||||
this.editorContent = event.srcElement ? event.srcElement.value : event.target.value
|
||||
this.$emit('input', this.editorContent)
|
||||
},
|
||||
handleInitialize(event) {
|
||||
/**
|
||||
* If autofocus is true, manually set focus to
|
||||
* beginning of content (consistent with Trix behavior)
|
||||
*/
|
||||
if (this.autofocus) {
|
||||
this.$refs.trix.editor.setSelectedRange(0)
|
||||
}
|
||||
this.$emit('trix-initialize', this.emitInitialize)
|
||||
},
|
||||
handleInitialContentChange(newContent, oldContent) {
|
||||
newContent = newContent === undefined ? '' : newContent
|
||||
if (this.$refs.trix.editor && this.$refs.trix.editor.innerHTML !== newContent) {
|
||||
/* Update editor's content when initial content changed */
|
||||
this.editorContent = newContent
|
||||
/**
|
||||
* If user are typing, then don't reload the editor,
|
||||
* hence keep cursor's position after typing.
|
||||
*/
|
||||
if (!this.isActived) {
|
||||
this.reloadEditorContent(this.editorContent)
|
||||
}
|
||||
}
|
||||
},
|
||||
emitEditorState(value) {
|
||||
/**
|
||||
* If localStorage is enabled,
|
||||
* then save editor's content into storage
|
||||
*/
|
||||
if (this.localStorage) {
|
||||
localStorage.setItem(this.storageId('VueTrix'), JSON.stringify(this.$refs.trix.editor))
|
||||
}
|
||||
this.$emit('update', this.editorContent)
|
||||
},
|
||||
storageId(component) {
|
||||
if (this.inputId) {
|
||||
return `${component}.${this.inputId}.content`
|
||||
} else {
|
||||
return `${component}.content`
|
||||
}
|
||||
},
|
||||
reloadEditorContent(newContent) {
|
||||
// Reload HTML content
|
||||
this.$refs.trix.editor.loadHTML(newContent)
|
||||
// Move cursor to end of new content updated
|
||||
this.$refs.trix.editor.setSelectedRange(this.getContentEndPosition())
|
||||
},
|
||||
getContentEndPosition() {
|
||||
return this.$refs.trix.editor.getDocument().toString().length - 1
|
||||
},
|
||||
decorateDisabledEditor(editorState) {
|
||||
/** Disable toolbar and editor by pointer events styling */
|
||||
if (editorState) {
|
||||
this.$refs.trix.toolbarElement.style['pointer-events'] = 'none'
|
||||
this.$refs.trix.contentEditable = false
|
||||
this.$refs.trix.style['background'] = '#e9ecef'
|
||||
} else {
|
||||
this.$refs.trix.toolbarElement.style['pointer-events'] = 'unset'
|
||||
this.$refs.trix.style['pointer-events'] = 'unset'
|
||||
this.$refs.trix.style['background'] = 'transparent'
|
||||
}
|
||||
},
|
||||
overrideConfig(config) {
|
||||
Trix.config = this.deepMerge(Trix.config, config)
|
||||
},
|
||||
deepMerge(target, override) {
|
||||
// deep merge the object into the target object
|
||||
for (let prop in override) {
|
||||
if (override.hasOwnProperty(prop)) {
|
||||
if (Object.prototype.toString.call(override[prop]) === '[object Object]') {
|
||||
// if the property is a nested object
|
||||
target[prop] = this.deepMerge(target[prop], override[prop])
|
||||
} else {
|
||||
// for regular property
|
||||
target[prop] = override[prop]
|
||||
}
|
||||
}
|
||||
}
|
||||
return target
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
/** Override editor configuration */
|
||||
this.overrideConfig(this.config)
|
||||
/** Check if editor read-only mode is required */
|
||||
this.decorateDisabledEditor(this.disabledEditor)
|
||||
this.$nextTick(() => {
|
||||
/**
|
||||
* If localStorage is enabled,
|
||||
* then load editor's content from the beginning.
|
||||
*/
|
||||
if (this.localStorage) {
|
||||
const savedValue = localStorage.getItem(this.storageId('VueTrix'))
|
||||
if (savedValue && !this.srcContent) {
|
||||
this.$refs.trix.editor.loadJSON(JSON.parse(savedValue))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="css" module>
|
||||
.trix_container {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
.trix_container .trix-button-group {
|
||||
background-color: white;
|
||||
}
|
||||
.trix_container .trix-content {
|
||||
background-color: white;
|
||||
}
|
||||
</style>
|
15
client/package-lock.json
generated
15
client/package-lock.json
generated
@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "audiobookshelf-client",
|
||||
"version": "2.0.14",
|
||||
"version": "2.0.17",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "audiobookshelf-client",
|
||||
"version": "2.0.14",
|
||||
"version": "2.0.17",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@nuxtjs/axios": "^5.13.6",
|
||||
@ -18,6 +18,7 @@
|
||||
"libarchive.js": "^1.3.0",
|
||||
"nuxt": "^2.15.8",
|
||||
"nuxt-socket-io": "^1.1.18",
|
||||
"trix": "^1.3.1",
|
||||
"v-click-outside": "^3.1.2",
|
||||
"vue-pdf": "^4.3.0",
|
||||
"vue-toastification": "^1.7.11",
|
||||
@ -15285,6 +15286,11 @@
|
||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||
"integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o="
|
||||
},
|
||||
"node_modules/trix": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/trix/-/trix-1.3.1.tgz",
|
||||
"integrity": "sha512-BbH6mb6gk+AV4f2as38mP6Ucc1LE3OD6XxkZnAgPIduWXYtvg2mI3cZhIZSLqmMh9OITEpOBCCk88IVmyjU7bA=="
|
||||
},
|
||||
"node_modules/ts-pnp": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/ts-pnp/-/ts-pnp-1.2.0.tgz",
|
||||
@ -29080,6 +29086,11 @@
|
||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||
"integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o="
|
||||
},
|
||||
"trix": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/trix/-/trix-1.3.1.tgz",
|
||||
"integrity": "sha512-BbH6mb6gk+AV4f2as38mP6Ucc1LE3OD6XxkZnAgPIduWXYtvg2mI3cZhIZSLqmMh9OITEpOBCCk88IVmyjU7bA=="
|
||||
},
|
||||
"ts-pnp": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/ts-pnp/-/ts-pnp-1.2.0.tgz",
|
||||
|
@ -22,6 +22,7 @@
|
||||
"libarchive.js": "^1.3.0",
|
||||
"nuxt": "^2.15.8",
|
||||
"nuxt-socket-io": "^1.1.18",
|
||||
"trix": "^1.3.1",
|
||||
"v-click-outside": "^3.1.2",
|
||||
"vue-pdf": "^4.3.0",
|
||||
"vue-toastification": "^1.7.11",
|
||||
@ -32,4 +33,4 @@
|
||||
"@nuxtjs/tailwindcss": "^4.2.1",
|
||||
"postcss": "^8.3.6"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ const sanitizeHtml = require('../libs/sanitizeHtml')
|
||||
function sanitize(html) {
|
||||
const sanitizerOptions = {
|
||||
allowedTags: [
|
||||
'p', 'ol', 'ul', 'li', 'a', 'strong', 'em'
|
||||
'p', 'ol', 'ul', 'li', 'a', 'strong', 'em', 'del'
|
||||
],
|
||||
disallowedTagsMode: 'discard',
|
||||
allowedAttributes: {
|
||||
|
Loading…
Reference in New Issue
Block a user