Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions nx/blocks/form/editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { LitElement, html, nothing } from 'da-lit';
import { createEngine } from '../../deps/da-sc-sdk/dist/index.js';
import { loadFormContext } from './utils/context.js';
import { attachPersistence } from './utils/persistence.js';
import { openMediaPreview, selectImageSource } from './utils/assets.js';
import { loadStyle, hashChange } from '../../../nx2/utils/utils.js';

import './views/editor.js';
Expand Down Expand Up @@ -41,13 +42,24 @@ function ctxFromHashState(state) {
return { org: state.org, repo: state.site, path: state.fullpath.slice(1) };
}

async function loadAemRepoConfig({ owner, repo }) {
try {
const { getRepositoryConfig } = await import('../../../nx2/utils/aem-assets/repository-config.js');
return await getRepositoryConfig(owner, repo);
} catch {
return null;
}
}

class Form extends LitElement {
static properties = {
ctx: { attribute: false },
_context: { state: true },
_state: { state: true },
_nav: { state: true },
_pendingSchemaId: { state: true },
_aemRepoConfig: { state: true },
_mediaPreviewOrigin: { state: true },
};

// Reactive properties (declared in static properties) must NOT have class-
Expand Down Expand Up @@ -81,6 +93,30 @@ class Form extends LitElement {
};
};

async _selectFromSource({ source, details }) {
if (source === 'upload') {
return selectImageSource({ details });
}
if (source === 'aem-assets' && this._aemRepoConfig) {
const { selectAemAsset } = await import('./utils/aem-selector.js');
return selectAemAsset({ repoConfig: this._aemRepoConfig });
}
throw new Error(`The "${source}" asset source is not available for this form.`);
}

_onSelectAsset = async ({ source }) => {
const { _details: details, _loadVersion: version } = this;
if (!details) {
throw new Error('The form document is not ready for asset selection.');
}

const result = await this._selectFromSource({ source, details });
if (version !== this._loadVersion) {
return { cancelled: true };
}
return result;
};

connectedCallback() {
super.connectedCallback();
this.shadowRoot.adoptedStyleSheets = [style];
Expand Down Expand Up @@ -142,6 +178,8 @@ class Form extends LitElement {
this._loadVersion += 1;
const version = this._loadVersion;
this._pendingSchemaId = '';
this._aemRepoConfig = null;
this._mediaPreviewOrigin = undefined;
this._state = null;
this._editor = null;
this._persistence?.detach();
Expand All @@ -163,6 +201,19 @@ class Form extends LitElement {
if (context.status === 'ready') {
this._start({ schema: context.schema, json: context.json });
}

if (context.status === 'ready' || context.status === 'select-schema') {
const { owner, repo } = this._details;
const [repoConfig, previewOrigin] = await Promise.all([
loadAemRepoConfig({ owner, repo }),
openMediaPreview({ owner, repo }),
]);
if (version !== this._loadVersion) {
return;
}
this._aemRepoConfig = repoConfig;
this._mediaPreviewOrigin = previewOrigin;
}
}

_onPendingSchemaChange(e) {
Expand Down Expand Up @@ -359,6 +410,11 @@ class Form extends LitElement {
.state=${this._state}
.nav=${this._nav}
.onSelect=${this._onSelect}
.assetContext=${{
onSelectSource: this._onSelectAsset,
previewOrigin: this._mediaPreviewOrigin,
aemAssetsAvailable: !!this._aemRepoConfig,
}}
></nx-editor>
<nx-preview .state=${this._state}></nx-preview>
</div>
Expand Down
276 changes: 276 additions & 0 deletions nx/blocks/form/fields/asset.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,276 @@
:host {
display: block;
font-family: var(--s2-font-family, adobe-clean, "Source Sans Pro", system-ui, sans-serif);

--asset-preview-height: 194px;
--asset-dialog-max-width: 620px;
--asset-dialog-single-width: 420px;
--asset-source-min-height: 174px;
}

.asset {
overflow: hidden;
border: 1px solid var(--s2-gray-300);
border-radius: var(--s2-corner-radius-500, 12px);
background: var(--s2-gray-25);
}

.asset-empty {
display: flex;
flex-direction: column;
align-items: center;
gap: 12px;
padding: 32px 16px;
text-align: center;
}

.asset-empty[hidden],
.asset-selected[hidden] {
display: none;
}

.asset-empty p {
margin: 0;
color: var(--s2-gray-700);
font-size: var(--s2-body-size-m, 16px);
font-weight: 600;
}

.asset-empty-icon,
.asset-file-icon,
.asset-source-icon {
display: inline-flex;
align-items: center;
justify-content: center;
color: var(--s2-blue-800);
background: light-dark(var(--s2-blue-100), var(--s2-blue-300));
}

.asset-empty-icon {
width: 52px;
height: 52px;
border-radius: var(--s2-corner-radius-500, 12px);
}

.asset-empty-icon svg,
.asset-file-icon svg,
.asset-source-icon svg {
width: 20px;
height: 20px;
}

.asset-preview {
position: relative;
display: flex;
align-items: center;
justify-content: center;
height: var(--asset-preview-height);
background: var(--s2-gray-100);
}

.asset-preview img {
--transparency-grid-size: 8px;
--transparency-grid-color-a: light-dark(#fff, #3a3a3a);
--transparency-grid-color-b: light-dark(#ccc, #505050);

width: 100%;
height: 100%;
object-fit: cover;
opacity: 0;
transition: opacity 130ms ease-out;
background-color: var(--transparency-grid-color-a);
background-image: conic-gradient(
var(--transparency-grid-color-b) 90deg, transparent 90deg 180deg,
var(--transparency-grid-color-b) 180deg 270deg, transparent 270deg
);
background-size: var(--transparency-grid-size) var(--transparency-grid-size);
}

.asset-preview img.is-loaded {
opacity: 1;
}

.asset-preview img[hidden] {
display: none;
}

.asset-preview-placeholder {
color: var(--s2-gray-600);
}

.asset-preview-placeholder svg {
width: 28px;
height: 28px;
}

.asset-actions {
position: absolute;
top: 12px;
right: 12px;
display: flex;
gap: 8px;
}

.asset-actions form-button {
display: inline-flex;
border-radius: 16px;
background: var(--s2-gray-25);
box-shadow: 0 2px 9px rgb(0 0 0 / 20%);
}

.asset-info {
display: flex;
align-items: center;
gap: 10px;
min-width: 0;
padding: 12px 16px;
}

.asset-file-icon {
flex: 0 0 32px;
height: 32px;
border-radius: var(--s2-corner-radius-400, 8px);
}

.asset-name {
overflow-wrap: anywhere;
color: var(--s2-gray-900);
font-size: var(--s2-body-size-s, 14px);
font-weight: 600;
}

.asset-progress {
margin: 4px 0 0;
color: var(--s2-gray-700);
font-size: var(--s2-component-s-regular-font-size, 12px);
line-height: 1.5;
}

dialog {
box-sizing: border-box;
width: min(var(--asset-dialog-max-width), calc(100vw - 32px));
max-height: calc(100vh - 32px);
overflow-y: auto;
padding: 0;
border: 1px solid var(--s2-gray-300);
border-radius: var(--s2-corner-radius-800, 16px);
background: var(--s2-gray-25);
color: var(--s2-gray-900);
box-shadow: 0 18px 55px rgb(0 0 0 / 20%);
font: inherit;
}

.asset-dialog-single,
.asset-remove-dialog {
width: min(var(--asset-dialog-single-width), calc(100vw - 32px));
}

dialog::backdrop {
background: rgb(13 16 35 / 55%);
}

.asset-dialog-header {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 24px;
padding: 24px 28px 12px;
}

.asset-remove-dialog .asset-dialog-header {
padding-bottom: 20px;
}

.asset-dialog-header h2 {
margin: 0 0 7px;
font-size: var(--s2-heading-size-s, 20px);
}

.asset-dialog-header p,
.source-description,
.asset-dialog-footer {
margin: 0;
color: var(--s2-gray-700);
font-size: var(--s2-body-size-s, 14px);
line-height: 1.5;
}

.asset-dialog-close {
padding: 2px 8px;
border: none;
border-radius: var(--s2-corner-radius-400, 8px);
background: transparent;
color: var(--s2-gray-700);
font-size: 24px;
cursor: pointer;
}

.asset-dialog-close:hover {
background: var(--s2-gray-100);
}

.asset-sources {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 12px;
padding: 14px 28px 26px;
}

.asset-dialog-single .asset-sources {
grid-template-columns: 1fr;
}

.asset-source {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 9px;
min-height: var(--asset-source-min-height);
padding: 21px;
border: 1px solid var(--s2-gray-300);
border-radius: var(--s2-corner-radius-500, 12px);
background: var(--s2-gray-50);
color: var(--s2-gray-900);
font: inherit;
text-align: left;
cursor: pointer;
}

.asset-dialog-single .asset-source {
min-height: 0;
}

.asset-source:hover,
.asset-source:focus-visible {
border-color: var(--s2-blue-800);
background: light-dark(var(--s2-blue-100), var(--s2-blue-300));
}

.asset-source-icon {
width: 42px;
height: 42px;
border-radius: var(--s2-corner-radius-400, 8px);
}

.source-title {
font-size: var(--s2-body-size-m, 16px);
font-weight: 700;
}

.asset-dialog-footer {
display: flex;
justify-content: flex-end;
gap: 8px;
padding: 12px 28px 16px;
border-top: 1px solid var(--s2-gray-300);
}

@media (width <= 550px) {
.asset-sources {
grid-template-columns: 1fr;
}

.asset-source {
min-height: 0;
}
}
Loading
Loading