diff --git a/.github/workflows/lint_test.yaml b/.github/workflows/lint_test.yaml index 777c564bc..f27e35504 100644 --- a/.github/workflows/lint_test.yaml +++ b/.github/workflows/lint_test.yaml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - node-version: [24] + node-version: [22] steps: - name: Checkout repository uses: actions/checkout@v4 diff --git a/blocks/browse/browse.css b/blocks/browse/browse.css new file mode 100644 index 000000000..dc76ba16b --- /dev/null +++ b/blocks/browse/browse.css @@ -0,0 +1,30 @@ +.light-scheme { + .browse:has(da-sites) { + [data-scheme="light"] { + display: block; + } + } +} + +.dark-scheme { + .browse:has(da-sites) { + [data-scheme="dark"] { + display: block; + } + } +} + +picture { + display: none; + position: absolute; + left: 0; + right: 0; + top: 0; +} + +img { + display: block; + width: 100%; + height: auto; + mask-image: linear-gradient(to bottom, rgb(0 0 0) 50%, transparent 100%); +} diff --git a/blocks/browse/browse.js b/blocks/browse/browse.js index 2ecb9b5c8..ee3275451 100644 --- a/blocks/browse/browse.js +++ b/blocks/browse/browse.js @@ -1,54 +1,43 @@ -import getPathDetails from '../shared/pathDetails.js'; +import { getNx } from '../../scripts/utils.js'; -// Preload Lit -import('../../deps/lit/dist/index.js'); +const { hashChange, loadStyle } = await import(`${getNx()}/utils/utils.js`); -async function loadComponent(el, cmpName, details) { - el.innerHTML = ''; +const styles = await loadStyle(import.meta.url); +document.adoptedStyleSheets.push(styles); + +async function loadComponent(el, cmpName, pathDetails) { + const existing = el.querySelector(cmpName); + if (existing && pathDetails) { + existing.details = pathDetails; + return; + } + // Swapping views — remove whichever component is currently mounted. + el.querySelector('da-sites, da-browse')?.remove(); await import(`./${cmpName}/${cmpName}.js`); const cmp = document.createElement(cmpName); - if (details) cmp.details = details; + cmp.details = pathDetails; el.append(cmp); } function setRecentSite(details) { - if (!details.repo) return; - if (details.repo.startsWith('.')) return; + if (!details.site) return; + // .trash, .da, .helix, .versions + if (details.site.startsWith('.')) return; const currentSites = JSON.parse(localStorage.getItem('da-sites')) || []; - const siteString = `${details.owner}/${details.repo}`; + const siteString = `${details.org}/${details.site}`; const foundIdx = currentSites.indexOf(siteString); if (foundIdx === 0) return; if (foundIdx !== -1) currentSites.splice(foundIdx, 1); localStorage.setItem('da-sites', JSON.stringify([siteString, ...currentSites].slice(0, 8))); } -async function setupExperience(el, e) { - const details = getPathDetails(); - if (details) setRecentSite(details); - if (e) { - const oldHash = new URL(e.oldURL).hash; - const newHash = new URL(e.newURL).hash; - - // Are they already browsing - if (oldHash.startsWith('#/') && newHash.startsWith('#/')) { - document.querySelector('da-browse').details = details; - return; - } - } - if (!details) { - await loadComponent(el, 'da-sites'); - } else { - await loadComponent(el, 'da-browse', details); - } -} - -export default async function init(el) { - await setupExperience(el); +export default function init(el) { + hashChange.subscribe((pathDetails) => { + const cmpName = pathDetails ? 'da-browse' : 'da-sites'; + loadComponent(el, cmpName, pathDetails); + if (pathDetails) setRecentSite(pathDetails); + }); // Lazily preload the editor setTimeout(() => { import('da-y-wrapper'); }, 3000); - - window.addEventListener('hashchange', async (e) => { - await setupExperience(el, e); - }); } diff --git a/blocks/browse/da-actionbar/da-actionbar.css b/blocks/browse/da-actionbar/da-actionbar.css index 07047ab5b..16ac1ef16 100644 --- a/blocks/browse/da-actionbar/da-actionbar.css +++ b/blocks/browse/da-actionbar/da-actionbar.css @@ -4,11 +4,11 @@ button { .da-action-bar { bottom: 12px; - position: fixed; - background: #BC1C74; + position: absolute; + background-color: rgb(188 28 116); min-height: 48px; - border-radius: 6px; - width: var(--grid-container-width); + border-radius: var(--s2-corner-radius-700); + width: var(--se-grid-container-width); z-index: 400; filter: drop-shadow(rgb(0 0 0 / 15%) 0 1px 4px); display: flex; @@ -25,7 +25,7 @@ button { height: 32px; color: #FFF; border: none; - border-radius: 4px; + border-radius: var(--s2-corner-radius-500); display: flex; align-items: center; gap: 12px; @@ -43,14 +43,14 @@ button { } .da-action-bar-left-rail { - margin-left: 14px; + margin-left: 3px; display: flex; align-items: center; gap: 12px; } .da-action-bar-right-rail { - margin: 0 3px; + margin-right: 3px; display: flex; gap: 8px; } diff --git a/blocks/browse/da-actionbar/da-actionbar.js b/blocks/browse/da-actionbar/da-actionbar.js index 73dd35330..7fba88c92 100644 --- a/blocks/browse/da-actionbar/da-actionbar.js +++ b/blocks/browse/da-actionbar/da-actionbar.js @@ -2,8 +2,8 @@ import { LitElement, html } from 'da-lit'; import { getNx } from '../../../scripts/utils.js'; // Styles -const { default: getStyle } = await import(`${getNx()}/utils/styles.js`); -const STYLE = await getStyle(import.meta.url); +const { loadStyle } = await import(`${getNx()}/utils/utils.js`); +const STYLE = await loadStyle(import.meta.url); export default class DaActionBar extends LitElement { static properties = { diff --git a/blocks/browse/da-breadcrumbs/da-breadcrumbs.css b/blocks/browse/da-breadcrumbs/da-breadcrumbs.css index ebb5dbff2..7b0e1c2f4 100644 --- a/blocks/browse/da-breadcrumbs/da-breadcrumbs.css +++ b/blocks/browse/da-breadcrumbs/da-breadcrumbs.css @@ -29,7 +29,7 @@ button { .da-breadcrumb-list-item-link-wrapper { padding: 0 10px; - background: #dcdcdc; + background: var(--s2-gray-200); border-radius: 6px; line-height: 32px; display: flex; @@ -38,7 +38,7 @@ button { .da-breadcrumb-list-item a { text-decoration: none; - color: rgb(44 44 44); + color: var(--s2-gray-800); } .da-breadcrumb-list-item-config { @@ -54,7 +54,7 @@ button { } .da-breadcrumb-list-item-config:hover { - background-color: #147af3; + background-color: var(--s2-blue-900); color: #FFF; } @@ -90,7 +90,7 @@ button { } } -@media (min-width: 900px) { +@media (width >= 900px) { .da-breadcrumb { margin-bottom: 12px; display: flex; diff --git a/blocks/browse/da-breadcrumbs/da-breadcrumbs.js b/blocks/browse/da-breadcrumbs/da-breadcrumbs.js index bffc86ba0..778070fcb 100644 --- a/blocks/browse/da-breadcrumbs/da-breadcrumbs.js +++ b/blocks/browse/da-breadcrumbs/da-breadcrumbs.js @@ -1,24 +1,18 @@ -import { LitElement, html } from 'da-lit'; +import { LitElement, html, nothing } from 'da-lit'; import { getNx } from '../../../scripts/utils.js'; -// Styles & Icons -const getStyle = (await import(`${getNx()}/public/utils/styles.js`)).default; -const getSvg = (await import(`${getNx()}/public/utils/svg.js`)).default; - -const STYLE = await getStyle(import.meta.url); -const ICONS = await getSvg({ paths: ['/blocks/browse/da-browse/img/Smock_Settings_18_N.svg'] }); +const { loadStyle } = await import(`${getNx()}/utils/utils.js`); +const style = await loadStyle(import.meta.url); export default class DaBreadcrumbs extends LitElement { static properties = { - fullpath: { type: String }, - depth: { type: Number }, + details: { attribute: false }, _breadcrumbs: { state: true }, }; connectedCallback() { super.connectedCallback(); - this.shadowRoot.adoptedStyleSheets = [STYLE]; - this.shadowRoot.append(...ICONS); + this.shadowRoot.adoptedStyleSheets = [style]; } update(props) { @@ -27,23 +21,23 @@ export default class DaBreadcrumbs extends LitElement { } getBreadcrumbs() { - const pathSplit = this.fullpath.split('/').filter((part) => part !== ''); + const pathSplit = this.details.fullpath.split('/').filter((part) => part !== ''); this._breadcrumbs = pathSplit.map((part, idx) => ({ name: part, path: `#/${pathSplit.slice(0, idx + 1).join('/')}`, })); } - renderConfig(length, crumb, idx) { - if (this.depth <= 2 && idx + 1 === length) { - return html` - - - `; - } - return null; + renderConfig(crumb) { + if (this.details.path) return nothing; + return html` + + + + + `; } render() { @@ -54,7 +48,7 @@ export default class DaBreadcrumbs extends LitElement {
  • `)} diff --git a/blocks/browse/da-browse/da-browse.css b/blocks/browse/da-browse/da-browse.css index ddb3be69f..6efdab479 100644 --- a/blocks/browse/da-browse/da-browse.css +++ b/blocks/browse/da-browse/da-browse.css @@ -1,6 +1,6 @@ :host { display: block; - max-width: var(--grid-container-width); + max-width: var(--se-grid-container-width); margin: 0 auto; padding: 80px 0; } @@ -19,7 +19,7 @@ padding: 0 0 6px; margin: 0; box-sizing: border-box; - color: rgb(143 143 143); + color: var(--s2-gray-700); text-transform: uppercase; font-size: 22px; font-weight: 700; @@ -34,12 +34,12 @@ width: 100%; opacity: 0; transition: opacity 0.2s ease-in-out 0s; - background: rgb(19 19 19); + background: var(--s2-gray-900); border-radius: 3px; } button[aria-selected="true"] { - color: rgb(19 19 19); + color: var(--s2-gray-900); } button[aria-selected="true"]::after { @@ -51,7 +51,7 @@ display: none; } -@media (min-width: 900px) { +@media (width >= 900px) { .da-list-header.context-browse { display: flex; flex-wrap: wrap; diff --git a/blocks/browse/da-browse/da-browse.js b/blocks/browse/da-browse/da-browse.js index 9b07659ea..de38167ff 100644 --- a/blocks/browse/da-browse/da-browse.js +++ b/blocks/browse/da-browse/da-browse.js @@ -1,5 +1,4 @@ import { LitElement, html, nothing } from 'da-lit'; -import { DA_ORIGIN } from '../../shared/constants.js'; import { daFetch, getFirstSheet } from '../../shared/utils.js'; import { getNx, sanitizePathParts } from '../../../scripts/utils.js'; @@ -9,9 +8,9 @@ import '../da-new/da-new.js'; import '../da-search/da-search.js'; import '../da-list/da-list.js'; -// Styles -const { default: getStyle } = await import(`${getNx()}/utils/styles.js`); -const STYLE = await getStyle(import.meta.url); +const { DA_ADMIN, loadStyle } = await import(`${getNx()}/utils/utils.js`); + +const style = await loadStyle(import.meta.url); export default class DaBrowse extends LitElement { static properties = { @@ -38,7 +37,7 @@ export default class DaBrowse extends LitElement { connectedCallback() { super.connectedCallback(); - this.shadowRoot.adoptedStyleSheets = [STYLE]; + this.shadowRoot.adoptedStyleSheets = [style]; document.addEventListener('keydown', this.handleShortcuts.bind(this)); } @@ -72,7 +71,7 @@ export default class DaBrowse extends LitElement { async update(props) { if (props.has('details') && this.details) { // Only re-fetch if the orgs are different - const reFetch = props.get('details')?.owner !== this.details.owner; + const reFetch = props.get('details')?.org !== this.details.org; this.editor = await this.getEditor(reFetch); } @@ -83,7 +82,7 @@ export default class DaBrowse extends LitElement { const DEF_EDIT = '/edit#'; if (reFetch) { - const resp = await daFetch(`${DA_ORIGIN}/config/${this.details.owner}/`); + const resp = await daFetch(`${DA_ADMIN}/config/${this.details.org}/`); if (!resp.ok) return DEF_EDIT; const json = await resp.json(); @@ -188,7 +187,7 @@ export default class DaBrowse extends LitElement { })}
    - + ${this._tabItems.map((tab) => html`
    ${tab.id === 'browse' ? this.renderNew() : this.renderSearch()} diff --git a/blocks/browse/da-list-item/da-list-item.css b/blocks/browse/da-list-item/da-list-item.css index fe0dd3f56..bb260583b 100644 --- a/blocks/browse/da-list-item/da-list-item.css +++ b/blocks/browse/da-list-item/da-list-item.css @@ -11,7 +11,7 @@ :host(:hover), :host(.is-expanded) { z-index: 1; - background: rgb(228 240 255); + background: var(--s2-blue-200); } button { @@ -41,7 +41,7 @@ button { justify-content: space-between; align-items: center; text-decoration: none; - color: rgb(46 46 46); + color: var(--s2-gray-900); padding: 24px 0; } @@ -53,37 +53,6 @@ button { height: 32px; } -.da-item-list-item-type-file { - background: url('/blocks/browse/img/Smock_Document_18_N.svg') center / cover no-repeat; -} - -.da-item-list-item-type-file-version { - background: url('/blocks/browse/img/Smock_FileTemplate_18_N.svg') center / cover no-repeat; -} - -.da-item-list-item-type-folder { - background: url('/blocks/browse/img/Smock_Folder_18_N.svg') center / cover no-repeat; -} - -.da-item-list-item-icon-html { - background: url('/blocks/browse/img/Smock_FileHTML_18_N.svg') center / cover no-repeat; -} - -.da-item-list-item-icon-link { - background: url('/blocks/browse/img/Smock_LinkOut_18_N.svg') center / cover no-repeat; -} - -.da-item-list-item-icon-jpg, -.da-item-list-item-icon-jpeg, -.da-item-list-item-icon-png, -.da-item-list-item-icon-svg { - background: url('/blocks/browse/img/Smock_Image_18_N.svg') center / cover no-repeat; -} - -.da-item-list-item-icon-json { - background: url('/blocks/browse/img/Smock_FileData_18_N.svg') center / cover no-repeat; -} - .da-item-list-item-date { min-width: 160px; } @@ -150,7 +119,7 @@ input[type="checkbox"] { height: 18px; width: 18px; border-radius: 2px; - background: rgb(221 221 221); + background: var(--s2-gray-400); } .checkbox-label::after { @@ -195,7 +164,7 @@ input[type="checkbox"] { .da-item-list-item-rename input:focus-visible { outline: none; border: none; - border-bottom: 1px dotted rgb(20 122 243); + border-bottom: 1px dotted var(--s2-blue-900); } .da-item-list-item-rename-actions { @@ -241,7 +210,7 @@ input[type="checkbox"] { } .da-item-list-item-expand-btn:hover::before { - background: #d4e7ff; + background: var(--s2-blue-300); } .da-item-list-item-expand-btn.is-visible { @@ -275,7 +244,7 @@ input[type="checkbox"] { .da-item-list-item-aem-btn { text-decoration: none; - color: #000; + color: var(--s2-gray-900); display: flex; background: none; gap: 12px; diff --git a/blocks/browse/da-list-item/da-list-item.js b/blocks/browse/da-list-item/da-list-item.js index aedc6d25a..2bffa64a2 100644 --- a/blocks/browse/da-list-item/da-list-item.js +++ b/blocks/browse/da-list-item/da-list-item.js @@ -5,8 +5,27 @@ import { getNx } from '../../../scripts/utils.js'; import getEditPath from '../shared.js'; import { formatDate } from '../../edit/da-versions/helpers.js'; -const { default: getStyle } = await import(`${getNx()}/utils/styles.js`); -const STYLE = await getStyle(import.meta.url); +// Styles +const { loadStyle } = await import(`${getNx()}/utils/utils.js`); +const STYLE = await loadStyle(import.meta.url); + +const ICONS = { + folder: '/img/icons/s2-icon-folder-20-n.svg', + file: '/img/icons/s2-icon-file-20-n.svg', + json: '/img/icons/s2-icon-data-20-n.svg', + link: '/img/icons/s2-icon-link-20-n.svg', + jpg: '/img/icons/s2-icon-image-20-n.svg', + jpeg: '/img/icons/s2-icon-image-20-n.svg', + png: '/img/icons/s2-icon-image-20-n.svg', + svg: '/img/icons/s2-icon-image-20-n.svg', + gif: '/img/icons/s2-icon-image-20-n.svg', + avif: '/img/icons/s2-icon-image-20-n.svg', + webp: '/img/icons/s2-icon-image-20-n.svg', + mp4: '/img/icons/s2-icon-video-20-n.svg', + media: '/img/icons/s2-icon-image-20-n.svg', + pdf: '/img/icons/s2-icon-acrobatsolid-20-n.svg', + folderClock: '/img/icons/s2-icon-folderclock-20-n.svg', +}; export default class DaListItem extends LitElement { static properties = { @@ -218,6 +237,13 @@ export default class DaListItem extends LitElement { `; } + renderIcon() { + // determine base type + const type = !this.ext ? 'folder' : this.ext; + const iconPath = ICONS[type] || ICONS.file; + return html``; + } + renderItem() { let path = this.ext ? getEditPath({ path: this.path, ext: this.ext, editor: this.editor }) : `#${this.path}`; let externalUrlPromise; @@ -229,14 +255,10 @@ export default class DaListItem extends LitElement { } return html` - ${this._isRenaming ? html` - -
    -
    + ${this._isRenaming ? html`
    ` : html` - + ${this.renderIcon()} `} -
    ${this.name}
    ${this.ext === 'link' ? nothing : this.renderDate()}
    `; @@ -254,7 +276,9 @@ export default class DaListItem extends LitElement { renderDaDetails() { return html` - + + +

    Version

    ${this._version || this._version === 0 ? this._version : 'Checking'}

    diff --git a/blocks/browse/da-list/da-list.css b/blocks/browse/da-list/da-list.css index 19abfbe03..ad3bf07fe 100644 --- a/blocks/browse/da-list/da-list.css +++ b/blocks/browse/da-list/da-list.css @@ -4,10 +4,10 @@ /* Item List */ .da-item-list { - border: 1px solid rgb(209 209 209); + border: 1px solid var(--s2-gray-200); border-radius: 6px; overflow: hidden; - background: #f8f8f8; + background: var(--s2-gray-75); } .da-list-sentinel { @@ -20,7 +20,7 @@ da-list-item::before { display: block; content: ""; height: 1px; - background: #e5e5e5; + background: var(--s2-gray-400); margin: 0 18px; } @@ -28,20 +28,20 @@ da-list-item::after { display: block; content: ""; height: 1px; - background: #e5e5e5; + background: var(--s2-gray-400); margin: 0 18px; } da-list-item:hover::before, da-list-item.is-expanded::before { margin: 0; - background: rgb(20 122 243); + background: var(--s2-blue-900); } da-list-item:hover::after, da-list-item.is-expanded::after { margin: 0; - background: rgb(20 122 243); + background: var(--s2-blue-900); } /* Empty list */ @@ -56,8 +56,8 @@ da-list-item.is-expanded::after { } .da-browse-panel-header { - margin: var(--spacing-75) 1px var(--spacing-300) 1px; - padding: 0 var(--spacing-400); + margin: var(--s2-spacing-100) 1px var(--s2-spacing-300) 1px; + padding: 0 var(--s2-spacing-400); display: grid; grid-template-columns: auto 1fr auto; align-items: center; @@ -83,7 +83,7 @@ da-list-item.is-expanded::after { display: flex; justify-content: center; align-items: center; - padding: 2px; + width: 32px; height: 18px; position: relative; } @@ -174,7 +174,7 @@ da-list-item.is-expanded::after { right: 0; bottom: -3px; height: 2px; - background: var(--s2-gray-700); + background: var(--s2-gray-200); border-radius: 1.5px; } diff --git a/blocks/browse/da-list/da-list.js b/blocks/browse/da-list/da-list.js index aa9798fdf..f50d7fe10 100644 --- a/blocks/browse/da-list/da-list.js +++ b/blocks/browse/da-list/da-list.js @@ -5,10 +5,9 @@ import { daFetch, aemAdmin } from '../../shared/utils.js'; import '../da-list-item/da-list-item.js'; -// Styles & Icons -const { default: getStyle } = await import(`${getNx()}/utils/styles.js`); +const { loadStyle } = await import(`${getNx()}/utils/utils.js`); +const STYLE = await loadStyle(import.meta.url); const { default: getSvg } = await import(`${getNx()}/utils/svg.js`); -const STYLE = await getStyle(import.meta.url); const ICONS = [ '/blocks/edit/img/Smock_Cancel_18_N.svg', '/blocks/edit/img/Smock_Checkmark_18_N.svg', diff --git a/blocks/browse/da-list/helpers/utils.js b/blocks/browse/da-list/helpers/utils.js index 38a6e328c..8a3148592 100644 --- a/blocks/browse/da-list/helpers/utils.js +++ b/blocks/browse/da-list/helpers/utils.js @@ -139,11 +139,11 @@ export async function handleUpload(list, fullpath, file) { export function items2Clipboard(items) { const aemUrls = items.reduce((acc, item) => { if (item.ext) { - const [org, repo, ...pathParts] = sanitizePathParts(item.path.replace('.html', '')); + const [org, site, ...pathParts] = sanitizePathParts(item.path.replace('.html', '')); const pageName = pathParts.pop(); pathParts.push(pageName === 'index' ? '' : pageName); - const url = `https://main--${repo}--${org}.aem.page/${pathParts.join('/')}`; + const url = `https://main--${site}--${org}.aem.page/${pathParts.join('/')}`; const toPush = item.message ? `${url} - ${item.message}` : url; acc.push(toPush); diff --git a/blocks/browse/da-new/da-new.css b/blocks/browse/da-new/da-new.css index 2a6a1a563..8f6abf3b5 100644 --- a/blocks/browse/da-new/da-new.css +++ b/blocks/browse/da-new/da-new.css @@ -16,9 +16,9 @@ button { top: 40px; margin: 0 0 0 6px; list-style: none; - background: #FFF; - box-shadow: 0 0 5px 0 #b5b5b5; - border-radius: 4px; + background: var(--s2-gray-25); + box-shadow: 0 0 5px 0 var(--s2-gray-500); + border-radius: 10px; padding: 6px; z-index: 100; } @@ -111,12 +111,12 @@ button { line-height: 28px; border: none; padding: 0 10px; - border-radius: 2px; + border-radius: 6px; background: transparent; } li.da-actions-menu-item button:hover { - background: #EFEFEF; + background: var(--s2-gray-100); } .da-input-error { diff --git a/blocks/browse/da-new/da-new.js b/blocks/browse/da-new/da-new.js index f0883fc81..c2ab7cd2c 100644 --- a/blocks/browse/da-new/da-new.js +++ b/blocks/browse/da-new/da-new.js @@ -4,8 +4,8 @@ import { getNx } from '../../../scripts/utils.js'; import getEditPath from '../shared.js'; // Styles & Icons -const { default: getStyle } = await import(`${getNx()}/utils/styles.js`); -const STYLE = await getStyle(import.meta.url); +const { loadStyle } = await import(`${getNx()}/utils/utils.js`); +const STYLE = await loadStyle(import.meta.url); const INPUT_ERROR = 'da-input-error'; @@ -14,12 +14,12 @@ export default class DaNew extends LitElement { fullpath: { type: String }, editor: { type: String }, permissions: { attribute: false }, - _createShow: { attribute: false }, - _createType: { attribute: false }, - _createFile: { attribute: false }, - _createName: { attribute: false }, - _fileLabel: { attribute: false }, - _externalUrl: { attribute: false }, + _createShow: { state: true }, + _createType: { state: true }, + _createFile: { state: true }, + _createName: { state: true }, + _fileLabel: { state: true }, + _externalUrl: { state: true }, }; connectedCallback() { diff --git a/blocks/browse/da-search/da-search.js b/blocks/browse/da-search/da-search.js index 8392fe339..71aeb010b 100644 --- a/blocks/browse/da-search/da-search.js +++ b/blocks/browse/da-search/da-search.js @@ -6,8 +6,8 @@ import { daFetch } from '../../shared/utils.js'; const { crawl, Queue } = await import(`${getNx()}/public/utils/tree.js`); // Styles -const { default: getStyle } = await import(`${getNx()}/utils/styles.js`); -const STYLE = await getStyle(import.meta.url); +const { loadStyle } = await import(`${getNx()}/utils/utils.js`); +const STYLE = await loadStyle(import.meta.url); const DEFAULT_LOCALES = ['langstore']; diff --git a/blocks/browse/da-sites/da-sites.css b/blocks/browse/da-sites/da-sites.css index 6116b554c..4aecf354d 100644 --- a/blocks/browse/da-sites/da-sites.css +++ b/blocks/browse/da-sites/da-sites.css @@ -14,11 +14,17 @@ h2 { } .da-site-bg { - width: 100%; position: absolute; left: 0; right: 0; top: 0; + + img { + display: block; + width: 100%; + height: auto; + mask-image: linear-gradient(to top, transparent, rgb(0 0 0) 99%); + } } .da-site-container { @@ -39,7 +45,7 @@ h2 { display: grid; place-items: center; place-content: center; - background: #fafafa; + background: var(--s2-gray-25); border-radius: var(--s2-corner-radius-800); min-height: 320px; box-shadow: rgb(0 0 0 / 8%) 0 6px 16px, rgb(0 0 0 / 12%) 0 3px 6px; @@ -76,17 +82,26 @@ form { form input { border: none; background: transparent; - border-bottom: 1px solid #a2a2a2; + border-bottom: 1px solid var(--s2-gray-900); font-family: var(--s2-font-family); font-size: 16px; padding: 0 0 4px 4px; line-height: 26px; width: 100%; + + &::placeholder { + font-style: italic; + color: var(--s2-gray-800); + } + + &:focus::placeholder { + opacity: 0.5; + } } form input:focus-visible { padding: 0 0 3px 4px; - border-bottom: 2px solid #808080; + border-bottom: 2px solid var(--s2-gray-1000); outline: none; outline-offset: 0; } @@ -122,11 +137,6 @@ form input.error { width: 100%; } -form input::placeholder { - font-style: italic; - color: #888; -} - form button { display: flex; justify-content: center; @@ -172,7 +182,7 @@ form button:hover { width: 100%; height: 100%; position: relative; - background-color: rgb(255 255 255); + background-color: rgb(0 0 0); border-radius: 12px; transition: all 0.5s ease-in-out; transform-style: preserve-3d; @@ -180,8 +190,8 @@ form button:hover { } .da-site-front img { - height: 100%; width: 100%; + height: auto; object-fit: cover; transform: scale(1); transition: transform .25s ease-in-out; @@ -346,12 +356,12 @@ form button:hover { grid-area: main; color: #fff; font-weight: 700; - font-size: var(--s2-heading-size-300); + font-size: var(--s2-body-size-s); text-decoration: none; span:nth-child(2) { - font-size: 16px; - font-weight: 500; + font-size: var(--s2-body-size-l); + font-weight: 400; } } diff --git a/blocks/browse/da-sites/da-sites.js b/blocks/browse/da-sites/da-sites.js index 7c93b5521..a8b79b6b6 100644 --- a/blocks/browse/da-sites/da-sites.js +++ b/blocks/browse/da-sites/da-sites.js @@ -1,8 +1,8 @@ import { LitElement, html, nothing } from 'da-lit'; -import getSheet from '../../shared/sheet.js'; -import { sanitizeName } from '../../../scripts/utils.js'; +import { getNx, sanitizeName } from '../../../scripts/utils.js'; -const sheet = await getSheet('/blocks/browse/da-sites/da-sites.css'); +const { loadStyle } = await import(`${getNx()}/utils/utils.js`); +const styles = await loadStyle(import.meta.url); const RANDOM_MAX = 8; @@ -24,40 +24,22 @@ export default class DaSites extends LitElement { connectedCallback() { super.connectedCallback(); - this.shadowRoot.adoptedStyleSheets = [sheet]; - this.getRecents(); - } - - mapRecentSites(recentSites) { - this._recents = recentSites.map((name) => ( - { - name, - img: `/blocks/browse/da-sites/img/cards/da-${getRandom()}.jpg`, - style: `da-card-style-${getRandom()}`, - } - )); - } - - mapRecentOrgs(recentOrgs) { - this._recents = recentOrgs.map((name) => ( - { - name, - img: `/blocks/browse/da-sites/img/cards/da-${getRandom()}.jpg`, - style: `da-card-style-${getRandom()}`, - } - )); + this.shadowRoot.adoptedStyleSheets = [styles]; + this._recents = this.getRecents(); } getRecents() { const recentSites = JSON.parse(localStorage.getItem('da-sites')) || []; - const recentOrgs = JSON.parse(localStorage.getItem('da-orgs')) || []; - if (recentSites.length > 0) { - this.mapRecentSites(recentSites); - localStorage.removeItem('da-orgs'); - } else if (recentOrgs.length > 0) { - this.mapRecentOrgs(recentOrgs); + return recentSites.map((name) => ( + { + name, + img: `/blocks/browse/da-sites/img/cards/da-${getRandom()}.jpg`, + style: `da-card-style-${getRandom()}`, + } + )); } + return null; } setStatus(text, description, type = 'info') { @@ -92,10 +74,9 @@ export default class DaSites extends LitElement { } const helixString = url.hostname.split('.')[0]; if (!helixString) return null; - // eslint-disable-next-line no-unused-vars - const [_, repo, org] = helixString.split('--'); - if (!repo || !org) return null; - return `#/${sanitizeName(org, false)}/${sanitizeName(repo, false)}`; + const [, site, org] = helixString.split('--'); + if (!site || !org) return null; + return `#/${sanitizeName(org, false)}/${sanitizeName(site, false)}`; } catch (_) { return null; } @@ -219,15 +200,14 @@ export default class DaSites extends LitElement { render() { return html` -

    Recents

    - ${this._recents && this._recents.length > 0 ? this.renderSites(this._recents) : this.renderEmpty()} + ${this._recents?.length > 0 ? this.renderSites(this._recents) : this.renderEmpty()}

    Sites

    - ${this._recents && this._recents.length > 0 ? this.renderGo() : nothing} + ${this._recents?.length > 0 ? this.renderGo() : nothing}
    diff --git a/blocks/shared/da-dialog/da-dialog.css b/blocks/shared/da-dialog/da-dialog.css index 3de9757a2..318dcdf49 100644 --- a/blocks/shared/da-dialog/da-dialog.css +++ b/blocks/shared/da-dialog/da-dialog.css @@ -22,7 +22,14 @@ svg.icon { overflow: auto; hr { + background: var(--s2-gray-200); + height: 2px; margin: 0 0 20px; + border-radius: 1px; + border-width: medium; + border-style: none; + border-color: currentcolor; + border-image: initial; } .da-dialog-header { @@ -33,6 +40,8 @@ svg.icon { p { line-height: 1; padding: 0 0 20px; + margin: 0; + font-weight: 700; } .da-dialog-close-btn { diff --git a/blocks/shared/da-dialog/da-dialog.js b/blocks/shared/da-dialog/da-dialog.js index ad35612df..8fb7eb68d 100644 --- a/blocks/shared/da-dialog/da-dialog.js +++ b/blocks/shared/da-dialog/da-dialog.js @@ -7,9 +7,8 @@ const nx = getNx(); await import(`${nx}/public/sl/components.js`); // Styles -const { default: getStyle } = await import(`${nx}/utils/styles.js`); -const SL = await getStyle(`${nx}/public/sl/styles.css`); -const STYLE = await getStyle(import.meta.url); +const { loadStyle } = await import(`${nx}/utils/utils.js`); +const STYLE = await loadStyle(import.meta.url); export default class DaDialog extends LitElement { static properties = { @@ -23,7 +22,7 @@ export default class DaDialog extends LitElement { connectedCallback() { super.connectedCallback(); - this.shadowRoot.adoptedStyleSheets = [SL, STYLE]; + this.shadowRoot.adoptedStyleSheets = [STYLE]; setTimeout(() => { this.showModal(); }, 20); } diff --git a/head.html b/head.html index f0fbd2154..b03038e0b 100644 --- a/head.html +++ b/head.html @@ -14,6 +14,7 @@ } } + + - diff --git a/scripts/scripts.js b/scripts/scripts.js index 1511a7413..0d4f33480 100644 --- a/scripts/scripts.js +++ b/scripts/scripts.js @@ -11,66 +11,56 @@ */ import { setNx, nxJS, nxCSS } from './utils.js'; -import { initIms } from '../blocks/shared/utils.js'; -/** Determine where to load NX from */ -const nx = setNx('/nx'); -const nx2 = nx.endsWith('nx2'); +export function decorateArea({ area = document } = {}) { + // Find all dark & light images + const lcpImgs = [...area.querySelectorAll('[alt="light"], [alt="dark"]')].filter((img) => { + const pic = img.parentElement; + const parent = img.alt === 'light' ? img.closest('.light-scheme') : img.closest('.dark-scheme'); + pic.dataset.scheme = img.alt; + img.alt = ''; + return !!parent; + }); -/** Default area decoration */ -const decorateArea = ({ area = document }) => { - const eagerLoad = (parent, selector) => { - const img = parent.querySelector(selector); - if (!img) return; - img.removeAttribute('loading'); - img.fetchPriority = 'high'; - }; + // If browsing with a hash, skip LCP detection + if (window.location.hash && area.querySelector('.browse')) return; - eagerLoad(area, 'img'); + // Only pick the first image from all found + const img = lcpImgs[0] || area.querySelector('img'); + if (!img) return; - if (!nx2) return; - // Prefix DA blocks so NX knows to load from DA - // TODO: NX2 forward compatibility, remove after upgrade - area.querySelectorAll('div[class]').forEach((block) => { - const { className } = block; + img.removeAttribute('loading'); + img.fetchPriority = 'high'; +} - // If its an nx block, remove the prefix, otherwise add 'da-' - block.className = className.startsWith('nx-') - ? className.replace('nx-', '') : `da-${className}`; - }); -}; +// Where to load NX +const nx = setNx('/nx'); +const nx2 = nx.endsWith('nx2'); +const { loadArea, setConfig, getColorScheme } = await import(`${nx}${nxJS}`); -// Who can provide blocks -const providers = { da: window.location.origin }; +// Set color scheme once +document.body.classList.add(getColorScheme()); + +// Load NX styles +const link = document.createElement('link'); +link.setAttribute('rel', 'stylesheet'); +link.setAttribute('href', `${nx}${nxCSS}`); +document.head.appendChild(link); -/** Setup the NX config object */ const CONFIG = { - providers, hostnames: ['da.live', 'da.page'], codeBase: import.meta.url.replace('/scripts/scripts.js', ''), + providers: { da: window.location.origin }, + decorateArea, imsClientId: 'darkalley', imsScope: 'ab.manage,AdobeID,gnav,openid,org.read,read_organizations,session,aem.frontend.all,additional_info.ownerOrg,additional_info.projectedProductContext,account_cluster.read', - decorateArea, }; -const { loadArea, setConfig } = await import(`${nx}${nxJS}`); - -function loadStyles() { - const link = document.createElement('link'); - link.setAttribute('rel', 'stylesheet'); - link.setAttribute('href', `${nx}${nxCSS}`); - document.head.appendChild(link); -} - export default async function loadPage() { - loadStyles(); - initIms(); - if (!nx2) { // pin to light scheme + document.body.classList.remove('light-scheme', 'dark-scheme'); document.body.classList.add('light-scheme'); - // nx2 decorates automatically - decorateArea({}); } await setConfig(CONFIG); await loadArea(); diff --git a/scripts/utils.js b/scripts/utils.js index 947795f45..cd4bc059e 100644 --- a/scripts/utils.js +++ b/scripts/utils.js @@ -10,28 +10,6 @@ * governing permissions and limitations under the License. */ -/** @deprecated Moved to scripts.js */ -export const codeBase = `${import.meta.url.replace('/scripts/utils.js', '')}`; - -/** @deprecated Moved to scripts.js */ -export function decorateArea(area = document) { - const eagerLoad = (parent, selector) => { - const img = parent.querySelector(selector); - img?.removeAttribute('loading'); - }; - - (async function loadLCPImage() { - const hero = area.querySelector('.nx-hero, .hero'); - if (!hero) { - eagerLoad(area, 'img'); - return; - } - - eagerLoad(hero, 'div:first-child img'); - eagerLoad(hero, 'div:last-child > div:last-child img'); - }()); -} - export function sanitizeName(name, preserveDots = true, allowUnderscores = true) { if (!name) return null; @@ -66,7 +44,9 @@ export function sanitizePath(path) { return `/${sanitizePathParts(path).join('/')}`; } -const nxVer = sanitizeName(new URLSearchParams(window.location.search).get('nxver')); +// Determine what version of NX to load +let nxVer = document.head.querySelector('[name="nxver"]')?.getAttribute('content'); +if (!nxVer) nxVer = sanitizeName(new URLSearchParams(window.location.search).get('nxver')); /** Determine NX filenames */ export const nxJS = nxVer ? '/scripts/nx.js' : '/scripts/nexter.js'; diff --git a/test/fixtures/nx/scripts/nexter.js b/test/fixtures/nx/scripts/nexter.js index c1df3c86e..6bff2269d 100644 --- a/test/fixtures/nx/scripts/nexter.js +++ b/test/fixtures/nx/scripts/nexter.js @@ -4,7 +4,17 @@ export function setConfig(config) { return config; } +export function loadArea() { + // Mock implementation + return Promise.resolve(); +} + export function loadStyle() { // Mock implementation return Promise.resolve(); } + +export function getColorScheme() { + // Mock implementation + return 'light-scheme'; +} diff --git a/test/fixtures/nx/utils/utils.js b/test/fixtures/nx/utils/utils.js new file mode 100644 index 000000000..9dbbc457e --- /dev/null +++ b/test/fixtures/nx/utils/utils.js @@ -0,0 +1,7 @@ +export const loadStyle = async () => { + const sheet = new CSSStyleSheet(); + sheet.replaceSync(''); + return sheet; +}; + +export const DA_ADMIN = 'https://admin.da.live'; diff --git a/test/unit/milo.js b/test/unit/milo.js index 26f39d606..9e8ccb669 100644 --- a/test/unit/milo.js +++ b/test/unit/milo.js @@ -1,5 +1,5 @@ -import { setNx, codeBase } from '../../scripts/utils.js'; +import { setNx } from '../../scripts/utils.js'; const nx = setNx('/nx'); const { setConfig } = await import(`${nx}/scripts/nexter.js`); -setConfig({ codeBase }); +setConfig({ codeBase: '/' }); diff --git a/test/unit/scripts/scripts.test.js b/test/unit/scripts/scripts.test.js new file mode 100644 index 000000000..7ad1d366c --- /dev/null +++ b/test/unit/scripts/scripts.test.js @@ -0,0 +1,100 @@ +import { expect } from '@esm-bundle/chai'; +import { setNx } from '../../../scripts/utils.js'; + +setNx('/test/fixtures/nx', { hostname: 'example.com' }); + +const { decorateArea } = await import('../../../scripts/scripts.js'); + +describe('decorateArea', () => { + let container; + + beforeEach(() => { + container = document.createElement('div'); + document.body.appendChild(container); + }); + + afterEach(() => { + container.remove(); + window.location.hash = ''; + }); + + it('eager-loads a light-background image in light-scheme', () => { + container.classList.add('light-scheme'); + container.innerHTML = 'light-background'; + decorateArea({ area: container }); + + const img = container.querySelector('img'); + expect(img.hasAttribute('loading')).to.be.false; + expect(img.fetchPriority).to.equal('high'); + }); + + it('eager-loads a dark-background image in dark-scheme', () => { + container.classList.add('dark-scheme'); + container.innerHTML = 'dark-background'; + decorateArea({ area: container }); + + const img = container.querySelector('img'); + expect(img.hasAttribute('loading')).to.be.false; + expect(img.fetchPriority).to.equal('high'); + }); + + it('falls back to the first img when no scheme-specific image exists', () => { + container.innerHTML = ''; + decorateArea({ area: container }); + + const img = container.querySelector('img'); + expect(img.hasAttribute('loading')).to.be.false; + expect(img.fetchPriority).to.equal('high'); + }); + + it('does nothing when there are no images', () => { + container.innerHTML = '
    No images here
    '; + decorateArea({ area: container }); + expect(container.querySelector('img')).to.be.null; + }); + + it('skips eager-load for images inside .browse when there is a hash', () => { + window.location.hash = '#some-path'; + container.innerHTML = '
    '; + decorateArea({ area: container }); + + const img = container.querySelector('img'); + expect(img.getAttribute('loading')).to.equal('lazy'); + expect(img.fetchPriority).to.not.equal('high'); + }); + + it('eager-loads images inside .browse when there is no hash', () => { + window.location.hash = ''; + container.innerHTML = '
    '; + decorateArea({ area: container }); + + const img = container.querySelector('img'); + expect(img.hasAttribute('loading')).to.be.false; + expect(img.fetchPriority).to.equal('high'); + }); + + it('eager-loads images outside .browse regardless of hash', () => { + window.location.hash = '#some-path'; + container.innerHTML = '
    '; + decorateArea({ area: container }); + + const img = container.querySelector('img'); + expect(img.hasAttribute('loading')).to.be.false; + expect(img.fetchPriority).to.equal('high'); + }); + + it('prefers light-background over generic img', () => { + container.classList.add('light-scheme'); + container.innerHTML = ` + + light + `; + decorateArea({ area: container }); + + const generic = container.querySelector('img[src="generic.jpg"]'); + const light = container.querySelector('picture[data-scheme="light"] img'); + expect(generic.getAttribute('loading')).to.equal('lazy'); + expect(light.hasAttribute('loading')).to.be.false; + expect(light.fetchPriority).to.equal('high'); + }); +});