Skip to content

Commit

Permalink
Merge pull request #1568 from bartbutenaers/ui-page-selectable-flex-l…
Browse files Browse the repository at this point in the history
…ayout

ui-page: layout editor button
  • Loading branch information
joepavitt authored Jan 10, 2025
2 parents 11c4f0a + f83430d commit a4ef6a5
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 66 deletions.
1 change: 1 addition & 0 deletions nodes/config/locales/en-US/ui_page.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"icon": "Icon",
"theme": "Theme",
"layout": "Layout",
"editLayout": "Edit Layout",
"grid": "Grid",
"fixed": "Fixed",
"tabs": "Tabs",
Expand Down
135 changes: 71 additions & 64 deletions nodes/config/ui_base.html
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,56 @@
/** @typedef {Object<string, Array<DashboardItem>>} DashboardItemLookup */
// #endregion

// Custom global sidebar function used in other nodes
async function showDashboardWysiwygEditor (pageId, baseId) {
// call to httpadmin endpoint requesting layout editor mode for this group
const windowUrl = new URL(window.location.href)
const base = RED.nodes.node(baseId)
const dashboardBasePath = (base.path || 'dashboard').replace(/\/$/, '').replace(/^\/+/, '')
const authTokens = RED.settings.get('auth-tokens') || {}
const headers = {}
if (authTokens.access_token) {
headers.Authorization = `${authTokens.token_type || 'Bearer'} ${authTokens.access_token}`
}

// promisify the ajax call so we can await it & return false after opening the popup
const ajax = () => {
return new Promise((resolve, reject) => {
$.ajax({
url: `${dashboardBasePath}/api/v1/${baseId}/edit/${pageId}`, // e.g. /dashboard/api/v1/123/edit/456
type: 'PATCH',
headers,
data: {
mode: 'edit',
dashboard: baseId,
page: pageId
},
success: function (data) {
// construct the url to open the layout editor
const _url = new URL(`${dashboardBasePath}/${data.path}`.replace(/\/\//g, '/'), windowUrl.origin)
_url.searchParams.set('edit-key', data.editKey)
const editorPath = windowUrl.pathname?.replace(/\/$/, '').replace(/^\/+/, '') || ''
if (editorPath) {
_url.searchParams.set('editor-path', editorPath)
}
resolve(_url)
},
error: function (err) {
reject(err)
}
})
})
}
try {
const url = await ajax()
const target = `ff-dashboard-${baseId}` // try to reuse the same window per base
window.open(url, target)
} catch (err) {
console.error('layout mode error', err)
RED.notify('Unable to begin layout editor', 'error')
}
}

(function () {
const supportedEditableLayouts = ['grid', 'flex']
const sidebarContainer = '<div style="position: relative; height: 100%;"></div>'
Expand Down Expand Up @@ -373,13 +423,12 @@
// backward compatibility for including header content
// The headerContent replaces the old (boolean) showPageTitle
if (this.headerContent === undefined) {
let showPageTitle = (this.showPageTitle === undefined) ? true : this.showPageTitle
const showPageTitle = (this.showPageTitle === undefined) ? true : this.showPageTitle

if (showPageTitle) {
this.headerContent = 'page'
$('#node-config-input-headerContent').val('page')
}
else {
} else {
this.headerContent = 'none'
$('#node-config-input-headerContent').val('none')
}
Expand Down Expand Up @@ -1150,71 +1199,29 @@
evt.preventDefault()
})

if (item.type === 'ui-page' && supportedEditableLayouts.includes(item.node.layout)) {
// button to edit group in wysiwyg layout editor
const layoutButton = $(`<a class="nr-db-sb-tab-layout-button editor-button editor-button-small nr-db-sb-list-header-button" title="${c_('layout.layoutEditor')}"><i class="fa fa-pencil-square-o"></i></a>`).appendTo(btnGroup)
layoutButton.on('click', async function (evt) {
if (item.type === 'ui-page') {
// add the "+ group" button
const groupEditButton = $(`<a href="#" class="editor-button editor-button-small nr-db-sb-list-header-button" title="${c_('layout.addGroup')}"><i class="fa fa-plus"></i></a>`).appendTo(btnGroup)
groupEditButton.on('click', function (evt) {
list.editableList('addItem')
evt.preventDefault()
evt.stopPropagation()
})

// call to httpadmin endpoint requesting layout editor mode for this group
const windowUrl = new URL(window.location.href)
const pageId = item.id
const baseId = item.node.ui
const base = RED.nodes.node(item.node.ui)
const dashboardBasePath = (base.path || 'dashboard').replace(/\/$/, '').replace(/^\/+/, '')
const authTokens = RED.settings.get('auth-tokens') || {}
const headers = {}
if (authTokens.access_token) {
headers.Authorization = `${authTokens.token_type || 'Bearer'} ${authTokens.access_token}`
}
if (supportedEditableLayouts.includes(item.node.layout)) {
// button to edit group in wysiwyg layout editor
const layoutButton = $(`<a class="nr-db-sb-tab-layout-button editor-button editor-button-small nr-db-sb-list-header-button" title="${c_('layout.layoutEditor')}"><i class="fa fa-pencil-square-o"></i></a>`).appendTo(btnGroup)
layoutButton.on('click', function (evt) {
evt.preventDefault()
evt.stopPropagation()

// promisify the ajax call so we can await it & return false after opening the popup
const ajax = () => {
return new Promise((resolve, reject) => {
$.ajax({
url: `${dashboardBasePath}/api/v1/${baseId}/edit/${pageId}`, // e.g. /dashboard/api/v1/123/edit/456
type: 'PATCH',
headers,
data: {
mode: 'edit',
dashboard: baseId,
page: pageId
},
success: function (data) {
// construct the url to open the layout editor
const _url = new URL(`${dashboardBasePath}/${data.path}`.replace(/\/\//g, '/'), windowUrl.origin)
_url.searchParams.set('edit-key', data.editKey)
const editorPath = windowUrl.pathname?.replace(/\/$/, '').replace(/^\/+/, '') || ''
if (editorPath) {
_url.searchParams.set('editor-path', editorPath)
}
resolve(_url)
},
error: function (err) {
reject(err)
}
})
})
}
try {
const url = await ajax()
const target = `ff-dashboard-${baseId}` // try to reuse the same window per base
window.open(url, target)
} catch (err) {
console.error('layout mode error', err)
RED.notify('Unable to begin layout editor', 'error')
}
return false // return false to click event to prevent default
})
}
const pageId = item.id
const baseId = item.node.ui
showDashboardWysiwygEditor(pageId, baseId)

// add the "+ group" button
const groupEditButton = $(`<a href="#" class="editor-button editor-button-small nr-db-sb-list-header-button" title="${c_('layout.addGroup')}"><i class="fa fa-plus"></i></a>`).appendTo(btnGroup)
groupEditButton.on('click', function (evt) {
list.editableList('addItem')
evt.preventDefault()
})
return false // return false to click event to prevent default
})
}
}

// if this is a group & it is not an unattached group, add the "_ spacer" button
if (item.type === 'ui-group' && !!item.page) {
Expand Down
27 changes: 25 additions & 2 deletions nodes/config/ui_page.html
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@
disabled: { value: 'false' }
},
oneditprepare: function () {
const node = this

if (this.path === -1) {
// we have no path set yet
let pageCount = 0
Expand Down Expand Up @@ -115,6 +117,19 @@
}
})

// button to edit page layout in wysiwyg layout editor
$('#node-config-layout-edit').on('click', function (evt) {
evt.preventDefault()
evt.stopPropagation()

const pageId = node.id
const baseNode = RED.nodes.node(node.ui)
// eslint-disable-next-line no-undef
showDashboardWysiwygEditor(pageId, baseNode.id) // defined in UI Base as a Global

return false // return false to click event to prevent default
})

// Breakpoints Editable List
const breakpointsList = $('#node-config-list-breakpoints').editableList({
header: $('<div>').addClass('node-config-list-breakpoints-header')
Expand Down Expand Up @@ -276,8 +291,16 @@
</div>
<div class="form-row">
<label for="node-config-input-layout"><i class="w-16 fa fa-th"></i> <span data-i18n="ui-page.label.layout"></label>
<input type="text" id="node-config-input-layout">
<input type="hidden" id="node-config-input-layoutType">
<div style="width: 70%; display: inline-flex;">
<div style="flex-grow: 1;">
<input type="text" id="node-config-input-layout" style="width: 100%;">
<input type="hidden" id="node-config-input-layoutType">
</div>
<button type="button" id="node-config-layout-edit" class="red-ui-button" style="margin-left: 10px;" title="Edit Layout">
<i class="fa fa-pencil-square-o"></i>
<span data-i18n="ui-page.label.editLayout"></span>
</button>
</div>
</div>
<div class="form-row" id="text-row-class">
<label for="node-config-input-className"><i class="w-16 fa fa-code"></i> <span data-i18n="ui-page.label.class"></label>
Expand Down

0 comments on commit a4ef6a5

Please sign in to comment.