Feature update and bug fixes
Features - added feature to view contributors - added option to preview selected content before exporting or publishing - Added option to share composed article preview (sub-admin only) - added option to re-arrange and remove selected content from preview page Fixed - Fixed bug causing changes shown in version history not to be properly styles - fixed issue causing newly created entry's author to not show up in version history entry
This commit is contained in:
parent
0bee787ccb
commit
db1a4db959
@ -1236,21 +1236,24 @@ customElements.define('sm-popup', class extends HTMLElement {
|
||||
}
|
||||
|
||||
setStateOpen() {
|
||||
const animOptions = {
|
||||
duration: 300,
|
||||
easing: 'ease'
|
||||
if (!this.isOpen || this.offset) {
|
||||
const animOptions = {
|
||||
duration: 300,
|
||||
easing: 'ease'
|
||||
}
|
||||
const initialAnimation = (window.innerWidth > 640) ? 'scale(1.1)' : `translateY(${this.offset ? `${this.offset}px` : '100%'})`
|
||||
this.animateTo(this.popup, [
|
||||
{
|
||||
opacity: this.offset ? 1 : 0,
|
||||
transform: initialAnimation
|
||||
},
|
||||
{
|
||||
opacity: 1,
|
||||
transform: 'none'
|
||||
},
|
||||
], animOptions)
|
||||
|
||||
}
|
||||
const initialAnimation = (window.innerWidth > 640) ? 'scale(1.1)' : `translateY(${this.offset ? `${this.offset}px` : '100%'})`
|
||||
this.animateTo(this.popup, [
|
||||
{
|
||||
opacity: this.offset ? 1 : 0,
|
||||
transform: initialAnimation
|
||||
},
|
||||
{
|
||||
opacity: 1,
|
||||
transform: 'none'
|
||||
},
|
||||
], animOptions)
|
||||
}
|
||||
|
||||
show(options = {}) {
|
||||
@ -1351,6 +1354,7 @@ customElements.define('sm-popup', class extends HTMLElement {
|
||||
}
|
||||
|
||||
handleTouchStart(e) {
|
||||
this.offset = 0
|
||||
this.popupHeader.addEventListener('touchmove', this.handleTouchMove, { passive: true });
|
||||
this.popupHeader.addEventListener('touchend', this.handleTouchEnd, { passive: true });
|
||||
this.touchStartY = e.changedTouches[0].clientY;
|
||||
@ -1371,7 +1375,6 @@ customElements.define('sm-popup', class extends HTMLElement {
|
||||
this.touchEndTime = e.timeStamp;
|
||||
cancelAnimationFrame(this.touchEndAnimation);
|
||||
this.touchEndY = e.changedTouches[0].clientY;
|
||||
this.popup.style.transition = 'transform 0.3s';
|
||||
this.threshold = this.popup.getBoundingClientRect().height * 0.3;
|
||||
if (this.touchEndTime - this.touchStartTime > 200) {
|
||||
if (this.touchEndY - this.touchStartY > this.threshold) {
|
||||
@ -1726,10 +1729,15 @@ smCopy.innerHTML = `
|
||||
align-items: center;
|
||||
grid-template-columns: minmax(0, 1fr) auto;
|
||||
}
|
||||
.copy-content{
|
||||
:host(:not([clip-text])) .copy-content{
|
||||
overflow-wrap: break-word;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
:host([clip-text]) .copy-content{
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.copy-button{
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
@ -3345,10 +3353,6 @@ smSwitch.innerHTML = `
|
||||
cursor: pointer;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
:host(:not([disabled])) label:focus-visible{
|
||||
-webkit-box-shadow: 0 0 0 0.1rem var(--accent-color);
|
||||
box-shadow: 0 0 0 0.1rem var(--accent-color);
|
||||
}
|
||||
:host([disabled]) {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.6;
|
||||
@ -3388,15 +3392,12 @@ smSwitch.innerHTML = `
|
||||
border-radius: 1rem;
|
||||
}
|
||||
|
||||
.switch:active .button::after,
|
||||
.switch:focus .button::after{
|
||||
opacity: 1
|
||||
}
|
||||
.switch:focus-visible .button::after{
|
||||
opacity: 1
|
||||
label:active .thumb::after,
|
||||
label:focus-within .thumb::after{
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.button::after{
|
||||
.thumb::after{
|
||||
content: '';
|
||||
display: -webkit-box;
|
||||
display: -ms-flexbox;
|
||||
@ -3412,7 +3413,7 @@ smSwitch.innerHTML = `
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
|
||||
.button {
|
||||
.thumb {
|
||||
position: relative;
|
||||
display: -webkit-inline-box;
|
||||
display: -ms-inline-flexbox;
|
||||
@ -3436,7 +3437,7 @@ smSwitch.innerHTML = `
|
||||
border: solid 0.3rem white;
|
||||
}
|
||||
|
||||
input:checked ~ .button {
|
||||
input:checked ~ .thumb {
|
||||
-webkit-transform: translateX(100%);
|
||||
-ms-transform: translateX(100%);
|
||||
transform: translateX(100%);
|
||||
@ -3451,7 +3452,7 @@ smSwitch.innerHTML = `
|
||||
<div part="switch" class="switch">
|
||||
<input type="checkbox">
|
||||
<div class="track"></div>
|
||||
<div class="button"></div>
|
||||
<div class="thumb"></div>
|
||||
</div>
|
||||
<slot name="right"></slot>
|
||||
</label>`
|
||||
|
||||
187
css/main.css
187
css/main.css
@ -139,7 +139,11 @@ sm-textarea {
|
||||
}
|
||||
|
||||
sm-button {
|
||||
--padding: 0.7rem 1rem;
|
||||
--padding: 0.6rem 0.8rem;
|
||||
-webkit-transition: -webkit-transform 0.3s;
|
||||
transition: -webkit-transform 0.3s;
|
||||
transition: transform 0.3s;
|
||||
transition: transform 0.3s, -webkit-transform 0.3s;
|
||||
}
|
||||
sm-button[variant=primary] .icon {
|
||||
fill: rgba(var(--background-color), 1);
|
||||
@ -196,7 +200,7 @@ ul {
|
||||
}
|
||||
|
||||
.full-bleed {
|
||||
grid-column: 1/4;
|
||||
grid-column: 1/-1;
|
||||
}
|
||||
|
||||
.h1 {
|
||||
@ -421,6 +425,7 @@ ul {
|
||||
|
||||
button:active,
|
||||
.button:active,
|
||||
sm-button:not([disabled]):active,
|
||||
.interact:active {
|
||||
-webkit-transform: scale(0.96);
|
||||
transform: scale(0.96);
|
||||
@ -512,18 +517,16 @@ menu-option {
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.page-layout {
|
||||
.page-layout,
|
||||
#preview_page {
|
||||
display: grid;
|
||||
grid-template-columns: 1rem minmax(0, 1fr) 1rem;
|
||||
}
|
||||
.page-layout > * {
|
||||
.page-layout > *,
|
||||
#preview_page > * {
|
||||
grid-column: 2/3;
|
||||
}
|
||||
|
||||
.page {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#main_header {
|
||||
display: grid;
|
||||
gap: 1rem;
|
||||
@ -534,6 +537,7 @@ menu-option {
|
||||
grid-template-columns: 1fr auto auto;
|
||||
grid-column: 1/-1;
|
||||
background-color: var(--foreground-color);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.label {
|
||||
@ -700,8 +704,31 @@ menu-option {
|
||||
flex-shrink: 0;
|
||||
border-radius: 0.5rem;
|
||||
background-color: var(--foreground-color);
|
||||
border: solid thin rgba(var(--text-color), 0.16);
|
||||
-webkit-box-shadow: 0 0 0 1px rgba(var(--text-color), 0.16) inset;
|
||||
box-shadow: 0 0 0 1px rgba(var(--text-color), 0.16) inset;
|
||||
}
|
||||
.content-card.selected {
|
||||
-webkit-box-shadow: 0 0 0 0.1rem var(--accent-color) inset;
|
||||
box-shadow: 0 0 0 0.1rem var(--accent-color) inset;
|
||||
}
|
||||
.content-card--empty {
|
||||
display: -webkit-box;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-box-direction: normal;
|
||||
-ms-flex-direction: column;
|
||||
flex-direction: column;
|
||||
}
|
||||
.content-card--empty .content__area {
|
||||
min-height: calc(60vh + 3rem);
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.content__header {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.content__area {
|
||||
border-radius: inherit;
|
||||
padding: 1rem;
|
||||
@ -735,8 +762,8 @@ menu-option {
|
||||
grid-template-columns: auto auto 1fr auto;
|
||||
}
|
||||
|
||||
.content__editor {
|
||||
font-size: 0.8rem;
|
||||
.content__contributors {
|
||||
font-size: 0.7rem;
|
||||
background-color: rgba(var(--text-color), 0.06);
|
||||
border-radius: 0.3rem;
|
||||
padding: 0.2rem 0.3rem;
|
||||
@ -873,33 +900,103 @@ menu-option {
|
||||
.entry__changes .removed > * {
|
||||
background-color: transparent;
|
||||
}
|
||||
.entry__changes .added {
|
||||
.entry__changes .added,
|
||||
.entry__changes .added > * {
|
||||
background-color: #00e67650;
|
||||
}
|
||||
.entry__changes .removed {
|
||||
.entry__changes .removed,
|
||||
.entry__changes .removed > * {
|
||||
color: var(--danger-color);
|
||||
-webkit-text-decoration-color: var(--danger-color);
|
||||
text-decoration-color: var(--danger-color);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 40rem) and (any-hover: none) {
|
||||
.cancel-order span {
|
||||
display: none;
|
||||
}
|
||||
.contributor {
|
||||
gap: 0.5rem;
|
||||
grid-template-columns: auto 1fr;
|
||||
}
|
||||
.contributor .icon {
|
||||
grid-row: span 2;
|
||||
}
|
||||
.contributor__id {
|
||||
font-size: 0.8rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
.contributor__time {
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
#preview_page {
|
||||
padding-bottom: 3rem;
|
||||
grid-template-columns: 1.5rem minmax(0, 1fr) 1.5rem;
|
||||
overflow-y: auto;
|
||||
height: 100%;
|
||||
-ms-flex-line-pack: start;
|
||||
align-content: flex-start;
|
||||
}
|
||||
#preview_page header {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
padding-top: 1.5rem;
|
||||
background-color: rgba(var(--background-color), 1);
|
||||
}
|
||||
#preview_page h3 {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
#preview_page h3:not(:first-of-type) {
|
||||
margin-top: 3rem;
|
||||
}
|
||||
#preview_page p {
|
||||
font-family: "noto serif", serif;
|
||||
font-size: 1rem;
|
||||
}
|
||||
#preview_page p > * {
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
.preview-group:not(:last-of-type) {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.preview-group__buttons {
|
||||
display: -webkit-inline-box;
|
||||
display: -ms-inline-flexbox;
|
||||
display: inline-flex;
|
||||
background-color: var(--foreground-color);
|
||||
border: solid thin rgba(var(--text-color), 0.2);
|
||||
padding: 0.2rem;
|
||||
border-radius: 0.3rem;
|
||||
-webkit-box-shadow: 0 0.1rem 0.2rem rgba(0, 0, 0, 0.06), 0 1rem 1.5rem -0.5rem rgba(0, 0, 0, 0.2);
|
||||
box-shadow: 0 0.1rem 0.2rem rgba(0, 0, 0, 0.06), 0 1rem 1.5rem -0.5rem rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
.preview-group__buttons button {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
.preview-group__buttons button .icon {
|
||||
height: 1rem;
|
||||
width: 1rem;
|
||||
}
|
||||
|
||||
.article__title {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 40rem) {
|
||||
sm-button {
|
||||
--padding: 0.9rem 1.6rem;
|
||||
}
|
||||
|
||||
#article_name_wrapper {
|
||||
#article_name_wrapper,
|
||||
#selected_content_options {
|
||||
grid-row: 2/3;
|
||||
grid-column: 1/-1;
|
||||
-webkit-box-pack: start;
|
||||
-ms-flex-pack: start;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
#article_name_wrapper sm-menu {
|
||||
#article_name_wrapper sm-menu,
|
||||
#selected_content_options sm-menu {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
@ -951,16 +1048,27 @@ menu-option {
|
||||
}
|
||||
|
||||
#main_page.active-sidebar {
|
||||
-ms-scroll-chaining: none;
|
||||
overscroll-behavior: contain;
|
||||
height: 100%;
|
||||
overflow-y: hidden;
|
||||
grid-template-rows: auto 1fr;
|
||||
grid-template-columns: minmax(0, 1fr) 24rem;
|
||||
}
|
||||
#main_page.active-sidebar #version_history_panel,
|
||||
#main_page.active-sidebar #article_wrapper {
|
||||
-ms-scroll-chaining: none;
|
||||
overscroll-behavior: contain;
|
||||
}
|
||||
#main_page.active-sidebar #article_wrapper {
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
#article_wrapper {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
#article_list_popup .popup__header {
|
||||
grid-template-columns: auto 1fr auto;
|
||||
padding-bottom: 1rem;
|
||||
@ -969,6 +1077,20 @@ menu-option {
|
||||
#article_list_search {
|
||||
width: 16rem;
|
||||
}
|
||||
|
||||
#preview_page {
|
||||
grid-template-columns: 1fr 60ch 1fr;
|
||||
}
|
||||
|
||||
.preview-group {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.preview-group__buttons {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 100%;
|
||||
}
|
||||
}
|
||||
@media (any-hover: hover) {
|
||||
::-webkit-scrollbar {
|
||||
@ -996,12 +1118,31 @@ button:hover {
|
||||
background-color: rgba(var(--text-color), 0.1);
|
||||
}
|
||||
|
||||
.transaction-card button {
|
||||
.content-card:not(.selected) sm-checkbox {
|
||||
opacity: 0;
|
||||
-webkit-transition: opacity 0.3s;
|
||||
transition: opacity 0.3s;
|
||||
-webkit-transition: opacity 0.2s;
|
||||
transition: opacity 0.2s;
|
||||
}
|
||||
.transaction-card:hover button {
|
||||
.content-card:hover sm-checkbox, .content-card:focus-within sm-checkbox {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.preview-group__buttons {
|
||||
-webkit-transition: opacity 0.1s;
|
||||
transition: opacity 0.1s;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.preview-group {
|
||||
border-radius: 0.3rem;
|
||||
}
|
||||
.preview-group:hover {
|
||||
background-color: rgba(var(--text-color), 0.06);
|
||||
}
|
||||
.preview-group:hover .preview-group__buttons {
|
||||
opacity: 1;
|
||||
}
|
||||
.preview-group .preview-group__buttons:focus-within {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
2
css/main.min.css
vendored
2
css/main.min.css
vendored
File diff suppressed because one or more lines are too long
156
css/main.scss
156
css/main.scss
@ -119,7 +119,8 @@ sm-textarea {
|
||||
--border-radius: 0.3rem;
|
||||
}
|
||||
sm-button {
|
||||
--padding: 0.7rem 1rem;
|
||||
--padding: 0.6rem 0.8rem;
|
||||
transition: transform 0.3s;
|
||||
&[variant="primary"] {
|
||||
.icon {
|
||||
fill: rgba(var(--background-color), 1);
|
||||
@ -179,7 +180,7 @@ ul {
|
||||
}
|
||||
|
||||
.full-bleed {
|
||||
grid-column: 1/4;
|
||||
grid-column: 1/-1;
|
||||
}
|
||||
|
||||
.h1 {
|
||||
@ -375,6 +376,7 @@ ul {
|
||||
|
||||
button:active,
|
||||
.button:active,
|
||||
sm-button:not([disabled]):active,
|
||||
.interact:active {
|
||||
transform: scale(0.96);
|
||||
}
|
||||
@ -450,16 +452,14 @@ menu-option {
|
||||
border-radius: 0.5rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.page-layout {
|
||||
.page-layout,
|
||||
#preview_page {
|
||||
display: grid;
|
||||
grid-template-columns: 1rem minmax(0, 1fr) 1rem;
|
||||
& > * {
|
||||
grid-column: 2/3;
|
||||
}
|
||||
}
|
||||
.page {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#main_header {
|
||||
display: grid;
|
||||
@ -469,6 +469,7 @@ menu-option {
|
||||
grid-template-columns: 1fr auto auto;
|
||||
grid-column: 1/-1;
|
||||
background-color: var(--foreground-color);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.label {
|
||||
@ -611,9 +612,21 @@ menu-option {
|
||||
flex-shrink: 0;
|
||||
border-radius: 0.5rem;
|
||||
background-color: var(--foreground-color);
|
||||
border: solid thin rgba(var(--text-color), 0.16);
|
||||
&--empty {
|
||||
box-shadow: 0 0 0 1px rgba(var(--text-color), 0.16) inset;
|
||||
&.selected {
|
||||
box-shadow: 0 0 0 0.1rem var(--accent-color) inset;
|
||||
}
|
||||
&--empty {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.content__area {
|
||||
min-height: calc(60vh + 3rem);
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
.content__header {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
.content__area {
|
||||
border-radius: inherit;
|
||||
@ -642,8 +655,8 @@ menu-option {
|
||||
padding: 0.5rem 1rem;
|
||||
grid-template-columns: auto auto 1fr auto;
|
||||
}
|
||||
.content__editor {
|
||||
font-size: 0.8rem;
|
||||
.content__contributors {
|
||||
font-size: 0.7rem;
|
||||
background-color: rgba(var(--text-color), 0.06);
|
||||
border-radius: 0.3rem;
|
||||
padding: 0.2rem 0.3rem;
|
||||
@ -763,27 +776,92 @@ menu-option {
|
||||
.removed > * {
|
||||
background-color: transparent;
|
||||
}
|
||||
.added {
|
||||
.added,
|
||||
.added > * {
|
||||
background-color: #00e67650;
|
||||
}
|
||||
.removed {
|
||||
.removed,
|
||||
.removed > * {
|
||||
color: var(--danger-color);
|
||||
text-decoration-color: var(--danger-color);
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 40rem) and (any-hover: none) {
|
||||
.cancel-order {
|
||||
span {
|
||||
display: none;
|
||||
.contributor {
|
||||
gap: 0.5rem;
|
||||
grid-template-columns: auto 1fr;
|
||||
.icon {
|
||||
grid-row: span 2;
|
||||
}
|
||||
&__id {
|
||||
font-size: 0.8rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
&__time {
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
}
|
||||
|
||||
#preview_page {
|
||||
padding-bottom: 3rem;
|
||||
grid-template-columns: 1.5rem minmax(0, 1fr) 1.5rem;
|
||||
overflow-y: auto;
|
||||
height: 100%;
|
||||
align-content: flex-start;
|
||||
header {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
padding-top: 1.5rem;
|
||||
background-color: rgba(var(--background-color), 1);
|
||||
}
|
||||
h3 {
|
||||
margin-bottom: 1rem;
|
||||
&:not(:first-of-type) {
|
||||
margin-top: 3rem;
|
||||
}
|
||||
}
|
||||
p {
|
||||
font-family: "noto serif", serif;
|
||||
font-size: 1rem;
|
||||
& > * {
|
||||
font-family: inherit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.preview-group {
|
||||
&:not(:last-of-type) {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
}
|
||||
.preview-group__buttons {
|
||||
display: inline-flex;
|
||||
background-color: var(--foreground-color);
|
||||
border: solid thin rgba(var(--text-color), 0.2);
|
||||
padding: 0.2rem;
|
||||
border-radius: 0.3rem;
|
||||
box-shadow: 0 0.1rem 0.2rem rgba(0, 0, 0, 0.06),
|
||||
0 1rem 1.5rem -0.5rem rgba(0, 0, 0, 0.2);
|
||||
button {
|
||||
padding: 0.5rem;
|
||||
.icon {
|
||||
height: 1rem;
|
||||
width: 1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.article__title {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 40rem) {
|
||||
sm-button {
|
||||
--padding: 0.9rem 1.6rem;
|
||||
}
|
||||
#article_name_wrapper {
|
||||
#article_name_wrapper,
|
||||
#selected_content_options {
|
||||
grid-row: 2/3;
|
||||
grid-column: 1/-1;
|
||||
justify-content: flex-start;
|
||||
@ -833,16 +911,24 @@ menu-option {
|
||||
}
|
||||
#main_page {
|
||||
&.active-sidebar {
|
||||
overscroll-behavior: contain;
|
||||
height: 100%;
|
||||
overflow-y: hidden;
|
||||
grid-template-rows: auto 1fr;
|
||||
grid-template-columns: minmax(0, 1fr) 24rem;
|
||||
#version_history_panel,
|
||||
#article_wrapper {
|
||||
overscroll-behavior: contain;
|
||||
}
|
||||
#article_wrapper {
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
#article_wrapper {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
#article_list_popup {
|
||||
.popup__header {
|
||||
grid-template-columns: auto 1fr auto;
|
||||
@ -852,6 +938,17 @@ menu-option {
|
||||
#article_list_search {
|
||||
width: 16rem;
|
||||
}
|
||||
#preview_page {
|
||||
grid-template-columns: 1fr 60ch 1fr;
|
||||
}
|
||||
.preview-group {
|
||||
position: relative;
|
||||
}
|
||||
.preview-group__buttons {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 100%;
|
||||
}
|
||||
}
|
||||
@media (any-hover: hover) {
|
||||
::-webkit-scrollbar {
|
||||
@ -874,12 +971,29 @@ menu-option {
|
||||
background-color: rgba(var(--text-color), 0.1);
|
||||
}
|
||||
}
|
||||
.transaction-card {
|
||||
button {
|
||||
.content-card {
|
||||
&:not(.selected) sm-checkbox {
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s;
|
||||
transition: opacity 0.2s;
|
||||
}
|
||||
&:hover button {
|
||||
&:hover sm-checkbox,
|
||||
&:focus-within sm-checkbox {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
.preview-group__buttons {
|
||||
transition: opacity 0.1s;
|
||||
opacity: 0;
|
||||
}
|
||||
.preview-group {
|
||||
border-radius: 0.3rem;
|
||||
&:hover {
|
||||
background-color: rgba(var(--text-color), 0.06);
|
||||
.preview-group__buttons {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
.preview-group__buttons:focus-within {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
522
index.html
522
index.html
@ -11,6 +11,8 @@
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400..700&display=swap" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Noto+Serif:ital,wght@0,400;0,700;1,400;1,700&display=swap"
|
||||
rel="stylesheet">
|
||||
<script src="purify.min.js" defer></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/fuse.js@6.4.6" defer></script>
|
||||
<script id="floGlobals">
|
||||
@ -57,7 +59,7 @@
|
||||
<sm-button variant="no-outline" class="submit-btn">OK</sm-button>
|
||||
</div>
|
||||
</sm-popup>
|
||||
<article id="main_page" class="grid">
|
||||
<article id="main_page" class="grid page">
|
||||
<header id="main_header">
|
||||
<div class="logo">
|
||||
<svg class="main-logo" width="23" height="22" viewBox="0 0 23 22" fill="none"
|
||||
@ -69,7 +71,7 @@
|
||||
</svg>
|
||||
<div class="grid">
|
||||
<span class="label">RanchiMall</span>
|
||||
<h4>Content Collab</h4>
|
||||
<h4>Content Collaboration</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div id="article_name_wrapper" class="flex gap-0-5 align-center justify-center">
|
||||
@ -117,6 +119,19 @@
|
||||
</menu-option>
|
||||
</sm-menu>
|
||||
</div>
|
||||
<div id="selected_content_options" class="flex hide-completely">
|
||||
<button class="button" onclick="formatSelectedContent()">
|
||||
<svg class="icon button__icon--left" xmlns="http://www.w3.org/2000/svg"
|
||||
enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000">
|
||||
<g>
|
||||
<rect fill="none" height="24" width="24" />
|
||||
<path
|
||||
d="M19,3H5C3.89,3,3,3.9,3,5v14c0,1.1,0.89,2,2,2h14c1.1,0,2-0.9,2-2V5C21,3.9,20.11,3,19,3z M19,19H5V7h14V19z M12,10.5 c1.84,0,3.48,0.96,4.34,2.5c-0.86,1.54-2.5,2.5-4.34,2.5S8.52,14.54,7.66,13C8.52,11.46,10.16,10.5,12,10.5 M12,9 c-2.73,0-5.06,1.66-6,4c0.94,2.34,3.27,4,6,4s5.06-1.66,6-4C17.06,10.66,14.73,9,12,9L12,9z M12,14.5c-0.83,0-1.5-0.67-1.5-1.5 s0.67-1.5,1.5-1.5s1.5,0.67,1.5,1.5S12.83,14.5,12,14.5z" />
|
||||
</g>
|
||||
</svg>
|
||||
Preview
|
||||
</button>
|
||||
</div>
|
||||
<theme-toggle></theme-toggle>
|
||||
<button onclick="showPopup('user_popup')">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="icon" height="24px" viewBox="0 0 24 24" width="24px"
|
||||
@ -185,9 +200,77 @@
|
||||
<ul id="version_timeline" class="flex direction-column gap-1-5"></ul>
|
||||
</aside>
|
||||
</article>
|
||||
<article id="preview_page" class="grid page hide-completely">
|
||||
<header class="grid gap-1 full-bleed">
|
||||
<div class="flex align-center space-between">
|
||||
<div class="flex gap-0-5">
|
||||
<button onclick="goHome()">
|
||||
<svg class="icon button__icon--left" xmlns="http://www.w3.org/2000/svg" height="24px"
|
||||
viewBox="0 0 24 24" width="24px" fill="#000000">
|
||||
<path d="M0 0h24v24H0V0z" fill="none" />
|
||||
<path d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z" />
|
||||
</svg>
|
||||
Preview
|
||||
</button>
|
||||
</div>
|
||||
<div class="flex align-center gap-0-5">
|
||||
<sm-button variant="primary">
|
||||
<svg class="icon button__icon--left" xmlns="http://www.w3.org/2000/svg" height="24px"
|
||||
viewBox="0 0 24 24" width="24px" fill="#000000">
|
||||
<path d="M0 0h24v24H0V0z" fill="none" />
|
||||
<path d="M5 4h14v2H5zm0 10h4v6h6v-6h4l-7-7-7 7zm8-2v6h-2v-6H9.83L12 9.83 14.17 12H13z" />
|
||||
</svg>
|
||||
Publish
|
||||
</sm-button>
|
||||
<sm-menu align-options="right">
|
||||
<menu-option>
|
||||
<svg class="icon button__icon--left" xmlns="http://www.w3.org/2000/svg"
|
||||
enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px"
|
||||
fill="#000000">
|
||||
<g>
|
||||
<rect fill="none" height="24" width="24" />
|
||||
</g>
|
||||
<g>
|
||||
<path
|
||||
d="M18,15v3H6v-3H4v3c0,1.1,0.9,2,2,2h12c1.1,0,2-0.9,2-2v-3H18z M17,11l-1.41-1.41L13,12.17V4h-2v8.17L8.41,9.59L7,11l5,5 L17,11z" />
|
||||
</g>
|
||||
</svg>
|
||||
Download as HTML
|
||||
</menu-option>
|
||||
<menu-option onclick="sharePreview()">
|
||||
<svg class="icon button__icon--left" xmlns="http://www.w3.org/2000/svg"
|
||||
enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px"
|
||||
fill="#000000">
|
||||
<g>
|
||||
<rect fill="none" height="24" width="24" />
|
||||
</g>
|
||||
<g>
|
||||
<path
|
||||
d="M16,5l-1.42,1.42l-1.59-1.59V16h-1.98V4.83L9.42,6.42L8,5l4-4L16,5z M20,10v11c0,1.1-0.9,2-2,2H6c-1.11,0-2-0.9-2-2V10 c0-1.11,0.89-2,2-2h3v2H6v11h12V10h-3V8h3C19.1,8,20,8.89,20,10z" />
|
||||
</g>
|
||||
</svg>
|
||||
Share preview
|
||||
</menu-option>
|
||||
</sm-menu>
|
||||
</div>
|
||||
</div>
|
||||
<h1 id="preview__title" class="article__title"></h1>
|
||||
</header>
|
||||
<div id="preview__body"></div>
|
||||
</article>
|
||||
<sm-popup id="article_list_popup">
|
||||
<header slot="header" class="popup__header">
|
||||
<h3>All articles</h3>
|
||||
<div class="flex align-center">
|
||||
<button class="popup__header__close" onclick="hidePopup()">
|
||||
<svg class="icon" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px"
|
||||
fill="#000000">
|
||||
<path d="M0 0h24v24H0V0z" fill="none" />
|
||||
<path
|
||||
d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41z" />
|
||||
</svg>
|
||||
</button>
|
||||
<h3>All articles</h3>
|
||||
</div>
|
||||
<sm-input id="article_list_search" placeholder="Search articles" type="search" autofocus>
|
||||
<svg slot="icon" class="icon" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24"
|
||||
width="24px" fill="#000000">
|
||||
@ -291,6 +374,49 @@
|
||||
<sm-button variant="primary" onclick="saveSectionEdit()">Save</sm-button>
|
||||
</div>
|
||||
</sm-popup>
|
||||
<sm-popup id="contributors_popup">
|
||||
<header slot="header" class="popup__header">
|
||||
<div class="flex align-center">
|
||||
<button class="popup__header__close" onclick="hidePopup()">
|
||||
<svg class="icon" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px"
|
||||
fill="#000000">
|
||||
<path d="M0 0h24v24H0V0z" fill="none" />
|
||||
<path
|
||||
d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41z" />
|
||||
</svg>
|
||||
</button>
|
||||
<h3>Contributors</h3>
|
||||
</div>
|
||||
</header>
|
||||
<div id="contributor_list" class="grid gap-1-5"></div>
|
||||
</sm-popup>
|
||||
<sm-popup id="share_popup">
|
||||
<header slot="header" class="popup__header">
|
||||
<div class="flex align-center">
|
||||
<button class="popup__header__close" onclick="hidePopup()">
|
||||
<svg class="icon" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px"
|
||||
fill="#000000">
|
||||
<path d="M0 0h24v24H0V0z" fill="none" />
|
||||
<path
|
||||
d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41z" />
|
||||
</svg>
|
||||
</button>
|
||||
<h3>Share article preview</h3>
|
||||
</div>
|
||||
</header>
|
||||
<span class="label">Copy link</span>
|
||||
<sm-copy id="shared_url" clip-text></sm-copy>
|
||||
</sm-popup>
|
||||
<template id="contributor_template">
|
||||
<div class="contributor grid">
|
||||
<svg class="icon" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||
d="M14.6243 7.99154C13.9395 8.14279 13.4165 8.69672 13.3047 9.38904C13.2248 9.88365 13.3664 10.3773 13.6735 10.7498L9.29125 15.0941C8.93718 15.4451 8.48852 15.6853 8.00011 15.7854L6.12418 16.1699L9.19811 13.1381C9.53438 12.8064 9.53812 12.265 9.20646 11.9287C8.8748 11.5924 8.33333 11.5887 7.99706 11.9203L4.97835 14.8977L5.47976 12.451C5.50451 12.3303 5.55656 12.2168 5.63191 12.1192L6.49083 11.0073C9.58386 7.00317 14.32 4.72705 19.2553 4.71054L16.3569 7.60884L14.6243 7.99154ZM2.79008 17.0559L3.8042 12.1076C3.88132 11.7313 4.0435 11.3776 4.27833 11.0736L5.13724 9.96172C8.89964 5.09105 14.861 2.53305 20.8954 3.07051C21.5971 3.133 22.2997 3.23735 23 3.38478L17.2133 9.1713L14.9932 9.66167L15.4238 9.91837C15.9039 10.2046 15.9849 10.8668 15.588 11.2603L10.4954 16.3088C9.90529 16.8938 9.15752 17.2941 8.34351 17.461L3.88965 18.3738L2.45572 19.788C2.11945 20.1197 1.57798 20.116 1.24632 19.7797C0.91466 19.4434 0.918397 18.9019 1.25467 18.5703L2.79008 17.0559Z" />
|
||||
</svg>
|
||||
<div class="contributor__id breakable"></div>
|
||||
<time class="contributor__time"></time>
|
||||
</div>
|
||||
</template>
|
||||
<template id="section_template">
|
||||
<text-field class="heading"></text-field>
|
||||
<section class="article-section">
|
||||
@ -304,6 +430,17 @@
|
||||
</template>
|
||||
<template id="content_card_template">
|
||||
<div class="content-card">
|
||||
<div class="content__header flex align-center space-between">
|
||||
<Button class="content__contributors flex align-center" title="Contributors">
|
||||
<svg class="icon" width="24" height="24" style="margin-right: 0.3rem;" viewBox="0 0 24 24"
|
||||
fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||
d="M14.6243 7.99154C13.9395 8.14279 13.4165 8.69672 13.3047 9.38904C13.2248 9.88365 13.3664 10.3773 13.6735 10.7498L9.29125 15.0941C8.93718 15.4451 8.48852 15.6853 8.00011 15.7854L6.12418 16.1699L9.19811 13.1381C9.53438 12.8064 9.53812 12.265 9.20646 11.9287C8.8748 11.5924 8.33333 11.5887 7.99706 11.9203L4.97835 14.8977L5.47976 12.451C5.50451 12.3303 5.55656 12.2168 5.63191 12.1192L6.49083 11.0073C9.58386 7.00317 14.32 4.72705 19.2553 4.71054L16.3569 7.60884L14.6243 7.99154ZM2.79008 17.0559L3.8042 12.1076C3.88132 11.7313 4.0435 11.3776 4.27833 11.0736L5.13724 9.96172C8.89964 5.09105 14.861 2.53305 20.8954 3.07051C21.5971 3.133 22.2997 3.23735 23 3.38478L17.2133 9.1713L14.9932 9.66167L15.4238 9.91837C15.9039 10.2046 15.9849 10.8668 15.588 11.2603L10.4954 16.3088C9.90529 16.8938 9.15752 17.2941 8.34351 17.461L3.88965 18.3738L2.45572 19.788C2.11945 20.1197 1.57798 20.116 1.24632 19.7797C0.91466 19.4434 0.918397 18.9019 1.25467 18.5703L2.79008 17.0559Z" />
|
||||
</svg>
|
||||
<span class="content__author"></span>
|
||||
</Button>
|
||||
<sm-checkbox class="content__checkbox"></sm-checkbox>
|
||||
</div>
|
||||
<div class="content__area"></div>
|
||||
<div class="flex align-center space-between">
|
||||
<div class="content__options grid align-center">
|
||||
@ -325,7 +462,6 @@
|
||||
<span class="content__score">0</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="content__editor"></div>
|
||||
<button class="submit-entry hide-completely">Submit</button>
|
||||
</div>
|
||||
</div>
|
||||
@ -339,7 +475,6 @@
|
||||
<figcaption class="flex"><span class="by" contenteditable="true">Aldous Huxley</span><cite class="citation"
|
||||
contenteditable="true">Brave New World</cite></figcaption>
|
||||
</figure>
|
||||
<p><br></p>
|
||||
</template>
|
||||
<template id="history_entry_template">
|
||||
<li class="history-entry grid gap-1">
|
||||
@ -484,7 +619,9 @@
|
||||
break;
|
||||
case 'article_list_popup':
|
||||
getRef('article_list').innerHTML = ``
|
||||
|
||||
break;
|
||||
case 'contributors_popup':
|
||||
getRef('contributor_list').innerHTML = ''
|
||||
break;
|
||||
}
|
||||
})
|
||||
@ -636,27 +773,43 @@
|
||||
if (searchParams) {
|
||||
const urlSearchParams = new URLSearchParams('?' + searchParams);
|
||||
params = Object.fromEntries(urlSearchParams.entries());
|
||||
pagesData.params = params
|
||||
}
|
||||
if (firstLoad || params.articleID !== pagesData.params.articleID) {
|
||||
if (!params.articleID)
|
||||
params['articleID'] = floGlobals.appObjects.cc.defaultArticle
|
||||
await Promise.all([
|
||||
floCloudAPI.requestObjectData(params.articleID),
|
||||
floCloudAPI.requestGeneralData(`${params.articleID}_gd`)
|
||||
])
|
||||
}
|
||||
switch (pageId) {
|
||||
case 'home':
|
||||
if (!params.articleID)
|
||||
params['articleID'] = floGlobals.appObjects.cc.defaultArticle
|
||||
Promise.all([
|
||||
floCloudAPI.requestObjectData(params.articleID),
|
||||
floCloudAPI.requestGeneralData(`${params.articleID}_gd`)
|
||||
]).then(() => {
|
||||
case 'main_page':
|
||||
if (!currentArticle.id || params.articleID !== pagesData.params.articleID) {
|
||||
hidePopup()
|
||||
render.article(params.articleID)
|
||||
window.history.replaceState('', '', `#/home?articleID=${params.articleID}`)
|
||||
})
|
||||
}
|
||||
window.history.replaceState('', '', `#/home?articleID=${params.articleID}`)
|
||||
getRef('preview__body').innerHTML = ''
|
||||
targetPage = 'main_page'
|
||||
break;
|
||||
case 'preview':
|
||||
case 'preview_page':
|
||||
if (params.uid) {
|
||||
getRef('preview__body').innerHTML = floGlobals.appObjects[params.articleID].preview.content
|
||||
}
|
||||
targetPage = 'preview_page'
|
||||
break
|
||||
}
|
||||
|
||||
document.querySelectorAll('.page').forEach(page => page.classList.add('hide-completely'))
|
||||
getRef(targetPage).classList.remove('hide-completely')
|
||||
if (pagesData.lastPage !== pageId) {
|
||||
pagesData.lastPage = pageId
|
||||
if (!pagesData.openedPages.includes(pageId)) {
|
||||
pagesData.openedPages.push(pageId)
|
||||
}
|
||||
pagesData.params = params
|
||||
}
|
||||
}
|
||||
// class based lazy loading
|
||||
@ -734,6 +887,14 @@
|
||||
this.render()
|
||||
}
|
||||
}
|
||||
function animateTo(element, keyframes, options) {
|
||||
const anime = element.animate(keyframes, { ...options, fill: 'both' })
|
||||
anime.finished.then(() => {
|
||||
anime.commitStyles()
|
||||
anime.cancel()
|
||||
})
|
||||
return anime
|
||||
}
|
||||
</script>
|
||||
<script>
|
||||
/* PatienceDiff
|
||||
@ -777,6 +938,66 @@
|
||||
}
|
||||
</script>
|
||||
<script id="cc">
|
||||
const slideInLeft = [
|
||||
{
|
||||
opacity: 0,
|
||||
transform: 'translateX(1rem)'
|
||||
},
|
||||
{
|
||||
opacity: 1,
|
||||
transform: 'translateX(0)'
|
||||
}
|
||||
]
|
||||
const slideOutLeft = [
|
||||
{
|
||||
opacity: 1,
|
||||
transform: 'translateX(0)'
|
||||
},
|
||||
{
|
||||
opacity: 0,
|
||||
transform: 'translateX(-1rem)'
|
||||
},
|
||||
]
|
||||
const slideInRight = [
|
||||
{
|
||||
opacity: 0,
|
||||
transform: 'translateX(-1rem)'
|
||||
},
|
||||
{
|
||||
opacity: 1,
|
||||
transform: 'translateX(0)'
|
||||
}
|
||||
]
|
||||
const slideOutRight = [
|
||||
{
|
||||
opacity: 1,
|
||||
transform: 'translateX(0)'
|
||||
},
|
||||
{
|
||||
opacity: 0,
|
||||
transform: 'translateX(1rem)'
|
||||
},
|
||||
]
|
||||
const slideInDown = [
|
||||
{
|
||||
opacity: 0,
|
||||
transform: 'translateY(-1rem)'
|
||||
},
|
||||
{
|
||||
opacity: 1,
|
||||
transform: 'translateY(0)'
|
||||
},
|
||||
]
|
||||
const slideOutUp = [
|
||||
{
|
||||
opacity: 1,
|
||||
transform: 'translateY(0)'
|
||||
},
|
||||
{
|
||||
opacity: 0,
|
||||
transform: 'translateY(-1rem)'
|
||||
},
|
||||
]
|
||||
const cc = {
|
||||
createNewArticle() {
|
||||
if (isSubAdmin) {
|
||||
@ -902,13 +1123,14 @@
|
||||
console.log(res)
|
||||
e.target.closest('.submit-entry').classList.add('hide-completely')
|
||||
notify('sent data', 'success')
|
||||
const iterationData = { ...entry, timestamp, editor: myFloID }
|
||||
if (isUniqueEntry) {
|
||||
contentArea.innerHTML = ''
|
||||
currentArticle.sections[sectionID].uniqueEntries.push(entry.origin)
|
||||
currentArticle.uniqueEntries[entry.origin] = { iterations: [{ ...entry, timestamp }] }
|
||||
currentArticle.uniqueEntries[entry.origin] = { iterations: [iterationData] }
|
||||
parentSection.firstElementChild.after(render.contentCard(entry.origin))
|
||||
} else {
|
||||
currentArticle.uniqueEntries[entry.origin].iterations.push({ ...entry, timestamp, editor: myFloID })
|
||||
currentArticle.uniqueEntries[entry.origin].iterations.push(iterationData)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
@ -920,6 +1142,19 @@
|
||||
hideVersionHistory()
|
||||
else
|
||||
showVersionHistory(entryUid)
|
||||
} else if (e.target.closest('.content__contributors')) {
|
||||
const entryUid = e.target.closest('.content-card').dataset.uid
|
||||
const { contributors } = getIterationDetails(entryUid)
|
||||
const frag = document.createDocumentFragment()
|
||||
getRef('contributor_list').innerHTML = ''
|
||||
for (const floID in contributors) {
|
||||
const contributor = getRef('contributor_template').content.cloneNode(true)
|
||||
contributor.querySelector('.contributor__id').textContent = floID
|
||||
contributor.querySelector('.contributor__time').textContent = `Last edited: ${getFormattedTime(contributors[floID])}`
|
||||
frag.append(contributor)
|
||||
}
|
||||
getRef('contributor_list').append(frag)
|
||||
showPopup('contributors_popup')
|
||||
}
|
||||
})
|
||||
getRef('article_wrapper').addEventListener("paste", e => {
|
||||
@ -932,6 +1167,157 @@
|
||||
|
||||
event.preventDefault();
|
||||
})
|
||||
const selectedContent = new Map()
|
||||
let isContentSelected = false
|
||||
getRef('article_wrapper').addEventListener("change", e => {
|
||||
if (e.target.closest('sm-checkbox')) {
|
||||
const contentCard = e.target.closest('.content-card')
|
||||
const contentID = contentCard.dataset.uid
|
||||
contentCard.classList.toggle('selected')
|
||||
if (!isSubAdmin)
|
||||
contentCard.querySelector('.content__area').toggleAttribute('contenteditable')
|
||||
if (selectedContent.has(contentID)) {
|
||||
selectedContent.delete(contentID)
|
||||
} else {
|
||||
const sectionID = contentCard.closest('.article-section').dataset.sectionId
|
||||
const content = DOMPurify.sanitize(contentCard.querySelector('.content__area').innerHTML)
|
||||
selectedContent.set(contentID, { content, sectionID })
|
||||
}
|
||||
const animOptions = {
|
||||
duration: 150,
|
||||
easing: 'ease',
|
||||
}
|
||||
const selectedContentSize = selectedContent.size
|
||||
if (selectedContentSize === 1 && !isContentSelected) {
|
||||
getRef('article_name_wrapper').classList.remove('hide-completely')
|
||||
getRef('selected_content_options').classList.add('hide-completely')
|
||||
animateTo(getRef('article_name_wrapper'), slideOutLeft, animOptions)
|
||||
.onfinish = () => {
|
||||
isContentSelected = true
|
||||
getRef('article_name_wrapper').classList.add('hide-completely')
|
||||
getRef('selected_content_options').classList.remove('hide-completely')
|
||||
animateTo(getRef('selected_content_options'), slideInLeft, animOptions)
|
||||
}
|
||||
} else if (selectedContent.size === 0) {
|
||||
getRef('article_name_wrapper').classList.add('hide-completely')
|
||||
getRef('selected_content_options').classList.remove('hide-completely')
|
||||
animateTo(getRef('selected_content_options'), slideOutRight, animOptions)
|
||||
.onfinish = () => {
|
||||
isContentSelected = false
|
||||
getRef('article_name_wrapper').classList.remove('hide-completely')
|
||||
getRef('selected_content_options').classList.add('hide-completely')
|
||||
animateTo(getRef('article_name_wrapper'), slideInRight, animOptions)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
function formatSelectedContent() {
|
||||
const composedDocumentStructure = {}
|
||||
floGlobals.appObjects[currentArticle.id].sections.forEach(section => composedDocumentStructure[section.id] = [])
|
||||
selectedContent.forEach(({ content, sectionID }, uid) => {
|
||||
composedDocumentStructure[sectionID].push({ content, uid })
|
||||
})
|
||||
for (const section in composedDocumentStructure) {
|
||||
if (!composedDocumentStructure[section].length)
|
||||
delete composedDocumentStructure[section]
|
||||
}
|
||||
let frag = document.createDocumentFragment()
|
||||
|
||||
for (const section in composedDocumentStructure) {
|
||||
frag.append(createElement('h3', {
|
||||
textContent: currentArticle.sections[section].title
|
||||
}))
|
||||
composedDocumentStructure[section].forEach(({ content, uid }) => {
|
||||
const group = createElement('div', {
|
||||
className: 'preview-group',
|
||||
attributes: { 'data-section-id': section, 'data-uid': uid },
|
||||
innerHTML: content
|
||||
})
|
||||
group.append(createElement('div', {
|
||||
className: 'preview-group__buttons flex align-center',
|
||||
innerHTML: `
|
||||
<button class="formatting-button move-up" title="Move up">
|
||||
<svg class="icon" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M4 12l1.41 1.41L11 7.83V20h2V7.83l5.58 5.59L20 12l-8-8-8 8z"/></svg>
|
||||
</button>
|
||||
<button class="formatting-button move-down" title="Move down">
|
||||
<svg class="icon" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M20 12l-1.41-1.41L13 16.17V4h-2v12.17l-5.58-5.59L4 12l8 8 8-8z"/></svg>
|
||||
</button>
|
||||
<button class="formatting-button remove-group" title="Remove from exporting">
|
||||
<svg class="icon" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M7 11v2h10v-2H7zm5-9C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z"/></svg>
|
||||
</button>
|
||||
`
|
||||
}))
|
||||
frag.append(group)
|
||||
})
|
||||
}
|
||||
getRef('preview__title').textContent = floGlobals.appObjects.cc.articleList[currentArticle.id].title
|
||||
getRef('preview__body').innerHTML = ''
|
||||
getRef('preview__body').append(frag)
|
||||
window.location.hash = `#/preview?articleID=${currentArticle.id}`
|
||||
}
|
||||
function goHome() {
|
||||
window.location.hash = `#/home?articleID=${pagesData.params.articleID}`
|
||||
}
|
||||
getRef('preview_page').addEventListener('click', e => {
|
||||
if (e.target.closest('.move-up')) {
|
||||
const currentElement = e.target.closest('.preview-group')
|
||||
const previousSibling = currentElement.previousElementSibling
|
||||
if (previousSibling?.dataset.sectionId === currentElement.dataset.sectionId) {
|
||||
const clone = currentElement.cloneNode(true)
|
||||
previousSibling.before(clone)
|
||||
currentElement.remove()
|
||||
}
|
||||
} else if (e.target.closest('.move-down')) {
|
||||
const currentElement = e.target.closest('.preview-group')
|
||||
const nextSibling = currentElement.nextElementSibling
|
||||
if (nextSibling?.dataset.sectionId === currentElement.dataset.sectionId) {
|
||||
const clone = currentElement.cloneNode(true)
|
||||
nextSibling.after(clone)
|
||||
currentElement.remove()
|
||||
}
|
||||
} else if (e.target.closest('.remove-group')) {
|
||||
const currentElement = e.target.closest('.preview-group')
|
||||
const uid = currentElement.dataset.uid
|
||||
if (currentArticle.id) {
|
||||
getRef('article_wrapper').querySelector(`.content-card[data-uid="${uid}"] sm-checkbox`).checked = false
|
||||
}
|
||||
if (!currentElement.nextElementSibling && currentElement.previousElementSibling.tagName === 'H3') {
|
||||
currentElement.previousElementSibling.remove()
|
||||
}
|
||||
currentElement.remove()
|
||||
}
|
||||
})
|
||||
function sharePreview() {
|
||||
if (isSubAdmin) {
|
||||
if (floGlobals.appObjects[pagesData.params.articleID].preview.uid) {
|
||||
floGlobals.appObjects[pagesData.params.articleID].preview.content = DOMPurify.sanitize(getRef('preview__body').innerHTML)
|
||||
} else {
|
||||
floGlobals.appObjects[pagesData.params.articleID].preview = {
|
||||
uid: floCrypto.randString(16, true),
|
||||
content: DOMPurify.sanitize(getRef('preview__body').innerHTML)
|
||||
}
|
||||
}
|
||||
floCloudAPI.updateObjectData(pagesData.params.articleID)
|
||||
.then(() => {
|
||||
notify('Created preview', 'success')
|
||||
const uid = floGlobals.appObjects[pagesData.params.articleID].preview.uid
|
||||
history.replaceState(null, null, `#/preview?articleID=${pagesData.params.articleID}&uid=${uid}`)
|
||||
if (window.navigator.share) {
|
||||
navigator.share({
|
||||
title: "Share article preview",
|
||||
url: window.location.href
|
||||
})
|
||||
} else {
|
||||
getRef('shared_url').value = window.location.href
|
||||
showPopup('share_popup')
|
||||
}
|
||||
})
|
||||
.catch(err => notify(err, 'error'))
|
||||
} else {
|
||||
notify('This action requires sub-admin privileges', 'error')
|
||||
}
|
||||
}
|
||||
|
||||
getRef('current_article_title').addEventListener("change", e => {
|
||||
floGlobals.appObjects.cc.articleList[currentArticle.id].title = e.target.value.trim()
|
||||
floCloudAPI.updateObjectData('cc')
|
||||
@ -1032,18 +1418,19 @@
|
||||
clone.querySelector('.content__area').setAttribute('contentEditable', true)
|
||||
}
|
||||
const { data, contributors } = getIterationDetails(id)
|
||||
console.log(contributors)
|
||||
clone.querySelector('.content__area').innerHTML = DOMPurify.sanitize(data)
|
||||
if (contributors.size === 1) {
|
||||
const [contributor] = contributors
|
||||
clone.querySelector('.content__editor').textContent = `by ${contributor}`;
|
||||
let noOfContributors = 0
|
||||
let firstContributor
|
||||
for (const contributor in contributors) {
|
||||
noOfContributors++
|
||||
if (noOfContributors === 1)
|
||||
firstContributor = contributor
|
||||
if (noOfContributors === 2) break
|
||||
}
|
||||
if (noOfContributors === 1) {
|
||||
clone.querySelector('.content__author').textContent = firstContributor
|
||||
} else {
|
||||
clone.querySelector('.content__editor').innerHTML = `
|
||||
<svg class="icon" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.6243 7.99154C13.9395 8.14279 13.4165 8.69672 13.3047 9.38904C13.2248 9.88365 13.3664 10.3773 13.6735 10.7498L9.29125 15.0941C8.93718 15.4451 8.48852 15.6853 8.00011 15.7854L6.12418 16.1699L9.19811 13.1381C9.53438 12.8064 9.53812 12.265 9.20646 11.9287C8.8748 11.5924 8.33333 11.5887 7.99706 11.9203L4.97835 14.8977L5.47976 12.451C5.50451 12.3303 5.55656 12.2168 5.63191 12.1192L6.49083 11.0073C9.58386 7.00317 14.32 4.72705 19.2553 4.71054L16.3569 7.60884L14.6243 7.99154ZM2.79008 17.0559L3.8042 12.1076C3.88132 11.7313 4.0435 11.3776 4.27833 11.0736L5.13724 9.96172C8.89964 5.09105 14.861 2.53305 20.8954 3.07051C21.5971 3.133 22.2997 3.23735 23 3.38478L17.2133 9.1713L14.9932 9.66167L15.4238 9.91837C15.9039 10.2046 15.9849 10.8668 15.588 11.2603L10.4954 16.3088C9.90529 16.8938 9.15752 17.2941 8.34351 17.461L3.88965 18.3738L2.45572 19.788C2.11945 20.1197 1.57798 20.116 1.24632 19.7797C0.91466 19.4434 0.918397 18.9019 1.25467 18.5703L2.79008 17.0559Z"/>
|
||||
</svg>
|
||||
|
||||
`
|
||||
clone.querySelector('.content__author').textContent = `${noOfContributors} contributors`
|
||||
}
|
||||
// clone.querySelector('.content__score').textContent = score;
|
||||
return clone
|
||||
@ -1062,15 +1449,19 @@
|
||||
removedAt.forEach(place => changed[place] = { added: false, content: changed[place] })
|
||||
const final = changed.map((word, index) => {
|
||||
if (word.hasOwnProperty('added')) {
|
||||
let consecutiveWords = [word.content],
|
||||
let consecutiveWords = [],
|
||||
i
|
||||
const type = word.added
|
||||
for (i = index + 1; (changed[i + 1] && changed[i + 1].hasOwnProperty('added') && changed[i + 1].added === type); i++) {
|
||||
for (i = index; (changed[i] && changed[i].hasOwnProperty('added') && changed[i].added === type); i++) {
|
||||
consecutiveWords.push(changed[i].content)
|
||||
}
|
||||
if (i - index > 1)
|
||||
changed.splice((index + 1), (i - index - 1))
|
||||
return `<s${type ? 'pan' : ''} class="${type ? 'added' : 'removed'}">${consecutiveWords.join(' ')}</s${type ? 'pan' : ''}>`
|
||||
if (type) {
|
||||
return `<span class="added">${consecutiveWords.join(' ')}</span>`
|
||||
} else {
|
||||
return `<s class="removed">${consecutiveWords.join(' ')}</s>`
|
||||
}
|
||||
} else return word
|
||||
})
|
||||
clone.querySelector('.entry__changes').innerHTML = DOMPurify.sanitize(final.join(' '))
|
||||
@ -1174,37 +1565,42 @@
|
||||
})
|
||||
|
||||
function saveSectionEdit() {
|
||||
const newSections = []
|
||||
getRef('section_list_container').querySelectorAll('.section-card--new').forEach(section => {
|
||||
const title = section.querySelector('input').value.trim()
|
||||
if (title !== '')
|
||||
newSections.push({
|
||||
id: floCrypto.randString(16, true),
|
||||
title
|
||||
})
|
||||
})
|
||||
if (newSections.length) {
|
||||
newSections.forEach(elem => {
|
||||
floGlobals.appObjects[currentArticle.id].sections.push(elem)
|
||||
})
|
||||
floCloudAPI.updateObjectData(currentArticle.id)
|
||||
.then((res) => {
|
||||
const frag = document.createDocumentFragment()
|
||||
const currentSectionCount = getRef('section_list_container').querySelectorAll('.section-card:not(.section-card--new)').length
|
||||
let index = currentSectionCount
|
||||
newSections.forEach(elem => {
|
||||
const { title, id } = elem
|
||||
currentArticle.sections[id] = { title, uniqueEntries: [] }
|
||||
frag.append(render.section(id, currentArticle.sections[id], index))
|
||||
index += 1
|
||||
if (isSubAdmin) {
|
||||
const newSections = []
|
||||
getRef('section_list_container').querySelectorAll('.section-card--new').forEach(section => {
|
||||
const title = section.querySelector('input').value.trim()
|
||||
if (title !== '')
|
||||
newSections.push({
|
||||
id: floCrypto.randString(16, true),
|
||||
title
|
||||
})
|
||||
getRef('article_wrapper').append(frag)
|
||||
notify('Sections updated', 'success')
|
||||
hidePopup()
|
||||
})
|
||||
.catch(err => {
|
||||
notify(err, 'error')
|
||||
})
|
||||
if (newSections.length) {
|
||||
newSections.forEach(elem => {
|
||||
floGlobals.appObjects[currentArticle.id].sections.push(elem)
|
||||
})
|
||||
floCloudAPI.updateObjectData(currentArticle.id)
|
||||
.then((res) => {
|
||||
const frag = document.createDocumentFragment()
|
||||
const currentSectionCount = getRef('section_list_container').querySelectorAll('.section-card:not(.section-card--new)').length
|
||||
let index = currentSectionCount
|
||||
newSections.forEach(elem => {
|
||||
const { title, id } = elem
|
||||
currentArticle.sections[id] = { title, uniqueEntries: [] }
|
||||
frag.append(render.section(id, currentArticle.sections[id], index))
|
||||
index += 1
|
||||
})
|
||||
getRef('article_wrapper').append(frag)
|
||||
notify('Sections updated', 'success')
|
||||
hidePopup()
|
||||
})
|
||||
.catch(err => {
|
||||
notify(err, 'error')
|
||||
})
|
||||
}
|
||||
} else {
|
||||
hidePopup()
|
||||
notify('This action requires sub-admin privileges', 'error')
|
||||
}
|
||||
}
|
||||
|
||||
@ -1254,12 +1650,12 @@
|
||||
|
||||
function getIterationDetails(uid, targetIndex) {
|
||||
let merged
|
||||
const contributors = new Set()
|
||||
const contributors = {}
|
||||
const limit = targetIndex || currentArticle.uniqueEntries[uid].iterations.length - 1
|
||||
for (let i = 0; i <= limit; i++) {
|
||||
const { data, editor } = currentArticle.uniqueEntries[uid].iterations[i]
|
||||
const { data, editor, timestamp } = currentArticle.uniqueEntries[uid].iterations[i]
|
||||
merged = i ? updateString(merged, data) : data
|
||||
contributors.add(editor)
|
||||
contributors[editor] = timestamp
|
||||
}
|
||||
return {
|
||||
data: merged,
|
||||
@ -1489,7 +1885,7 @@
|
||||
await Promise.all([
|
||||
floCloudAPI.requestObjectData('cc'),
|
||||
])
|
||||
showPage(window.location.hash)
|
||||
showPage(window.location.hash, { firstLoad: true })
|
||||
|
||||
console.log(result)
|
||||
// alert(`Welcome FLO_ID: ${ myFloID }`)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user