diff --git a/blocks/edit/da-title/da-title.css b/blocks/edit/da-title/da-title.css
index d3b565346..9a0e87a72 100644
--- a/blocks/edit/da-title/da-title.css
+++ b/blocks/edit/da-title/da-title.css
@@ -44,6 +44,11 @@ da-dialog {
background: var(--s2-blue-900);
border-color: var(--s2-blue-900);
color: #FFF;
+
+ &:disabled {
+ background: var(--s2-gray-500);
+ border-color: var(--s2-gray-500);
+ }
}
.da-title-inner {
@@ -121,13 +126,16 @@ da-dialog {
z-index: 2;
}
-.collab-icon.collab-popup::after {
+.collab-icon.collab-popup::after,
+.da-title-action::after {
display: block;
content: attr(data-popup-content);
position: absolute;
bottom: -32px;
left: 50%;
transform: translateX(-50%);
+ font-size: 12px;
+ line-height: 1.6;
text-align: center;
text-transform: capitalize;
background: #676767;
@@ -137,6 +145,21 @@ da-dialog {
border-radius: 4px;
}
+.da-title-action {
+ position: relative;
+ display: none;
+
+ &::after {
+ display: none;
+ bottom: -22px;
+ text-transform: unset;
+ }
+
+ &:hover::after {
+ display: unset;
+ }
+}
+
.collab-icon-user {
height: 24px;
border-radius: 12px;
@@ -172,9 +195,15 @@ da-dialog {
height: 27px;
}
-.da-title-action {
+.da-title-action-send {
position: relative;
- display: none;
+ padding: 5px 0;
+ width: 44px;
+ height: 44px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ overflow: hidden;
}
.da-title-actions {
@@ -184,7 +213,8 @@ da-dialog {
gap: 12px;
height: 44px;
- &.is-open {
+ &.is-open,
+ &.has-one-action {
margin-left: 12px;
.da-title-action {
@@ -200,6 +230,18 @@ da-dialog {
}
}
+ &.has-one-action {
+ margin-left: 0;
+
+ .da-title-action-send {
+ display: none;
+ }
+
+ &::before {
+ background: transparent;
+ }
+ }
+
&.is-fixed {
position: fixed;
right: 18px;
@@ -208,17 +250,6 @@ da-dialog {
}
}
-.da-title-action-send {
- position: relative;
- padding: 5px 0;
- width: 44px;
- height: 44px;
- display: flex;
- justify-content: center;
- align-items: center;
- overflow: hidden;
-}
-
.da-title-action-send-icon {
position: relative;
display: block;
diff --git a/blocks/edit/da-title/da-title.js b/blocks/edit/da-title/da-title.js
index 0eaf57fda..de5f6c53b 100644
--- a/blocks/edit/da-title/da-title.js
+++ b/blocks/edit/da-title/da-title.js
@@ -38,10 +38,12 @@ export default class DaTitle extends LitElement {
collabUsers: { attribute: false },
previewPrefix: { attribute: false },
livePrefix: { attribute: false },
+ disabledText: { attribute: false },
_lazyMods: { state: true },
_configs: { state: true },
_actions: { state: true },
_status: { state: true },
+ _isSending: { state: true },
_dialog: { state: true },
};
@@ -68,7 +70,7 @@ export default class DaTitle extends LitElement {
update(changed) {
super.update(changed);
if (changed.has('details') && this.details) {
- this.reset();
+ this.setup();
this.delayedSetup();
}
}
@@ -86,7 +88,49 @@ export default class DaTitle extends LitElement {
reset() {
this._scheduled = undefined;
this._configs = undefined;
- this._actions = {};
+ }
+
+ setup() {
+ this.reset();
+ this._actions = { available: this.getAvailableActions() };
+ // Lazily filter the actions down
+ this.filterActions();
+ }
+
+ getAvailableActions() {
+ const { view, path, fullpath } = this.details;
+
+ // Config only gets save
+ if (view === 'config') return ['save'];
+
+ // DA app configs only get save
+ if (fullpath.includes('/.da/') && view === 'sheet') return ['save'];
+
+ const availableActions = [];
+
+ if (view === 'sheet') {
+ availableActions.push('save');
+ }
+
+ if (path) {
+ availableActions.push('preview', 'publish');
+ }
+
+ return availableActions;
+ }
+
+ async filterActions() {
+ const { org, site, fullpath } = this.details;
+ const configs = await Promise.all(fetchDaConfigs({ org, site }));
+ const configTab = configs.flatMap((config) => getFirstSheet(config) || []);
+
+ // Check which actions should be allowed for the document based on config
+ const publishConfigs = configTab.filter((c) => c.key === 'editor.hidePublish');
+ const hidePublish = publishConfigs.some((c) => fullpath.startsWith(c.value));
+ if (!hidePublish) return;
+
+ this._actions.available = this._actions.available.filter((action) => action !== 'publish');
+ this.requestUpdate();
}
// Run setup after a short delay.
@@ -101,11 +145,6 @@ export default class DaTitle extends LitElement {
]);
const { org, site, path, fullpath } = this.details;
- const configs = await Promise.all(fetchDaConfigs({ org, site }));
- this._configs = configs.flatMap((config) => getFirstSheet(config) || []);
-
- this._actions.available = await this.getAvailableActions();
- this.requestUpdate();
// Only a valid path gets AEM-bound features
if (path) {
@@ -119,40 +158,20 @@ export default class DaTitle extends LitElement {
return getExistingSchedule(org, site, path);
}
- async getAvailableActions() {
- const { view, path, fullpath } = this.details;
-
- // Config only gets save
- if (view === 'config') return ['save'];
-
- const availableActions = [];
-
- if (view === 'sheet') {
- availableActions.push('save');
- }
-
- if (path) {
- availableActions.push('preview');
-
- // Check which actions should be allowed for the document based on config
- const publishConfigs = this._configs.filter((c) => c.key === 'editor.hidePublish');
- const hidePublish = publishConfigs.some((c) => fullpath.startsWith(c.value));
-
- if (!hidePublish) availableActions.push('publish');
- }
-
- return availableActions;
- }
-
toggleActions() {
this._actions.open = !this._actions.open;
this.requestUpdate();
}
- handleError(json, action, icon) {
+ handleSuccess(action) {
+ const opts = { detail: { action }, composed: true, bubbles: true };
+ const event = new CustomEvent('success', opts);
+ this.dispatchEvent(event);
+ }
+
+ handleError(json, action) {
this._status = { ...json.error, action };
- icon.classList.remove('is-sending');
- icon.parentElement.classList.add('is-error');
+ this._isSending = false;
}
async setScheduledDialog(schedule) {
@@ -207,14 +226,23 @@ export default class DaTitle extends LitElement {
async handleAction(action) {
this._status = null;
- this._sendButton.classList.add('is-sending');
+ this._isSending = true;
this._actions.open = false;
- this.requestUpdate();
const { org, site, view, fullpath, path } = this.details;
const aemPath = `/${org}/${site}${path}`;
+ // Bail before writing if the remote drifted under us — protects against
+ // last-write-wins. Drift triggers the stale-content dialog via onStale.
+ if (view === 'sheet' || view === 'config') {
+ const { staleCheck } = await import('../../sheet/utils/utils.js');
+ if (await staleCheck.checkForDrift()) {
+ this._isSending = false;
+ return;
+ }
+ }
+
// Only save to DA if it is a sheet or config
if (view === 'sheet') {
const sheetPath = fullpath.replace('.json', '');
@@ -229,11 +257,16 @@ export default class DaTitle extends LitElement {
return;
}
}
+ if (view === 'sheet' || view === 'config') {
+ // Tell anything listening save was successful
+ this.handleSuccess('save');
+ }
+
// AEM Actions
if (action === 'preview' || action === 'publish') {
let json = await saveToAem(aemPath, 'preview');
if (json.error) {
- this.handleError(json, 'preview', this._sendButton);
+ this.handleError(json, 'preview');
return;
}
@@ -244,7 +277,7 @@ export default class DaTitle extends LitElement {
if (this._scheduled?.scheduled) {
const shouldContinue = await this.setScheduledDialog(this._scheduled);
if (!shouldContinue) {
- this._sendButton.classList.remove('is-sending');
+ this._isSending = false;
return;
}
}
@@ -254,7 +287,7 @@ export default class DaTitle extends LitElement {
// Handle all AEM errors
if (json.error) {
- this.handleError(json, 'publish', this._sendButton);
+ this.handleError(json, 'publish');
return;
}
@@ -286,7 +319,7 @@ export default class DaTitle extends LitElement {
if (action === 'publish') saveDaVersion(fullpath, 'Published');
else if (action === 'preview') saveDaVersion(fullpath, 'Previewed');
}
- this._sendButton.classList.remove('is-sending');
+ this._isSending = false;
}
async handleRoleRequest() {
@@ -317,10 +350,6 @@ export default class DaTitle extends LitElement {
this._dialog = { title, content, action: closeAction };
}
- get _sendButton() {
- return this.shadowRoot.querySelector('.da-title-action-send-icon');
- }
-
get _canPrepare() {
return !!this.details.path;
}
@@ -337,7 +366,9 @@ export default class DaTitle extends LitElement {
`)}`;
@@ -406,10 +437,10 @@ export default class DaTitle extends LitElement {
${this.collabStatus ? this.renderCollab() : nothing}
${this._canPrepare ? html`` : nothing}
${this._status ? this.renderError() : nothing}
-
+
${this.renderActions()}
-