Add live preview of project card stuff

This commit is contained in:
Ben Visness 2024-07-02 22:11:41 -05:00
parent 51ad8d03d4
commit 22c70cda8e
7 changed files with 118 additions and 39 deletions

View File

@ -1,4 +1,7 @@
function ImageSelector(form, maxFileSize, container, defaultImageUrl) { function ImageSelector(form, maxFileSize, container, {
defaultImageUrl = "",
onUpdate = (url) => {},
} = {}) {
this.form = form; this.form = form;
this.maxFileSize = maxFileSize; this.maxFileSize = maxFileSize;
this.fileInput = container.querySelector(".imginput"); this.fileInput = container.querySelector(".imginput");
@ -11,12 +14,13 @@ function ImageSelector(form, maxFileSize, container, defaultImageUrl) {
this.originalImageUrl = this.imageEl.getAttribute("data-imginput-original"); this.originalImageUrl = this.imageEl.getAttribute("data-imginput-original");
this.originalImageFilename = this.imageEl.getAttribute("data-imginput-original-filename"); this.originalImageFilename = this.imageEl.getAttribute("data-imginput-original-filename");
this.currentImageUrl = this.originalImageUrl; this.currentImageUrl = this.originalImageUrl;
this.defaultImageUrl = defaultImageUrl || ""; this.defaultImageUrl = defaultImageUrl;
this.onUpdate = onUpdate;
this.fileInput.value = ""; this.fileInput.value = "";
this.removeImageInput.value = ""; this.removeImageInput.value = "";
this.setImageUrl(this.originalImageUrl); this.setImageUrl(this.originalImageUrl, true);
this.updatePreview(); this.updatePreview();
this.fileInput.addEventListener("change", function(ev) { this.fileInput.addEventListener("change", function(ev) {
@ -79,7 +83,7 @@ ImageSelector.prototype.setError = function(error) {
this.fileInput.reportValidity(); this.fileInput.reportValidity();
} }
ImageSelector.prototype.setImageUrl = function(url) { ImageSelector.prototype.setImageUrl = function(url, initial = false) {
this.currentImageUrl = url; this.currentImageUrl = url;
this.imageEl.src = url; this.imageEl.src = url;
if (url.length > 0) { if (url.length > 0) {
@ -87,6 +91,10 @@ ImageSelector.prototype.setImageUrl = function(url) {
} else { } else {
this.imageEl.style.display = "none"; this.imageEl.style.display = "none";
} }
this.url = url;
if (!initial) {
this.onUpdate(url);
}
}; };
ImageSelector.prototype.updatePreview = function(file) { ImageSelector.prototype.updatePreview = function(file) {

View File

@ -8501,6 +8501,17 @@ span.icon-rss::before {
content: "\200b"; content: "\200b";
padding: var(--spacing-2) 0; padding: var(--spacing-2) 0;
} }
.project-homepage-card {
width: 100%;
max-width: var(--site-width-narrow);
padding: var(--spacing-3);
background-color: var(--c-transparent-background);
--link-color: var(--color);
display: flex;
gap: var(--spacing-3);
overflow: hidden;
margin-top: -4rem;
}
/* src/rawdata/scss/showcase.css */ /* src/rawdata/scss/showcase.css */
.showcase-item .gradient { .showcase-item .gradient {

View File

@ -94,4 +94,17 @@
content: '\200b'; content: '\200b';
padding: var(--spacing-2) 0; padding: var(--spacing-2) 0;
} }
}
.project-homepage-card {
width: 100%;
max-width: var(--site-width-narrow);
padding: var(--spacing-3);
background-color: var(--c-transparent-background);
--link-color: var(--color);
display: flex;
gap: var(--spacing-3);
overflow: hidden;
margin-top: -4rem;
} }

View File

@ -7,7 +7,7 @@
{{ $filename := or (and .Asset .Asset.Filename) "" }} {{ $filename := or (and .Asset .Asset.Filename) "" }}
<img class="w4 flex-shrink-0" data-imginput-original="{{ $url }}" data-imginput-original-filename="{{ $filename }}" src="{{ $url }}" /> <img class="w4 flex-shrink-0" data-imginput-original="{{ $url }}" data-imginput-original-filename="{{ $filename }}" src="{{ $url }}" />
<div class="w1 flex-grow-1 flex flex-column g1"> <div class="w1 flex-grow-1 flex flex-column g1">
<div class="flex f6"> <div class="flex f6 g2">
<a href="javascript:;" class="imginput-reset-link">Reset</a> <a href="javascript:;" class="imginput-reset-link">Reset</a>
<a href="javascript:;" class="imginput-remove-link">Remove</a> <a href="javascript:;" class="imginput-remove-link">Remove</a>
</div> </div>

View File

@ -157,26 +157,22 @@
{{ else }} {{ else }}
<div class="pv4 f6 tc">You are not following anything. Follow users and projects to see their posts here.</div> <div class="pv4 f6 tc">You are not following anything. Follow users and projects to see their posts here.</div>
{{ end }} {{ end }}
TODO: READ MORE LINK
</div> </div>
{{ end }} {{ end }}
<div data-tab="featured" class="timeline"> <div data-tab="featured" class="timeline">
{{ range .FeaturedItems }} {{ range .FeaturedItems }}
{{ template "timeline_item.html" . }} {{ template "timeline_item.html" . }}
{{ end }} {{ end }}
TODO: READ MORE LINK
</div> </div>
<div data-tab="recent" class="timeline"> <div data-tab="recent" class="timeline">
{{ range .RecentItems }} {{ range .RecentItems }}
{{ template "timeline_item.html" . }} {{ template "timeline_item.html" . }}
{{ end }} {{ end }}
TODO: READ MORE LINK
</div> </div>
<div data-tab="news" class="timeline"> <div data-tab="news" class="timeline">
{{ range .NewsItems }} {{ range .NewsItems }}
{{ template "timeline_item.html" . }} {{ template "timeline_item.html" . }}
{{ end }} {{ end }}
TODO: READ MORE LINK
</div> </div>
</div> </div>
</div> </div>

View File

@ -49,12 +49,24 @@
<div class="flex flex-column g3"> <div class="flex flex-column g3">
<div class="input-group"> <div class="input-group">
<label>Project Title*</label> <label>Project Title*</label>
<input required type="text" name="project_name" maxlength="255" class="textbox" value="{{ .ProjectSettings.Name }}"> <input
required
type="text"
id="project_name" name="project_name"
maxlength="255"
value="{{ .ProjectSettings.Name }}"
oninput="updateCardPreview()"
>
</div> </div>
<div class="input-group"> <div class="input-group">
<label>Short Description*</label> <label>Short Description*</label>
<textarea id="description" required maxlength="140" name="shortdesc"> <textarea
required
id="description" name="shortdesc"
maxlength="140"
oninput="updateCardPreview()"
>
{{- .ProjectSettings.Blurb -}} {{- .ProjectSettings.Blurb -}}
</textarea> </textarea>
<div class="f6">Plaintext only. No links or markdown.</div> <div class="f6">Plaintext only. No links or markdown.</div>
@ -215,6 +227,13 @@
<!-- need href and title --> <!-- need href and title -->
<a data-tmpl="root" class="ph2 flex"><!-- need icon --></a> <a data-tmpl="root" class="ph2 flex"><!-- need icon --></a>
</template> </template>
<template id="owner_preview">
<div class="flex g1 items-center b">
<img data-tmpl="avatar" class="avatar avatar-user avatar-small"><!-- need src -->
<span data-tmpl="name"><!-- need name --></span>
</div>
</template>
<div hidden> <div hidden>
{{ range .AllLogos }} {{ range .AllLogos }}
<span id="link-icon-{{ .Name }}">{{ .Svg }}</span> <span id="link-icon-{{ .Name }}">{{ .Svg }}</span>
@ -226,17 +245,43 @@
We don't have a good story for sharing templates between Go and JS. We don't have a good story for sharing templates between Go and JS.
--> -->
<!-- Header image / links --> <div class="flex flex-column items-center">
<div id="header_img_preview" class="project-header-img"><!-- Needs background-image --> <!-- Header image / links -->
<div class="flex justify-end pa3"> <div id="header_img_preview" class="project-header-img"><!-- Needs background-image -->
<div class="flex g3"> <div class="flex justify-end pa3">
<div id="primary_links_preview" class="project-links hide-if-empty"></div> <div class="flex g3">
<div id="secondary_links_preview" class="project-links ph1 hide-if-empty"></div> <div id="primary_links_preview" class="project-links hide-if-empty"></div>
<div id="secondary_links_preview" class="project-links ph1 hide-if-empty"></div>
</div>
</div>
</div>
<!-- Card -->
<div class="project-homepage-card">
<div id="logo_preview" class="flex-shrink-0 flex">
<img class="project-card-logo" alt="Project Logo">
</div>
<div class="details flex-grow-1">
<h3 id="name_preview" class="b mb2 f4"></h3>
<div id="blurb_preview" class="blurb"></div>
<div id="owners_preview_container">
<hr class="mv3">
<div id="owners_preview" class="flex flex-wrap g2">
<!-- TODO(redesign): Actually preview owners -->
<div class="flex g1 items-center b">
<div class="avatar avatar-user avatar-small"></div>
<span>Example User</span>
</div>
</div>
</div>
<!-- TODO(redesign): Preview badges -->
</div> </div>
</div> </div>
</div> </div>
<!-- Long description preview --> <!-- Long description preview -->
<h3 class="pt4 f4">About {{ .Project.Name }}</h3>
<hr class="mv3">
<div id="desc_preview" class="w-100 post-content"></div> <div id="desc_preview" class="w-100 post-content"></div>
</div> </div>
</div> </div>
@ -247,14 +292,6 @@
let projectForm = document.querySelector("#project_form"); let projectForm = document.querySelector("#project_form");
projectForm.addEventListener("invalid", function(ev) {
switchToTabOfElement(document.body, ev.target);
}, true);
function gotoTab(tabName) {
switchTab(document.body, tabName);
}
////////// //////////
// Tags // // Tags //
////////// //////////
@ -302,6 +339,7 @@
let ownersError = document.querySelector("#owners_error"); let ownersError = document.querySelector("#owners_error");
let ownerList = document.querySelector("#owner_list"); let ownerList = document.querySelector("#owner_list");
let ownerTemplate = makeTemplateCloner("owner_row"); let ownerTemplate = makeTemplateCloner("owner_row");
let ownerPreviewTemplate = makeTemplateCloner("owner_preview");
addOwnerInput.addEventListener("keypress", function(ev) { addOwnerInput.addEventListener("keypress", function(ev) {
if (ev.which == 13) { if (ev.which == 13) {
@ -394,38 +432,52 @@
updateAddOwnerStyles(); updateAddOwnerStyles();
}); });
///////////////////// //////////////////////////////
// Logo management // // Logo / header management //
///////////////////// //////////////////////////////
const logoMaxFileSize = {{ .LogoMaxFileSize }}; const logoMaxFileSize = {{ .LogoMaxFileSize }};
const headerMaxFileSize = {{ .HeaderMaxFileSize }};
const logoSelector = new ImageSelector( const logoSelector = new ImageSelector(
document.querySelector("#project_form"), document.querySelector("#project_form"),
logoMaxFileSize, logoMaxFileSize,
document.querySelector(".light_logo") document.querySelector(".light_logo"),
{
onUpdate() {
updateCardPreview();
},
},
); );
function openLogoSelector(e) { function openLogoSelector(e) {
e.preventDefault(); e.preventDefault();
logoSelector.openFileInput(); logoSelector.openFileInput();
} }
///////////////////////
// Header management //
///////////////////////
const headerMaxFileSize = {{ .HeaderMaxFileSize }};
const headerSelector = new ImageSelector( const headerSelector = new ImageSelector(
document.querySelector("#project_form"), document.querySelector("#project_form"),
headerMaxFileSize, headerMaxFileSize,
document.querySelector(".header_image") document.querySelector(".header_image"),
{
onUpdate() {
updateCardPreview();
},
},
); );
function openHeaderSelector(e) { function openHeaderSelector(e) {
e.preventDefault(); e.preventDefault();
headerSelector.openFileInput(); headerSelector.openFileInput();
} }
function updateCardPreview() {
document.querySelector("#logo_preview").hidden = !logoSelector.url;
document.querySelector("#logo_preview img").src = logoSelector.url;
document.querySelector("#header_img_preview").style.backgroundImage = `url(${headerSelector.url})`;
document.querySelector("#name_preview").innerText = document.querySelector("#project_name").value;
document.querySelector("#blurb_preview").innerText = document.querySelector("#description").value;
}
updateCardPreview();
////////////////// //////////////////
// Asset upload // // Asset upload //
////////////////// //////////////////

View File

@ -14,7 +14,7 @@
<div class="flex justify-between pa3"> <div class="flex justify-between pa3">
<div class="flex g3"> <div class="flex g3">
{{ if .CanEdit }} {{ if .CanEdit }}
<div class="bg-transparent flex"> <div class="project-links">
<a class="ph3 pv2 flex items-center" href="{{ .EditUrl }}"><span class="mr2 flex items-center">{{ svg "edit-line" }}</span>Edit Project</a> <a class="ph3 pv2 flex items-center" href="{{ .EditUrl }}"><span class="mr2 flex items-center">{{ svg "edit-line" }}</span>Edit Project</a>
</div> </div>
{{ end }} {{ end }}
@ -37,7 +37,7 @@
</div> </div>
</div> </div>
</div> </div>
<div class="w-100 mw-site-narrow pa3 bg-transparent link-normal flex g3 overflow-hidden" style="margin-top: -4rem"> <div class="project-homepage-card">
{{ if .Project.Logo }} {{ if .Project.Logo }}
<a class="flex-shrink-0 flex" href="{{ .Project.Url }}"> <a class="flex-shrink-0 flex" href="{{ .Project.Url }}">
<img class="project-card-logo" src="{{ .Project.Logo }}" alt="Project Logo"> <img class="project-card-logo" src="{{ .Project.Logo }}" alt="Project Logo">
@ -127,7 +127,6 @@
{{ range .RecentActivity }} {{ range .RecentActivity }}
{{ template "timeline_item.html" . }} {{ template "timeline_item.html" . }}
{{ end }} {{ end }}
TODO: READ MORE LINK
</div> </div>
</div> </div>
</div> </div>