From c66521eb5c11c40330e49a6eceb00fb6db81c105 Mon Sep 17 00:00:00 2001 From: BrainFooLong Date: Tue, 9 Apr 2024 17:55:25 +0200 Subject: [PATCH] added new render target RENDER_TARGET_NONE_AND_CLOSE added new response receiver attribute removed login custom bg images added warmup after setup --- .../Framelix/dev/web-types/web-types.json | 17 +++++ .../modules/Framelix/js/form/framelix-form.js | 27 ++++--- .../modules/Framelix/js/framelix-request.js | 59 ++++++++++----- .../Framelix/public/dist/js/form.min.js | 22 +++--- .../Framelix/public/dist/js/general.min.js | 72 ++++++++++++------- .../FramelixTypeDefJsRequestOptions.js | 8 ++- appdata/modules/Framelix/src/Form/Form.php | 19 ++--- .../src/Html/TypeDefs/JsRequestOptions.php | 7 ++ .../Framelix/src/View/Backend/Login.php | 20 ------ .../Framelix/src/View/Backend/Setup.php | 29 +++++--- .../src/View/Backend/UserProfile/Fido2.php | 1 - .../FramelixDemo/src/View/Invoices.php | 2 - 12 files changed, 171 insertions(+), 112 deletions(-) diff --git a/appdata/modules/Framelix/dev/web-types/web-types.json b/appdata/modules/Framelix/dev/web-types/web-types.json index b691262..54036f0 100644 --- a/appdata/modules/Framelix/dev/web-types/web-types.json +++ b/appdata/modules/Framelix/dev/web-types/web-types.json @@ -6,6 +6,23 @@ "default-icon": "icon.png", "contributions": { "html": { + "attributes": [ + { + "name": "title", + "description": "Displays the content as a tooltip. Accepts a language key as well." + }, + { + "name": "data-toolip", + "description": "Alias for 'title'" + }, + { + "name": "data-request-response-receiver", + "description": "If set, this container will receive the request response if a child container does make an async request.", + "value": { + "kind": "no-value" + } + } + ], "elements": [ { "name": "framelix-button", diff --git a/appdata/modules/Framelix/js/form/framelix-form.js b/appdata/modules/Framelix/js/form/framelix-form.js index 4ad7342..8c6cdb9 100644 --- a/appdata/modules/Framelix/js/form/framelix-form.js +++ b/appdata/modules/Framelix/js/form/framelix-form.js @@ -93,7 +93,7 @@ class FramelixForm { * The target to render the submit response to * @type {FramelixTypeDefJsRequestOptions} */ - requestOptions = { url: '', renderTarget: 'currentcontext' } + requestOptions = { url: null, renderTarget: FramelixTypeDefJsRequestOptions.RENDER_TARGET_CURRENT_CONTEXT } /** * Submit the form async @@ -110,12 +110,6 @@ class FramelixForm { */ submitAsyncRaw = true - /** - * Execute the javascript code after form submit - * @var {string|null} - */ - executeAfterAsyncSubmit = null - /** * Submit the form with enter key * @type {boolean} @@ -818,7 +812,9 @@ class FramelixForm { } } this.hideValidationMessage() - let submitUrl = this.submitUrl + const requestOptions = this.requestOptions ? this.requestOptions : { url: null, renderTarget: 'currentcontext' } + let submitUrl = this.submitUrl || requestOptions.url + // if no submit url is defined, try to use the current tab context url if (!submitUrl) { const tabContent = this.form.closest('.framelix-tab-content') if (tabContent.length) { @@ -845,6 +841,13 @@ class FramelixForm { // if request does handle anything itself, do not proceed handling the request const responseCheckHeadersStatus = await request.checkHeaders() + + // handling closing modals, no matter what happen later + if (requestOptions && requestOptions.renderTarget === FramelixTypeDefJsRequestOptions.RENDER_TARGET_NONE_AND_CLOSE) { + FramelixModal.destroyAll() + FramelixPopup.destroyAll() + } + if (responseCheckHeadersStatus !== 0) { return true } @@ -884,13 +887,7 @@ class FramelixForm { } if (typeof responseData.buffer === 'string' && responseData.buffer.length) { - FramelixRequest.renderResponse(responseData.buffer, this.requestOptions, this.container[0]) - } - - if (this.executeAfterAsyncSubmit) { - await new Promise(function (resolve) { - eval('(async function(){' + self.executeAfterAsyncSubmit + ' resolve();})()') - }) + FramelixRequest.renderResponse(responseData.buffer, requestOptions, this.container[0]) } return true } diff --git a/appdata/modules/Framelix/js/framelix-request.js b/appdata/modules/Framelix/js/framelix-request.js index 2210a8e..423554b 100644 --- a/appdata/modules/Framelix/js/framelix-request.js +++ b/appdata/modules/Framelix/js/framelix-request.js @@ -209,36 +209,59 @@ class FramelixRequest { return null } initiatorElement = $(initiatorElement) - let parentCell = initiatorElement.closest('td') - let parentPopup = initiatorElement.closest('.framelix-popup') - let parentModal = initiatorElement.closest('.framelix-modal') - let parentTab = initiatorElement.closest('.framelix-tab-content') + let validTarget = false + if (!validTarget) { + let responseReceiver = initiatorElement.closest('td') + if (responseReceiver.length) { + validTarget = true + requestOptions.renderTarget = { elementSelector: responseReceiver } + } + } + + if (!validTarget) { + let responseReceiver = initiatorElement.closest('.framelix-popup') + if (responseReceiver.length) { + validTarget = true + requestOptions.renderTarget = { popupOptions: responseReceiver[0].framelixPopupInstance.options } + initiatorElement = responseReceiver[0].framelixPopupInstance.target + } + } - if (parentCell.length) { - requestOptions.renderTarget = { elementSelector: parentCell } - } else if (parentPopup.length) { - requestOptions.renderTarget = { popupOptions: parentPopup[0].framelixPopupInstance.options } - initiatorElement = parentPopup[0].framelixPopupInstance.target - } else if (parentModal.length) { - const modal = FramelixModal.instances[parentModal.attr('data-instance-id')] - if (modal) { - requestOptions.renderTarget = { modalOptions: { instance: modal } } + if (!validTarget) { + let responseReceiver = initiatorElement.closest('.framelix-modal') + if (responseReceiver.length) { + validTarget = true + const modal = FramelixModal.instances[responseReceiver.attr('data-instance-id')] + if (modal) { + requestOptions.renderTarget = { modalOptions: { instance: modal } } + } } - } else if (parentTab.length) { - const modal = FramelixModal.instances[parentModal.attr('data-instance-id')] - if (modal) { - requestOptions.renderTarget = { modalOptions: { instance: modal } } + } + + if (!validTarget) { + let responseReceiver = initiatorElement.closest('[data-request-response-receiver]') + if (responseReceiver.length) { + validTarget = true + requestOptions.renderTarget = { elementSelector: responseReceiver } } } } } + console.log(requestOptions) const isFixedResponse = !(request instanceof FramelixRequest) - if (!requestOptions.renderTarget) { + if (!requestOptions.renderTarget || requestOptions.renderTarget === FramelixTypeDefJsRequestOptions.RENDER_TARGET_NONE_AND_CLOSE) { if (!isFixedResponse) { Framelix.showProgressBar(1) request.checkHeaders().then(function () { Framelix.showProgressBar(null) + if (requestOptions.renderTarget === FramelixTypeDefJsRequestOptions.RENDER_TARGET_NONE_AND_CLOSE) { + FramelixPopup.destroyAll() + FramelixModal.destroyAll() + } }) + } else if (requestOptions.renderTarget === FramelixTypeDefJsRequestOptions.RENDER_TARGET_NONE_AND_CLOSE) { + FramelixPopup.destroyAll() + FramelixModal.destroyAll() } } else if (requestOptions.renderTarget.modalOptions) { let options = requestOptions.renderTarget.modalOptions diff --git a/appdata/modules/Framelix/public/dist/js/form.min.js b/appdata/modules/Framelix/public/dist/js/form.min.js index 7a057ed..0cc832a 100644 --- a/appdata/modules/Framelix/public/dist/js/form.min.js +++ b/appdata/modules/Framelix/public/dist/js/form.min.js @@ -17,12 +17,11 @@ class FramelixForm { submitMethod = 'post'; submitUrl = null; requestOptions = { - url: '', - renderTarget: 'currentcontext' + url: null, + renderTarget: FramelixTypeDefJsRequestOptions.RENDER_TARGET_CURRENT_CONTEXT }; submitAsync = true; submitAsyncRaw = true; - executeAfterAsyncSubmit = null; submitWithEnter = true; autocomplete = false; stickyFormButtons = false; @@ -530,7 +529,11 @@ class FramelixForm { } } this.hideValidationMessage(); - let submitUrl = this.submitUrl; + const requestOptions = this.requestOptions ? this.requestOptions : { + url: null, + renderTarget: 'currentcontext' + }; + let submitUrl = this.submitUrl || requestOptions.url; if (!submitUrl) { const tabContent = this.form.closest('.framelix-tab-content'); if (tabContent.length) { @@ -556,6 +559,10 @@ class FramelixForm { } self.hideValidationMessage(); const responseCheckHeadersStatus = await request.checkHeaders(); + if (requestOptions && requestOptions.renderTarget === FramelixTypeDefJsRequestOptions.RENDER_TARGET_NONE_AND_CLOSE) { + FramelixModal.destroyAll(); + FramelixPopup.destroyAll(); + } if (responseCheckHeadersStatus !== 0) { return true; } @@ -587,12 +594,7 @@ class FramelixForm { FramelixToast.showNext(); } if (typeof responseData.buffer === 'string' && responseData.buffer.length) { - FramelixRequest.renderResponse(responseData.buffer, this.requestOptions, this.container[0]); - } - if (this.executeAfterAsyncSubmit) { - await new Promise(function (resolve) { - eval('(async function(){' + self.executeAfterAsyncSubmit + ' resolve();})()'); - }); + FramelixRequest.renderResponse(responseData.buffer, requestOptions, this.container[0]); } return true; } diff --git a/appdata/modules/Framelix/public/dist/js/general.min.js b/appdata/modules/Framelix/public/dist/js/general.min.js index 2fe6a7b..a5edfcb 100644 --- a/appdata/modules/Framelix/public/dist/js/general.min.js +++ b/appdata/modules/Framelix/public/dist/js/general.min.js @@ -1628,47 +1628,66 @@ class FramelixRequest { return null; } initiatorElement = $(initiatorElement); - let parentCell = initiatorElement.closest('td'); - let parentPopup = initiatorElement.closest('.framelix-popup'); - let parentModal = initiatorElement.closest('.framelix-modal'); - let parentTab = initiatorElement.closest('.framelix-tab-content'); - if (parentCell.length) { - requestOptions.renderTarget = { - elementSelector: parentCell - }; - } else if (parentPopup.length) { - requestOptions.renderTarget = { - popupOptions: parentPopup[0].framelixPopupInstance.options - }; - initiatorElement = parentPopup[0].framelixPopupInstance.target; - } else if (parentModal.length) { - const modal = FramelixModal.instances[parentModal.attr('data-instance-id')]; - if (modal) { + let validTarget = false; + if (!validTarget) { + let responseReceiver = initiatorElement.closest('td'); + if (responseReceiver.length) { + validTarget = true; requestOptions.renderTarget = { - modalOptions: { - instance: modal - } + elementSelector: responseReceiver }; } - } else if (parentTab.length) { - const modal = FramelixModal.instances[parentModal.attr('data-instance-id')]; - if (modal) { + } + if (!validTarget) { + let responseReceiver = initiatorElement.closest('.framelix-popup'); + if (responseReceiver.length) { + validTarget = true; requestOptions.renderTarget = { - modalOptions: { - instance: modal - } + popupOptions: responseReceiver[0].framelixPopupInstance.options + }; + initiatorElement = responseReceiver[0].framelixPopupInstance.target; + } + } + if (!validTarget) { + let responseReceiver = initiatorElement.closest('.framelix-modal'); + if (responseReceiver.length) { + validTarget = true; + const modal = FramelixModal.instances[responseReceiver.attr('data-instance-id')]; + if (modal) { + requestOptions.renderTarget = { + modalOptions: { + instance: modal + } + }; + } + } + } + if (!validTarget) { + let responseReceiver = initiatorElement.closest('[data-request-response-receiver]'); + if (responseReceiver.length) { + validTarget = true; + requestOptions.renderTarget = { + elementSelector: responseReceiver }; } } } } + console.log(requestOptions); const isFixedResponse = !(request instanceof FramelixRequest); - if (!requestOptions.renderTarget) { + if (!requestOptions.renderTarget || requestOptions.renderTarget === FramelixTypeDefJsRequestOptions.RENDER_TARGET_NONE_AND_CLOSE) { if (!isFixedResponse) { Framelix.showProgressBar(1); request.checkHeaders().then(function () { Framelix.showProgressBar(null); + if (requestOptions.renderTarget === FramelixTypeDefJsRequestOptions.RENDER_TARGET_NONE_AND_CLOSE) { + FramelixPopup.destroyAll(); + FramelixModal.destroyAll(); + } }); + } else if (requestOptions.renderTarget === FramelixTypeDefJsRequestOptions.RENDER_TARGET_NONE_AND_CLOSE) { + FramelixPopup.destroyAll(); + FramelixModal.destroyAll(); } } else if (requestOptions.renderTarget.modalOptions) { let options = requestOptions.renderTarget.modalOptions; @@ -3048,6 +3067,7 @@ class FramelixTypeDefJsRequestOptions extends FramelixBaseTypeDef { static RENDER_TARGET_MODAL_NEW = "modalnew"; static RENDER_TARGET_POPUP = "popup"; static RENDER_TARGET_CURRENT_CONTEXT = "currentcontext"; + static RENDER_TARGET_NONE_AND_CLOSE = "none-close"; static toAttrValue(data) { return super.toAttrValue(data); } diff --git a/appdata/modules/Framelix/public/dist/typedefs/FramelixTypeDefJsRequestOptions.js b/appdata/modules/Framelix/public/dist/typedefs/FramelixTypeDefJsRequestOptions.js index a86f5b0..5477b25 100644 --- a/appdata/modules/Framelix/public/dist/typedefs/FramelixTypeDefJsRequestOptions.js +++ b/appdata/modules/Framelix/public/dist/typedefs/FramelixTypeDefJsRequestOptions.js @@ -19,6 +19,12 @@ class FramelixTypeDefJsRequestOptions extends FramelixBaseTypeDef { */ static RENDER_TARGET_CURRENT_CONTEXT = "currentcontext" + /** + * Same as "null", makes the request, but not render it, additionally it destroys all modals and popups to get back + * to the pages context + */ + static RENDER_TARGET_NONE_AND_CLOSE = "none-close" + /** * @param {FramelixTypeDefJsRequestOptions|Object} data * @return {string} @@ -41,7 +47,7 @@ class FramelixTypeDefJsRequestOptions extends FramelixBaseTypeDef { * The render target for the request * If null, it will make the request but not render it anywhere * Require any of the class constants starting with RENDER_TARGET_ - * @type {FramelixTypeDefJsRenderTarget|Object|null|("modalnew", "popup", "currentcontext")} + * @type {FramelixTypeDefJsRenderTarget|Object|null|("modalnew", "popup", "currentcontext", "none-close")} */ renderTarget = null diff --git a/appdata/modules/Framelix/src/Form/Form.php b/appdata/modules/Framelix/src/Form/Form.php index a2096ef..a45c9c4 100644 --- a/appdata/modules/Framelix/src/Form/Form.php +++ b/appdata/modules/Framelix/src/Form/Form.php @@ -2,11 +2,11 @@ namespace Framelix\Framelix\Form; -use Framelix\Framelix\Enums\ButtonColor; use Framelix\Framelix\Form\Field\File; use Framelix\Framelix\Html\HtmlAttributes; use Framelix\Framelix\Html\PhpToJsData; use Framelix\Framelix\Html\TypeDefs\ElementColor; +use Framelix\Framelix\Html\TypeDefs\JsRequestOptions; use Framelix\Framelix\Lang; use Framelix\Framelix\Network\Request; use Framelix\Framelix\Network\Response; @@ -69,11 +69,20 @@ class Form implements JsonSerializable /** * The url to submit to - * If null then it is the current url + * If null then it is the current url or current tag * @var Url|View|string|null */ public Url|View|string|null $submitUrl = null; + /** + * The request options instead of submitUrl + * @var JsRequestOptions|array|null + */ + public JsRequestOptions|array|null $requestOptions = [ + 'url' => null, + 'renderTarget' => JsRequestOptions::RENDER_TARGET_CURRENT_CONTEXT, + ]; + /** * Submit the form async * If false then the form will be submitted with native form submit features (new page load) @@ -89,12 +98,6 @@ class Form implements JsonSerializable */ public bool $submitAsyncRaw = false; - /** - * Execute the javascript code after form submit - * @var string|null - */ - public ?string $executeAfterAsyncSubmit = null; - /** * Validation message to show in the frontend * @var string|null diff --git a/appdata/modules/Framelix/src/Html/TypeDefs/JsRequestOptions.php b/appdata/modules/Framelix/src/Html/TypeDefs/JsRequestOptions.php index 09c7134..1e1132e 100644 --- a/appdata/modules/Framelix/src/Html/TypeDefs/JsRequestOptions.php +++ b/appdata/modules/Framelix/src/Html/TypeDefs/JsRequestOptions.php @@ -31,6 +31,12 @@ class JsRequestOptions extends BaseTypeDef */ public const string RENDER_TARGET_CURRENT_CONTEXT = "currentcontext"; + /** + * Same as "null", makes the request, but not render it, additionally it destroys all modals and popups to get back + * to the pages context + */ + public const string RENDER_TARGET_NONE_AND_CLOSE = "none-close"; + public function __construct( /** * The url to load for the request @@ -51,6 +57,7 @@ public function __construct( self::RENDER_TARGET_MODAL_NEW, self::RENDER_TARGET_CURRENT_CONTEXT, self::RENDER_TARGET_POPUP, + self::RENDER_TARGET_NONE_AND_CLOSE, null, ])] public JsRenderTarget|string|null $renderTarget = null, diff --git a/appdata/modules/Framelix/src/View/Backend/Login.php b/appdata/modules/Framelix/src/View/Backend/Login.php index 2b38608..3367f51 100644 --- a/appdata/modules/Framelix/src/View/Backend/Login.php +++ b/appdata/modules/Framelix/src/View/Backend/Login.php @@ -30,10 +30,6 @@ class Login extends View { - public static string $imgBgDark = __DIR__ . "/../../../public/img/bg-login-dark.webp"; - - public static string $imgBgLight = __DIR__ . "/../../../public/img/bg-login-light.webp"; - protected string|bool $accessRole = "*"; public static function onJsCall(JsCall $jsCall): void @@ -173,22 +169,6 @@ public function showContent(): void framelix-button:not(.fido2-enabled)[data-action='fido2'] { display: none; } - html[data-layout="2"][data-color-scheme=dark] .framelix-page { - background: url() center no-repeat; - background-size: cover; - } - html[data-layout="2"][data-color-scheme=dark] .framelix-content-inner-inner { - backdrop-filter: blur(12px); - background: rgba(0, 0, 0, 0.7); - } - html[data-layout="2"][data-color-scheme=light] .framelix-page { - background: url() center no-repeat; - background-size: cover; - } - html[data-layout="2"][data-color-scheme=light] .framelix-content-inner-inner { - backdrop-filter: blur(12px); - background: rgba(255, 255, 255, 0.6); - } + minLength = 8; $form->addField($field); - return $form; } + } \ No newline at end of file diff --git a/appdata/modules/Framelix/src/View/Backend/UserProfile/Fido2.php b/appdata/modules/Framelix/src/View/Backend/UserProfile/Fido2.php index 8ced00e..49f0633 100644 --- a/appdata/modules/Framelix/src/View/Backend/UserProfile/Fido2.php +++ b/appdata/modules/Framelix/src/View/Backend/UserProfile/Fido2.php @@ -2,7 +2,6 @@ namespace Framelix\Framelix\View\Backend\UserProfile; -use Framelix\Framelix\Enums\ButtonColor; use Framelix\Framelix\Form\Form; use Framelix\Framelix\Html\Toast; use Framelix\Framelix\Html\TypeDefs\ElementColor; diff --git a/appdata/modules/FramelixDemo/src/View/Invoices.php b/appdata/modules/FramelixDemo/src/View/Invoices.php index f57e8c0..66632f6 100644 --- a/appdata/modules/FramelixDemo/src/View/Invoices.php +++ b/appdata/modules/FramelixDemo/src/View/Invoices.php @@ -80,8 +80,6 @@ public static function onJsCall(JsCall $jsCall): void $form->addField($field); $form->addSubmitButton('pdf-download', '__framelixdemo_storable_invoice_download__', '709'); - $form->executeAfterAsyncSubmit = /** @lang JavaScript */ - 'await FramelixModal.destroyAll()'; $form->show(); break; }