mirror of
https://github.com/WordPress/five-for-the-future.git
synced 2025-04-18 17:33:43 +03:00
Manage Pledge: Update email dialog to use JS submission (#104)
Use the proper dialog behavior for submitting an email to generate a manage link. Fixes #98.
This commit is contained in:
parent
a67ef04505
commit
a11e3c5fa5
136
plugins/wporg-5ftf/assets/js/frontend.js
Normal file
136
plugins/wporg-5ftf/assets/js/frontend.js
Normal file
|
@ -0,0 +1,136 @@
|
|||
/* global FiveForTheFuture, jQuery */
|
||||
jQuery( document ).ready( function( $ ) {
|
||||
const button = document.getElementById( 'toggle-management-link-form' );
|
||||
const template = wp.template( '5ftf-send-link-dialog' );
|
||||
|
||||
if ( ! template && ! button ) {
|
||||
// No modal on this page.
|
||||
return;
|
||||
}
|
||||
|
||||
$( document.body ).prepend( template() );
|
||||
const modal = document.getElementById( 'send-link-dialog' );
|
||||
const modalBg = document.getElementById( 'send-link-dialog-bg' );
|
||||
const children = document.querySelectorAll( 'body > *:not([role="dialog"])' );
|
||||
|
||||
/**
|
||||
* Get the top/left position for the modal, based on the button location.
|
||||
*/
|
||||
function getModalPosition() {
|
||||
const offsetTop = ( 'number' === typeof window.scrollY ) ? window.scrollY : window.pageYOffset;
|
||||
const offsetLeft = ( 'number' === typeof window.scrollX ) ? window.scrollX : window.pageXOffset;
|
||||
const bounds = button.getBoundingClientRect();
|
||||
const modalWidth = 300; // Modal width is hardcoded, because it's not visible yet.
|
||||
|
||||
return {
|
||||
top: bounds.y + offsetTop + bounds.height,
|
||||
left: bounds.x + offsetLeft + bounds.width - modalWidth,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Open the modal.
|
||||
*/
|
||||
function openModal() {
|
||||
const position = getModalPosition();
|
||||
// Hide other content on this page while modal is open.
|
||||
for ( let i = 0; i < children.length; i++ ) {
|
||||
if ( children[ i ].hasAttribute( 'data-no-inert' ) ) {
|
||||
continue;
|
||||
}
|
||||
if ( children[ i ].getAttribute( 'inert' ) ) {
|
||||
children[ i ].setAttribute( 'data-keep-inert', '' );
|
||||
} else {
|
||||
children[ i ].setAttribute( 'inert', 'true' );
|
||||
}
|
||||
}
|
||||
|
||||
modal.removeAttribute( 'hidden' );
|
||||
modalBg.removeAttribute( 'hidden' );
|
||||
modal.style.top = position.top + 'px';
|
||||
modal.style.left = position.left + 'px';
|
||||
modal.focus();
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the modal.
|
||||
*/
|
||||
function closeModal() {
|
||||
// Reveal content again.
|
||||
for ( let i = 0; i < children.length; i++ ) {
|
||||
if ( ! children[ i ].hasAttribute( 'data-keep-inert' ) ) {
|
||||
children[ i ].removeAttribute( 'inert' );
|
||||
}
|
||||
|
||||
children[ i ].removeAttribute( 'data-keep-inert' );
|
||||
}
|
||||
|
||||
modal.hidden = true;
|
||||
modalBg.hidden = true;
|
||||
|
||||
// Wait a tick before setting focus. See https://github.com/WICG/inert#performance-and-gotchas
|
||||
setTimeout( function() {
|
||||
if ( button ) {
|
||||
button.focus();
|
||||
} else {
|
||||
document.body.focus();
|
||||
}
|
||||
}, 0 );
|
||||
}
|
||||
|
||||
function sendRequest( event ) {
|
||||
event.preventDefault();
|
||||
const email = $( event.target.querySelector( 'input[type="email"]' ) ).val();
|
||||
$( event.target.querySelector( '.message' ) ).html( '' );
|
||||
$.ajax( {
|
||||
type: 'POST',
|
||||
url: FiveForTheFuture.ajaxurl,
|
||||
data: {
|
||||
action: 'send-manage-email',
|
||||
pledge_id: FiveForTheFuture.pledgeId,
|
||||
email,
|
||||
_ajax_nonce: FiveForTheFuture.ajaxNonce,
|
||||
},
|
||||
success( response ) {
|
||||
if ( response.message ) {
|
||||
// Say the message for screen reader users.
|
||||
wp.a11y.speak( response.message );
|
||||
|
||||
if ( response.success ) {
|
||||
closeModal();
|
||||
$( button ).after( $( '<p>' ).html( '<em>' + response.message + '<em>' ) );
|
||||
} else {
|
||||
const $message = $( '<div>' )
|
||||
.addClass( 'notice notice-alt' )
|
||||
.addClass( response.success ? 'notice-success' : 'notice-error' )
|
||||
.append( $( '<p>' ).html( response.message ) );
|
||||
|
||||
$( event.target.querySelector( '.message' ) ).html( $message );
|
||||
}
|
||||
}
|
||||
},
|
||||
dataType: 'json',
|
||||
} );
|
||||
}
|
||||
|
||||
// Initialize.
|
||||
$( button ).on( 'click', function( event ) {
|
||||
event.preventDefault();
|
||||
|
||||
if ( !! modal.hidden ) {
|
||||
openModal( event );
|
||||
} else {
|
||||
closeModal( event );
|
||||
}
|
||||
} );
|
||||
|
||||
$( modalBg ).on( 'click', closeModal );
|
||||
$( modal ).on( 'click', '.pledge-dialog__close', closeModal );
|
||||
$( document ).on( 'keydown', function( event ) {
|
||||
if ( 27 === event.which ) { // Esc
|
||||
closeModal( event );
|
||||
}
|
||||
} );
|
||||
|
||||
$( modal.querySelector( 'form' ) ).submit( sendRequest );
|
||||
} );
|
833
plugins/wporg-5ftf/assets/js/inert.js
Normal file
833
plugins/wporg-5ftf/assets/js/inert.js
Normal file
|
@ -0,0 +1,833 @@
|
|||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? factory() :
|
||||
typeof define === 'function' && define.amd ? define('inert', factory) :
|
||||
(factory());
|
||||
}(this, (function () { 'use strict';
|
||||
|
||||
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
|
||||
|
||||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
||||
|
||||
/**
|
||||
* This work is licensed under the W3C Software and Document License
|
||||
* (http://www.w3.org/Consortium/Legal/2015/copyright-software-and-document).
|
||||
*/
|
||||
|
||||
// Convenience function for converting NodeLists.
|
||||
/** @type {typeof Array.prototype.slice} */
|
||||
var slice = Array.prototype.slice;
|
||||
|
||||
/**
|
||||
* IE has a non-standard name for "matches".
|
||||
* @type {typeof Element.prototype.matches}
|
||||
*/
|
||||
var matches = Element.prototype.matches || Element.prototype.msMatchesSelector;
|
||||
|
||||
/** @type {string} */
|
||||
var _focusableElementsString = ['a[href]', 'area[href]', 'input:not([disabled])', 'select:not([disabled])', 'textarea:not([disabled])', 'button:not([disabled])', 'iframe', 'object', 'embed', '[contenteditable]'].join(',');
|
||||
|
||||
/**
|
||||
* `InertRoot` manages a single inert subtree, i.e. a DOM subtree whose root element has an `inert`
|
||||
* attribute.
|
||||
*
|
||||
* Its main functions are:
|
||||
*
|
||||
* - to create and maintain a set of managed `InertNode`s, including when mutations occur in the
|
||||
* subtree. The `makeSubtreeUnfocusable()` method handles collecting `InertNode`s via registering
|
||||
* each focusable node in the subtree with the singleton `InertManager` which manages all known
|
||||
* focusable nodes within inert subtrees. `InertManager` ensures that a single `InertNode`
|
||||
* instance exists for each focusable node which has at least one inert root as an ancestor.
|
||||
*
|
||||
* - to notify all managed `InertNode`s when this subtree stops being inert (i.e. when the `inert`
|
||||
* attribute is removed from the root node). This is handled in the destructor, which calls the
|
||||
* `deregister` method on `InertManager` for each managed inert node.
|
||||
*/
|
||||
|
||||
var InertRoot = function () {
|
||||
/**
|
||||
* @param {!Element} rootElement The Element at the root of the inert subtree.
|
||||
* @param {!InertManager} inertManager The global singleton InertManager object.
|
||||
*/
|
||||
function InertRoot(rootElement, inertManager) {
|
||||
_classCallCheck(this, InertRoot);
|
||||
|
||||
/** @type {!InertManager} */
|
||||
this._inertManager = inertManager;
|
||||
|
||||
/** @type {!Element} */
|
||||
this._rootElement = rootElement;
|
||||
|
||||
/**
|
||||
* @type {!Set<!InertNode>}
|
||||
* All managed focusable nodes in this InertRoot's subtree.
|
||||
*/
|
||||
this._managedNodes = new Set();
|
||||
|
||||
// Make the subtree hidden from assistive technology
|
||||
if (this._rootElement.hasAttribute('aria-hidden')) {
|
||||
/** @type {?string} */
|
||||
this._savedAriaHidden = this._rootElement.getAttribute('aria-hidden');
|
||||
} else {
|
||||
this._savedAriaHidden = null;
|
||||
}
|
||||
this._rootElement.setAttribute('aria-hidden', 'true');
|
||||
|
||||
// Make all focusable elements in the subtree unfocusable and add them to _managedNodes
|
||||
this._makeSubtreeUnfocusable(this._rootElement);
|
||||
|
||||
// Watch for:
|
||||
// - any additions in the subtree: make them unfocusable too
|
||||
// - any removals from the subtree: remove them from this inert root's managed nodes
|
||||
// - attribute changes: if `tabindex` is added, or removed from an intrinsically focusable
|
||||
// element, make that node a managed node.
|
||||
this._observer = new MutationObserver(this._onMutation.bind(this));
|
||||
this._observer.observe(this._rootElement, { attributes: true, childList: true, subtree: true });
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this whenever this object is about to become obsolete. This unwinds all of the state
|
||||
* stored in this object and updates the state of all of the managed nodes.
|
||||
*/
|
||||
|
||||
|
||||
_createClass(InertRoot, [{
|
||||
key: 'destructor',
|
||||
value: function destructor() {
|
||||
this._observer.disconnect();
|
||||
|
||||
if (this._rootElement) {
|
||||
if (this._savedAriaHidden !== null) {
|
||||
this._rootElement.setAttribute('aria-hidden', this._savedAriaHidden);
|
||||
} else {
|
||||
this._rootElement.removeAttribute('aria-hidden');
|
||||
}
|
||||
}
|
||||
|
||||
this._managedNodes.forEach(function (inertNode) {
|
||||
this._unmanageNode(inertNode.node);
|
||||
}, this);
|
||||
|
||||
// Note we cast the nulls to the ANY type here because:
|
||||
// 1) We want the class properties to be declared as non-null, or else we
|
||||
// need even more casts throughout this code. All bets are off if an
|
||||
// instance has been destroyed and a method is called.
|
||||
// 2) We don't want to cast "this", because we want type-aware optimizations
|
||||
// to know which properties we're setting.
|
||||
this._observer = /** @type {?} */null;
|
||||
this._rootElement = /** @type {?} */null;
|
||||
this._managedNodes = /** @type {?} */null;
|
||||
this._inertManager = /** @type {?} */null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {!Set<!InertNode>} A copy of this InertRoot's managed nodes set.
|
||||
*/
|
||||
|
||||
}, {
|
||||
key: '_makeSubtreeUnfocusable',
|
||||
|
||||
|
||||
/**
|
||||
* @param {!Node} startNode
|
||||
*/
|
||||
value: function _makeSubtreeUnfocusable(startNode) {
|
||||
var _this2 = this;
|
||||
|
||||
composedTreeWalk(startNode, function (node) {
|
||||
return _this2._visitNode(node);
|
||||
});
|
||||
|
||||
var activeElement = document.activeElement;
|
||||
|
||||
if (!document.body.contains(startNode)) {
|
||||
// startNode may be in shadow DOM, so find its nearest shadowRoot to get the activeElement.
|
||||
var node = startNode;
|
||||
/** @type {!ShadowRoot|undefined} */
|
||||
var root = undefined;
|
||||
while (node) {
|
||||
if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
|
||||
root = /** @type {!ShadowRoot} */node;
|
||||
break;
|
||||
}
|
||||
node = node.parentNode;
|
||||
}
|
||||
if (root) {
|
||||
activeElement = root.activeElement;
|
||||
}
|
||||
}
|
||||
if (startNode.contains(activeElement)) {
|
||||
activeElement.blur();
|
||||
// In IE11, if an element is already focused, and then set to tabindex=-1
|
||||
// calling blur() will not actually move the focus.
|
||||
// To work around this we call focus() on the body instead.
|
||||
if (activeElement === document.activeElement) {
|
||||
document.body.focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {!Node} node
|
||||
*/
|
||||
|
||||
}, {
|
||||
key: '_visitNode',
|
||||
value: function _visitNode(node) {
|
||||
if (node.nodeType !== Node.ELEMENT_NODE) {
|
||||
return;
|
||||
}
|
||||
var element = /** @type {!Element} */node;
|
||||
|
||||
// If a descendant inert root becomes un-inert, its descendants will still be inert because of
|
||||
// this inert root, so all of its managed nodes need to be adopted by this InertRoot.
|
||||
if (element !== this._rootElement && element.hasAttribute('inert')) {
|
||||
this._adoptInertRoot(element);
|
||||
}
|
||||
|
||||
if (matches.call(element, _focusableElementsString) || element.hasAttribute('tabindex')) {
|
||||
this._manageNode(element);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the given node with this InertRoot and with InertManager.
|
||||
* @param {!Node} node
|
||||
*/
|
||||
|
||||
}, {
|
||||
key: '_manageNode',
|
||||
value: function _manageNode(node) {
|
||||
var inertNode = this._inertManager.register(node, this);
|
||||
this._managedNodes.add(inertNode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister the given node with this InertRoot and with InertManager.
|
||||
* @param {!Node} node
|
||||
*/
|
||||
|
||||
}, {
|
||||
key: '_unmanageNode',
|
||||
value: function _unmanageNode(node) {
|
||||
var inertNode = this._inertManager.deregister(node, this);
|
||||
if (inertNode) {
|
||||
this._managedNodes['delete'](inertNode);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister the entire subtree starting at `startNode`.
|
||||
* @param {!Node} startNode
|
||||
*/
|
||||
|
||||
}, {
|
||||
key: '_unmanageSubtree',
|
||||
value: function _unmanageSubtree(startNode) {
|
||||
var _this3 = this;
|
||||
|
||||
composedTreeWalk(startNode, function (node) {
|
||||
return _this3._unmanageNode(node);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* If a descendant node is found with an `inert` attribute, adopt its managed nodes.
|
||||
* @param {!Element} node
|
||||
*/
|
||||
|
||||
}, {
|
||||
key: '_adoptInertRoot',
|
||||
value: function _adoptInertRoot(node) {
|
||||
var inertSubroot = this._inertManager.getInertRoot(node);
|
||||
|
||||
// During initialisation this inert root may not have been registered yet,
|
||||
// so register it now if need be.
|
||||
if (!inertSubroot) {
|
||||
this._inertManager.setInert(node, true);
|
||||
inertSubroot = this._inertManager.getInertRoot(node);
|
||||
}
|
||||
|
||||
inertSubroot.managedNodes.forEach(function (savedInertNode) {
|
||||
this._manageNode(savedInertNode.node);
|
||||
}, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback used when mutation observer detects subtree additions, removals, or attribute changes.
|
||||
* @param {!Array<!MutationRecord>} records
|
||||
* @param {!MutationObserver} self
|
||||
*/
|
||||
|
||||
}, {
|
||||
key: '_onMutation',
|
||||
value: function _onMutation(records, self) {
|
||||
records.forEach(function (record) {
|
||||
var target = /** @type {!Element} */record.target;
|
||||
if (record.type === 'childList') {
|
||||
// Manage added nodes
|
||||
slice.call(record.addedNodes).forEach(function (node) {
|
||||
this._makeSubtreeUnfocusable(node);
|
||||
}, this);
|
||||
|
||||
// Un-manage removed nodes
|
||||
slice.call(record.removedNodes).forEach(function (node) {
|
||||
this._unmanageSubtree(node);
|
||||
}, this);
|
||||
} else if (record.type === 'attributes') {
|
||||
if (record.attributeName === 'tabindex') {
|
||||
// Re-initialise inert node if tabindex changes
|
||||
this._manageNode(target);
|
||||
} else if (target !== this._rootElement && record.attributeName === 'inert' && target.hasAttribute('inert')) {
|
||||
// If a new inert root is added, adopt its managed nodes and make sure it knows about the
|
||||
// already managed nodes from this inert subroot.
|
||||
this._adoptInertRoot(target);
|
||||
var inertSubroot = this._inertManager.getInertRoot(target);
|
||||
this._managedNodes.forEach(function (managedNode) {
|
||||
if (target.contains(managedNode.node)) {
|
||||
inertSubroot._manageNode(managedNode.node);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
}, {
|
||||
key: 'managedNodes',
|
||||
get: function get() {
|
||||
return new Set(this._managedNodes);
|
||||
}
|
||||
|
||||
/** @return {boolean} */
|
||||
|
||||
}, {
|
||||
key: 'hasSavedAriaHidden',
|
||||
get: function get() {
|
||||
return this._savedAriaHidden !== null;
|
||||
}
|
||||
|
||||
/** @param {?string} ariaHidden */
|
||||
|
||||
}, {
|
||||
key: 'savedAriaHidden',
|
||||
set: function set(ariaHidden) {
|
||||
this._savedAriaHidden = ariaHidden;
|
||||
}
|
||||
|
||||
/** @return {?string} */
|
||||
,
|
||||
get: function get() {
|
||||
return this._savedAriaHidden;
|
||||
}
|
||||
}]);
|
||||
|
||||
return InertRoot;
|
||||
}();
|
||||
|
||||
/**
|
||||
* `InertNode` initialises and manages a single inert node.
|
||||
* A node is inert if it is a descendant of one or more inert root elements.
|
||||
*
|
||||
* On construction, `InertNode` saves the existing `tabindex` value for the node, if any, and
|
||||
* either removes the `tabindex` attribute or sets it to `-1`, depending on whether the element
|
||||
* is intrinsically focusable or not.
|
||||
*
|
||||
* `InertNode` maintains a set of `InertRoot`s which are descendants of this `InertNode`. When an
|
||||
* `InertRoot` is destroyed, and calls `InertManager.deregister()`, the `InertManager` notifies the
|
||||
* `InertNode` via `removeInertRoot()`, which in turn destroys the `InertNode` if no `InertRoot`s
|
||||
* remain in the set. On destruction, `InertNode` reinstates the stored `tabindex` if one exists,
|
||||
* or removes the `tabindex` attribute if the element is intrinsically focusable.
|
||||
*/
|
||||
|
||||
|
||||
var InertNode = function () {
|
||||
/**
|
||||
* @param {!Node} node A focusable element to be made inert.
|
||||
* @param {!InertRoot} inertRoot The inert root element associated with this inert node.
|
||||
*/
|
||||
function InertNode(node, inertRoot) {
|
||||
_classCallCheck(this, InertNode);
|
||||
|
||||
/** @type {!Node} */
|
||||
this._node = node;
|
||||
|
||||
/** @type {boolean} */
|
||||
this._overrodeFocusMethod = false;
|
||||
|
||||
/**
|
||||
* @type {!Set<!InertRoot>} The set of descendant inert roots.
|
||||
* If and only if this set becomes empty, this node is no longer inert.
|
||||
*/
|
||||
this._inertRoots = new Set([inertRoot]);
|
||||
|
||||
/** @type {?number} */
|
||||
this._savedTabIndex = null;
|
||||
|
||||
/** @type {boolean} */
|
||||
this._destroyed = false;
|
||||
|
||||
// Save any prior tabindex info and make this node untabbable
|
||||
this.ensureUntabbable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this whenever this object is about to become obsolete.
|
||||
* This makes the managed node focusable again and deletes all of the previously stored state.
|
||||
*/
|
||||
|
||||
|
||||
_createClass(InertNode, [{
|
||||
key: 'destructor',
|
||||
value: function destructor() {
|
||||
this._throwIfDestroyed();
|
||||
|
||||
if (this._node && this._node.nodeType === Node.ELEMENT_NODE) {
|
||||
var element = /** @type {!Element} */this._node;
|
||||
if (this._savedTabIndex !== null) {
|
||||
element.setAttribute('tabindex', this._savedTabIndex);
|
||||
} else {
|
||||
element.removeAttribute('tabindex');
|
||||
}
|
||||
|
||||
// Use `delete` to restore native focus method.
|
||||
if (this._overrodeFocusMethod) {
|
||||
delete element.focus;
|
||||
}
|
||||
}
|
||||
|
||||
// See note in InertRoot.destructor for why we cast these nulls to ANY.
|
||||
this._node = /** @type {?} */null;
|
||||
this._inertRoots = /** @type {?} */null;
|
||||
this._destroyed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {boolean} Whether this object is obsolete because the managed node is no longer inert.
|
||||
* If the object has been destroyed, any attempt to access it will cause an exception.
|
||||
*/
|
||||
|
||||
}, {
|
||||
key: '_throwIfDestroyed',
|
||||
|
||||
|
||||
/**
|
||||
* Throw if user tries to access destroyed InertNode.
|
||||
*/
|
||||
value: function _throwIfDestroyed() {
|
||||
if (this.destroyed) {
|
||||
throw new Error('Trying to access destroyed InertNode');
|
||||
}
|
||||
}
|
||||
|
||||
/** @return {boolean} */
|
||||
|
||||
}, {
|
||||
key: 'ensureUntabbable',
|
||||
|
||||
|
||||
/** Save the existing tabindex value and make the node untabbable and unfocusable */
|
||||
value: function ensureUntabbable() {
|
||||
if (this.node.nodeType !== Node.ELEMENT_NODE) {
|
||||
return;
|
||||
}
|
||||
var element = /** @type {!Element} */this.node;
|
||||
if (matches.call(element, _focusableElementsString)) {
|
||||
if ( /** @type {!HTMLElement} */element.tabIndex === -1 && this.hasSavedTabIndex) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (element.hasAttribute('tabindex')) {
|
||||
this._savedTabIndex = /** @type {!HTMLElement} */element.tabIndex;
|
||||
}
|
||||
element.setAttribute('tabindex', '-1');
|
||||
if (element.nodeType === Node.ELEMENT_NODE) {
|
||||
element.focus = function () {};
|
||||
this._overrodeFocusMethod = true;
|
||||
}
|
||||
} else if (element.hasAttribute('tabindex')) {
|
||||
this._savedTabIndex = /** @type {!HTMLElement} */element.tabIndex;
|
||||
element.removeAttribute('tabindex');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add another inert root to this inert node's set of managing inert roots.
|
||||
* @param {!InertRoot} inertRoot
|
||||
*/
|
||||
|
||||
}, {
|
||||
key: 'addInertRoot',
|
||||
value: function addInertRoot(inertRoot) {
|
||||
this._throwIfDestroyed();
|
||||
this._inertRoots.add(inertRoot);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the given inert root from this inert node's set of managing inert roots.
|
||||
* If the set of managing inert roots becomes empty, this node is no longer inert,
|
||||
* so the object should be destroyed.
|
||||
* @param {!InertRoot} inertRoot
|
||||
*/
|
||||
|
||||
}, {
|
||||
key: 'removeInertRoot',
|
||||
value: function removeInertRoot(inertRoot) {
|
||||
this._throwIfDestroyed();
|
||||
this._inertRoots['delete'](inertRoot);
|
||||
if (this._inertRoots.size === 0) {
|
||||
this.destructor();
|
||||
}
|
||||
}
|
||||
}, {
|
||||
key: 'destroyed',
|
||||
get: function get() {
|
||||
return (/** @type {!InertNode} */this._destroyed
|
||||
);
|
||||
}
|
||||
}, {
|
||||
key: 'hasSavedTabIndex',
|
||||
get: function get() {
|
||||
return this._savedTabIndex !== null;
|
||||
}
|
||||
|
||||
/** @return {!Node} */
|
||||
|
||||
}, {
|
||||
key: 'node',
|
||||
get: function get() {
|
||||
this._throwIfDestroyed();
|
||||
return this._node;
|
||||
}
|
||||
|
||||
/** @param {?number} tabIndex */
|
||||
|
||||
}, {
|
||||
key: 'savedTabIndex',
|
||||
set: function set(tabIndex) {
|
||||
this._throwIfDestroyed();
|
||||
this._savedTabIndex = tabIndex;
|
||||
}
|
||||
|
||||
/** @return {?number} */
|
||||
,
|
||||
get: function get() {
|
||||
this._throwIfDestroyed();
|
||||
return this._savedTabIndex;
|
||||
}
|
||||
}]);
|
||||
|
||||
return InertNode;
|
||||
}();
|
||||
|
||||
/**
|
||||
* InertManager is a per-document singleton object which manages all inert roots and nodes.
|
||||
*
|
||||
* When an element becomes an inert root by having an `inert` attribute set and/or its `inert`
|
||||
* property set to `true`, the `setInert` method creates an `InertRoot` object for the element.
|
||||
* The `InertRoot` in turn registers itself as managing all of the element's focusable descendant
|
||||
* nodes via the `register()` method. The `InertManager` ensures that a single `InertNode` instance
|
||||
* is created for each such node, via the `_managedNodes` map.
|
||||
*/
|
||||
|
||||
|
||||
var InertManager = function () {
|
||||
/**
|
||||
* @param {!Document} document
|
||||
*/
|
||||
function InertManager(document) {
|
||||
_classCallCheck(this, InertManager);
|
||||
|
||||
if (!document) {
|
||||
throw new Error('Missing required argument; InertManager needs to wrap a document.');
|
||||
}
|
||||
|
||||
/** @type {!Document} */
|
||||
this._document = document;
|
||||
|
||||
/**
|
||||
* All managed nodes known to this InertManager. In a map to allow looking up by Node.
|
||||
* @type {!Map<!Node, !InertNode>}
|
||||
*/
|
||||
this._managedNodes = new Map();
|
||||
|
||||
/**
|
||||
* All inert roots known to this InertManager. In a map to allow looking up by Node.
|
||||
* @type {!Map<!Node, !InertRoot>}
|
||||
*/
|
||||
this._inertRoots = new Map();
|
||||
|
||||
/**
|
||||
* Observer for mutations on `document.body`.
|
||||
* @type {!MutationObserver}
|
||||
*/
|
||||
this._observer = new MutationObserver(this._watchForInert.bind(this));
|
||||
|
||||
// Add inert style.
|
||||
addInertStyle(document.head || document.body || document.documentElement);
|
||||
|
||||
// Wait for document to be loaded.
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', this._onDocumentLoaded.bind(this));
|
||||
} else {
|
||||
this._onDocumentLoaded();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether the given element should be an inert root or not.
|
||||
* @param {!Element} root
|
||||
* @param {boolean} inert
|
||||
*/
|
||||
|
||||
|
||||
_createClass(InertManager, [{
|
||||
key: 'setInert',
|
||||
value: function setInert(root, inert) {
|
||||
if (inert) {
|
||||
if (this._inertRoots.has(root)) {
|
||||
// element is already inert
|
||||
return;
|
||||
}
|
||||
|
||||
var inertRoot = new InertRoot(root, this);
|
||||
root.setAttribute('inert', '');
|
||||
this._inertRoots.set(root, inertRoot);
|
||||
// If not contained in the document, it must be in a shadowRoot.
|
||||
// Ensure inert styles are added there.
|
||||
if (!this._document.body.contains(root)) {
|
||||
var parent = root.parentNode;
|
||||
while (parent) {
|
||||
if (parent.nodeType === 11) {
|
||||
addInertStyle(parent);
|
||||
}
|
||||
parent = parent.parentNode;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!this._inertRoots.has(root)) {
|
||||
// element is already non-inert
|
||||
return;
|
||||
}
|
||||
|
||||
var _inertRoot = this._inertRoots.get(root);
|
||||
_inertRoot.destructor();
|
||||
this._inertRoots['delete'](root);
|
||||
root.removeAttribute('inert');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the InertRoot object corresponding to the given inert root element, if any.
|
||||
* @param {!Node} element
|
||||
* @return {!InertRoot|undefined}
|
||||
*/
|
||||
|
||||
}, {
|
||||
key: 'getInertRoot',
|
||||
value: function getInertRoot(element) {
|
||||
return this._inertRoots.get(element);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the given InertRoot as managing the given node.
|
||||
* In the case where the node has a previously existing inert root, this inert root will
|
||||
* be added to its set of inert roots.
|
||||
* @param {!Node} node
|
||||
* @param {!InertRoot} inertRoot
|
||||
* @return {!InertNode} inertNode
|
||||
*/
|
||||
|
||||
}, {
|
||||
key: 'register',
|
||||
value: function register(node, inertRoot) {
|
||||
var inertNode = this._managedNodes.get(node);
|
||||
if (inertNode !== undefined) {
|
||||
// node was already in an inert subtree
|
||||
inertNode.addInertRoot(inertRoot);
|
||||
} else {
|
||||
inertNode = new InertNode(node, inertRoot);
|
||||
}
|
||||
|
||||
this._managedNodes.set(node, inertNode);
|
||||
|
||||
return inertNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* De-register the given InertRoot as managing the given inert node.
|
||||
* Removes the inert root from the InertNode's set of managing inert roots, and remove the inert
|
||||
* node from the InertManager's set of managed nodes if it is destroyed.
|
||||
* If the node is not currently managed, this is essentially a no-op.
|
||||
* @param {!Node} node
|
||||
* @param {!InertRoot} inertRoot
|
||||
* @return {?InertNode} The potentially destroyed InertNode associated with this node, if any.
|
||||
*/
|
||||
|
||||
}, {
|
||||
key: 'deregister',
|
||||
value: function deregister(node, inertRoot) {
|
||||
var inertNode = this._managedNodes.get(node);
|
||||
if (!inertNode) {
|
||||
return null;
|
||||
}
|
||||
|
||||
inertNode.removeInertRoot(inertRoot);
|
||||
if (inertNode.destroyed) {
|
||||
this._managedNodes['delete'](node);
|
||||
}
|
||||
|
||||
return inertNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback used when document has finished loading.
|
||||
*/
|
||||
|
||||
}, {
|
||||
key: '_onDocumentLoaded',
|
||||
value: function _onDocumentLoaded() {
|
||||
// Find all inert roots in document and make them actually inert.
|
||||
var inertElements = slice.call(this._document.querySelectorAll('[inert]'));
|
||||
inertElements.forEach(function (inertElement) {
|
||||
this.setInert(inertElement, true);
|
||||
}, this);
|
||||
|
||||
// Comment this out to use programmatic API only.
|
||||
this._observer.observe(this._document.body, { attributes: true, subtree: true, childList: true });
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback used when mutation observer detects attribute changes.
|
||||
* @param {!Array<!MutationRecord>} records
|
||||
* @param {!MutationObserver} self
|
||||
*/
|
||||
|
||||
}, {
|
||||
key: '_watchForInert',
|
||||
value: function _watchForInert(records, self) {
|
||||
var _this = this;
|
||||
records.forEach(function (record) {
|
||||
switch (record.type) {
|
||||
case 'childList':
|
||||
slice.call(record.addedNodes).forEach(function (node) {
|
||||
if (node.nodeType !== Node.ELEMENT_NODE) {
|
||||
return;
|
||||
}
|
||||
var inertElements = slice.call(node.querySelectorAll('[inert]'));
|
||||
if (matches.call(node, '[inert]')) {
|
||||
inertElements.unshift(node);
|
||||
}
|
||||
inertElements.forEach(function (inertElement) {
|
||||
this.setInert(inertElement, true);
|
||||
}, _this);
|
||||
}, _this);
|
||||
break;
|
||||
case 'attributes':
|
||||
if (record.attributeName !== 'inert') {
|
||||
return;
|
||||
}
|
||||
var target = /** @type {!Element} */record.target;
|
||||
var inert = target.hasAttribute('inert');
|
||||
_this.setInert(target, inert);
|
||||
break;
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
}]);
|
||||
|
||||
return InertManager;
|
||||
}();
|
||||
|
||||
/**
|
||||
* Recursively walk the composed tree from |node|.
|
||||
* @param {!Node} node
|
||||
* @param {(function (!Element))=} callback Callback to be called for each element traversed,
|
||||
* before descending into child nodes.
|
||||
* @param {?ShadowRoot=} shadowRootAncestor The nearest ShadowRoot ancestor, if any.
|
||||
*/
|
||||
|
||||
|
||||
function composedTreeWalk(node, callback, shadowRootAncestor) {
|
||||
if (node.nodeType == Node.ELEMENT_NODE) {
|
||||
var element = /** @type {!Element} */node;
|
||||
if (callback) {
|
||||
callback(element);
|
||||
}
|
||||
|
||||
// Descend into node:
|
||||
// If it has a ShadowRoot, ignore all child elements - these will be picked
|
||||
// up by the <content> or <shadow> elements. Descend straight into the
|
||||
// ShadowRoot.
|
||||
var shadowRoot = /** @type {!HTMLElement} */element.shadowRoot;
|
||||
if (shadowRoot) {
|
||||
composedTreeWalk(shadowRoot, callback, shadowRoot);
|
||||
return;
|
||||
}
|
||||
|
||||
// If it is a <content> element, descend into distributed elements - these
|
||||
// are elements from outside the shadow root which are rendered inside the
|
||||
// shadow DOM.
|
||||
if (element.localName == 'content') {
|
||||
var content = /** @type {!HTMLContentElement} */element;
|
||||
// Verifies if ShadowDom v0 is supported.
|
||||
var distributedNodes = content.getDistributedNodes ? content.getDistributedNodes() : [];
|
||||
for (var i = 0; i < distributedNodes.length; i++) {
|
||||
composedTreeWalk(distributedNodes[i], callback, shadowRootAncestor);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// If it is a <slot> element, descend into assigned nodes - these
|
||||
// are elements from outside the shadow root which are rendered inside the
|
||||
// shadow DOM.
|
||||
if (element.localName == 'slot') {
|
||||
var slot = /** @type {!HTMLSlotElement} */element;
|
||||
// Verify if ShadowDom v1 is supported.
|
||||
var _distributedNodes = slot.assignedNodes ? slot.assignedNodes({ flatten: true }) : [];
|
||||
for (var _i = 0; _i < _distributedNodes.length; _i++) {
|
||||
composedTreeWalk(_distributedNodes[_i], callback, shadowRootAncestor);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If it is neither the parent of a ShadowRoot, a <content> element, a <slot>
|
||||
// element, nor a <shadow> element recurse normally.
|
||||
var child = node.firstChild;
|
||||
while (child != null) {
|
||||
composedTreeWalk(child, callback, shadowRootAncestor);
|
||||
child = child.nextSibling;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a style element to the node containing the inert specific styles
|
||||
* @param {!Node} node
|
||||
*/
|
||||
function addInertStyle(node) {
|
||||
if (node.querySelector('style#inert-style')) {
|
||||
return;
|
||||
}
|
||||
var style = document.createElement('style');
|
||||
style.setAttribute('id', 'inert-style');
|
||||
style.textContent = '\n' + '[inert] {\n' + ' pointer-events: none;\n' + ' cursor: default;\n' + '}\n' + '\n' + '[inert], [inert] * {\n' + ' user-select: none;\n' + ' -webkit-user-select: none;\n' + ' -moz-user-select: none;\n' + ' -ms-user-select: none;\n' + '}\n';
|
||||
node.appendChild(style);
|
||||
}
|
||||
|
||||
/** @type {!InertManager} */
|
||||
var inertManager = new InertManager(document);
|
||||
|
||||
if (!Element.prototype.hasOwnProperty('inert')) {
|
||||
Object.defineProperty(Element.prototype, 'inert', {
|
||||
enumerable: true,
|
||||
/** @this {!Element} */
|
||||
get: function get() {
|
||||
return this.hasAttribute('inert');
|
||||
},
|
||||
/** @this {!Element} */
|
||||
set: function set(inert) {
|
||||
inertManager.setInert(this, inert);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
})));
|
2
plugins/wporg-5ftf/assets/js/inert.min.js
vendored
Normal file
2
plugins/wporg-5ftf/assets/js/inert.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
|
@ -109,7 +109,7 @@ function send_manage_email_handler() {
|
|||
}
|
||||
} else {
|
||||
$error_message = sprintf(
|
||||
__( 'That\'s not the address that we have for this pledge, please try a different one. If none of the addresses you try are working, please <a href="%s">email us</a> for help.', 'wporg-5ftf' ),
|
||||
__( 'That\'s not the address that we have for this pledge. If you don\'t know the email associated with this pledge, <a href="%s">please contact us for help.</a>', 'wporg-5ftf' ),
|
||||
get_permalink( get_page_by_path( 'report' ) )
|
||||
);
|
||||
|
||||
|
|
|
@ -14,7 +14,6 @@ defined( 'WPINC' ) || die();
|
|||
// Todo make this into simple optionless blocks instead?
|
||||
add_shortcode( '5ftf_pledge_form_new', __NAMESPACE__ . '\render_form_new' );
|
||||
add_shortcode( '5ftf_pledge_form_manage', __NAMESPACE__ . '\render_form_manage' );
|
||||
add_shortcode( '5ftf_pledge_form_manage_link', __NAMESPACE__ . '\render_manage_link_request' );
|
||||
|
||||
/**
|
||||
* Render the form(s) for creating new pledges.
|
||||
|
@ -175,19 +174,6 @@ function render_form_manage() {
|
|||
return ob_get_clean();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Render the `render_manage_link_request` shortcode.
|
||||
*/
|
||||
function render_manage_link_request() {
|
||||
// @todo enable when https://github.com/WordPress/five-for-the-future/issues/6 is done
|
||||
if ( ! defined( 'WPORG_SANDBOXED' ) || ! WPORG_SANDBOXED ) {
|
||||
return;
|
||||
}
|
||||
|
||||
require_once FiveForTheFuture\get_views_path() . 'form-pledge-request-manage-link.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a submission from the Manage Existing Pledge form.
|
||||
*
|
||||
|
|
|
@ -18,12 +18,19 @@ const SLUG = 'pledge';
|
|||
const SLUG_PL = 'pledges';
|
||||
const CPT_ID = FiveForTheFuture\PREFIX . '_' . SLUG;
|
||||
|
||||
add_action( 'init', __NAMESPACE__ . '\register', 0 );
|
||||
add_action( 'admin_menu', __NAMESPACE__ . '\admin_menu' );
|
||||
// Admin hooks.
|
||||
add_action( 'init', __NAMESPACE__ . '\register', 0 );
|
||||
add_action( 'admin_menu', __NAMESPACE__ . '\admin_menu' );
|
||||
add_action( 'pre_get_posts', __NAMESPACE__ . '\filter_query' );
|
||||
// List table columns.
|
||||
add_filter( 'manage_edit-' . CPT_ID . '_columns', __NAMESPACE__ . '\add_list_table_columns' );
|
||||
add_action( 'manage_' . CPT_ID . '_posts_custom_column', __NAMESPACE__ . '\populate_list_table_columns', 10, 2 );
|
||||
|
||||
// Front end hooks.
|
||||
add_action( 'wp_enqueue_scripts', __NAMESPACE__ . '\enqueue_assets' );
|
||||
add_action( 'pledge_footer', __NAMESPACE__ . '\render_manage_link_request' );
|
||||
add_action( 'wp_footer', __NAMESPACE__ . '\render_js_templates' );
|
||||
|
||||
/**
|
||||
* Register all the things.
|
||||
*
|
||||
|
@ -252,3 +259,56 @@ function filter_query( $query ) {
|
|||
// see https://github.com/WordPress/five-for-the-future/issues/70#issuecomment-549066883.
|
||||
$query->set( 'posts_per_page', 100 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue assets for front-end management.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function enqueue_assets() {
|
||||
wp_register_script( 'wicg-inert', plugins_url( 'assets/js/inert.min.js', __DIR__ ), [], '3.0.0', true );
|
||||
|
||||
if ( CPT_ID === get_post_type() ) {
|
||||
$ver = filemtime( FiveForTheFuture\PATH . '/assets/js/frontend.js' );
|
||||
wp_enqueue_script( '5ftf-frontend', plugins_url( 'assets/js/frontend.js', __DIR__ ), [ 'jquery', 'wp-a11y', 'wp-util', 'wicg-inert' ], $ver, true );
|
||||
|
||||
$script_data = [
|
||||
'ajaxurl' => admin_url( 'admin-ajax.php', 'relative' ), // The global ajaxurl is not set on the frontend.
|
||||
'pledgeId' => get_the_ID(),
|
||||
'ajaxNonce' => wp_create_nonce( 'send-manage-email' ),
|
||||
];
|
||||
wp_add_inline_script(
|
||||
'5ftf-frontend',
|
||||
sprintf(
|
||||
'var FiveForTheFuture = JSON.parse( decodeURIComponent( \'%s\' ) );',
|
||||
rawurlencode( wp_json_encode( $script_data ) )
|
||||
),
|
||||
'before'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the button to toggle the "Request Manage Email" dialog.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function render_manage_link_request() {
|
||||
// @todo enable when https://github.com/WordPress/five-for-the-future/issues/6 is done
|
||||
if ( ! defined( 'WPORG_SANDBOXED' ) || ! WPORG_SANDBOXED ) {
|
||||
return;
|
||||
}
|
||||
|
||||
require_once FiveForTheFuture\get_views_path() . 'button-request-manage-link.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Render JS templates at the end of the page.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function render_js_templates() {
|
||||
if ( CPT_ID === get_post_type() ) {
|
||||
require_once FiveForTheFuture\get_views_path() . 'modal-request-manage-link.php';
|
||||
}
|
||||
}
|
||||
|
|
13
plugins/wporg-5ftf/views/button-request-manage-link.php
Normal file
13
plugins/wporg-5ftf/views/button-request-manage-link.php
Normal file
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
|
||||
namespace WordPressDotOrg\FiveForTheFuture\View;
|
||||
|
||||
defined( 'WPINC' ) || die();
|
||||
|
||||
?>
|
||||
<div class="edit-pledge-wrapper">
|
||||
<button id="toggle-management-link-form" class="button button-link">
|
||||
<span class="dashicons dashicons-edit" aria-hidden="true"></span>
|
||||
<?php esc_html_e( 'Edit Pledge', 'wporg-5ftf' ); ?>
|
||||
</button>
|
||||
</div>
|
|
@ -1,75 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace WordPressDotOrg\FiveForTheFuture\View;
|
||||
|
||||
defined( 'WPINC' ) || die();
|
||||
|
||||
/**
|
||||
* @var array $errors
|
||||
*/
|
||||
|
||||
// Hide it if it hasn't submitted, but show success/error messages if it was submitted.
|
||||
// phpcs:ignore
|
||||
$hidden = empty( $errors ) && empty( $_POST['get_manage_pledge_link'] ) ? 'hidden' : '';
|
||||
|
||||
?>
|
||||
|
||||
<button id="toggle-management-link-form">
|
||||
<span class="dashicons dashicons-edit"></span>
|
||||
<?php esc_html_e( 'Edit Pledge', 'wporg-5ftf' ); ?>
|
||||
</button>
|
||||
|
||||
<div id="request-management-link" <?php echo esc_attr( $hidden ); ?> >
|
||||
<p>
|
||||
<?php esc_html_e( 'Only pledge admins can edit pledges.', 'wporg-5ftf' ); ?>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<?php esc_html_e( "If you're the admin, enter your email address and a confirmation link will be sent to you.", 'wporg-5ftf' ); ?>
|
||||
</p>
|
||||
|
||||
<form action="#form-messages" method="post">
|
||||
<input type="hidden" name="pledge_id" value="<?php echo esc_attr( get_post()->ID ); ?>" />
|
||||
|
||||
<label for="pledge_admin_address">
|
||||
<?php esc_html_e( 'Email Address', 'wporg-5ftf' ); ?>
|
||||
</label>
|
||||
|
||||
<input
|
||||
id="pledge_admin_address"
|
||||
name="pledge_admin_address"
|
||||
type="email"
|
||||
required
|
||||
value=""
|
||||
/>
|
||||
|
||||
<input
|
||||
type="submit"
|
||||
name="get_manage_pledge_link"
|
||||
value="Submit"
|
||||
/>
|
||||
|
||||
<?php require __DIR__ . '/partial-result-messages.php'; ?>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
( function() {
|
||||
var toggleLinkFormButton = document.getElementById( 'toggle-management-link-form' ),
|
||||
linkForm = document.getElementById( 'request-management-link' );
|
||||
|
||||
// Toggle the form when the button is clicked.
|
||||
toggleLinkFormButton.addEventListener( 'click', function() {
|
||||
switch( linkForm.hidden ) {
|
||||
case true:
|
||||
linkForm.hidden = false;
|
||||
linkForm.scrollIntoView( { behavior: 'smooth' } );
|
||||
break;
|
||||
|
||||
default:
|
||||
linkForm.hidden = true;
|
||||
break;
|
||||
}
|
||||
} );
|
||||
}() );
|
||||
</script>
|
44
plugins/wporg-5ftf/views/modal-request-manage-link.php
Normal file
44
plugins/wporg-5ftf/views/modal-request-manage-link.php
Normal file
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
namespace WordPressDotOrg\FiveForTheFuture\View;
|
||||
|
||||
defined( 'WPINC' ) || die();
|
||||
|
||||
?>
|
||||
<script type="text/template" id="tmpl-5ftf-send-link-dialog">
|
||||
<div id="send-link-dialog-bg" class="pledge-dialog__background" hidden data-no-inert></div>
|
||||
<div id="send-link-dialog" role="dialog" class="pledge-dialog" hidden tabindex="-1" aria-label="<?php esc_attr_e( 'Request to edit this pledge', 'wporg-5ftf' ); ?>">
|
||||
<p>
|
||||
<?php esc_html_e( 'Only pledge admins can edit pledges.', 'wporg-5ftf' ); ?>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<?php esc_html_e( "If you're the admin, enter your email address and a confirmation link will be sent to you.", 'wporg-5ftf' ); ?>
|
||||
</p>
|
||||
|
||||
<form method="post">
|
||||
<input type="hidden" name="pledge_id" value="<?php echo esc_attr( get_post()->ID ); ?>" />
|
||||
|
||||
<label for="pledge_admin_address">
|
||||
<?php esc_html_e( 'Email Address', 'wporg-5ftf' ); ?>
|
||||
</label>
|
||||
|
||||
<input
|
||||
id="pledge_admin_address"
|
||||
name="pledge_admin_address"
|
||||
type="email"
|
||||
required
|
||||
value=""
|
||||
/>
|
||||
|
||||
<div class="message"></div>
|
||||
|
||||
<input
|
||||
type="submit"
|
||||
name="get_manage_pledge_link"
|
||||
value="<?php esc_attr_e( 'Submit', 'wporg-5ftf' ); ?>"
|
||||
/>
|
||||
</form>
|
||||
<button type="button" class="button button-link pledge-dialog__close" aria-label="Close"><span class="dashicons dashicons-no-alt" aria-hidden="true"></span></button></div>
|
||||
</div>
|
||||
</script>
|
|
@ -27,6 +27,7 @@
|
|||
@import "page";
|
||||
@import "pagination";
|
||||
@import "pledge-contributors";
|
||||
@import "pledge-dialog";
|
||||
@import "pledge-list";
|
||||
@import "pledge-single";
|
||||
@import "search-form";
|
||||
|
|
77
themes/wporg-5ftf/css/components/_pledge-dialog.scss
Normal file
77
themes/wporg-5ftf/css/components/_pledge-dialog.scss
Normal file
|
@ -0,0 +1,77 @@
|
|||
.pledge-dialog {
|
||||
position: absolute;
|
||||
z-index: 100;
|
||||
width: 300px;
|
||||
margin-top: 20px;
|
||||
padding: 20px;
|
||||
background: #fff;
|
||||
border: 1px solid $color-gray-light-400;
|
||||
box-shadow: rgba($color-gray-900, 0.1) 0 3px 30px;
|
||||
font-size: ms(-3);
|
||||
|
||||
&[hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&:before,
|
||||
&:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -11px;
|
||||
right: 40px;
|
||||
border-style: solid;
|
||||
border-color: transparent transparent #fff;
|
||||
border-width: 0 12px 12px;
|
||||
}
|
||||
|
||||
&:before {
|
||||
top: -12px;
|
||||
border-bottom-color: $color-gray-light-400;
|
||||
}
|
||||
|
||||
label {
|
||||
display: inline-block;
|
||||
margin-bottom: 4px;
|
||||
font-size: ms(-4);
|
||||
font-weight: bold;
|
||||
color: $color-gray-500;
|
||||
}
|
||||
|
||||
input[type="email"] {
|
||||
display: block;
|
||||
width: 100%;
|
||||
margin: 0 0 20px 0;
|
||||
padding: ms(-7) ms(-2);
|
||||
border: 1px solid $color-gray-100;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
input[type="submit"] {
|
||||
font-size: ms(-3);
|
||||
}
|
||||
|
||||
.notice p {
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
.pledge-dialog__close {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
padding: 10px;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
.pledge-dialog__background {
|
||||
position: fixed;
|
||||
z-index: 99;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
|
||||
&[hidden] {
|
||||
display: none;
|
||||
}
|
||||
}
|
|
@ -51,54 +51,43 @@ body.single.single-5ftf_pledge {
|
|||
|
||||
.entry-footer {
|
||||
margin-top: ms(2);
|
||||
margin-bottom: ms(20);
|
||||
border-top: 1px solid $color-gray-light-500;
|
||||
padding-top: ms(2);
|
||||
|
||||
.report-wrapper {
|
||||
margin-bottom: 1em;
|
||||
|
||||
a {
|
||||
color: $color-alert-red;
|
||||
}
|
||||
}
|
||||
|
||||
.edit-pledge-wrapper {
|
||||
text-align: right;
|
||||
|
||||
p {
|
||||
font-size: ms(-2);
|
||||
}
|
||||
|
||||
.button {
|
||||
padding-right: 4px;
|
||||
text-decoration: none;
|
||||
color: $color__wp-blue;
|
||||
font-size: 1rem;
|
||||
|
||||
&:hover {
|
||||
border-bottom: 1px solid currentColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include breakpoint( $breakpoint-tablet ) {
|
||||
display: grid;
|
||||
grid-template-columns: 50% 50%;
|
||||
display: flex;
|
||||
|
||||
#toggle-management-link-form,
|
||||
#request-management-link {
|
||||
float: right;
|
||||
clear: both;
|
||||
.report-wrapper {
|
||||
flex: 1 0 auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#request-management-link {
|
||||
width: 300px;
|
||||
border: 1px solid #ebeced; // setup var
|
||||
box-shadow: rgba(25, 30, 35, 0.1) 0 3px 30px;
|
||||
margin-top: 20px;
|
||||
padding: 15px;
|
||||
|
||||
// todo add the upper ^ carot thing
|
||||
// border-radius or something to draw triangle
|
||||
// element inside the box, but then abs position outside it?
|
||||
// z-index above parent so it's above box-shadow - probably happens by default?
|
||||
|
||||
p:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
input[type="email"] {
|
||||
width: 100%;
|
||||
margin: 0 0 20px 0;
|
||||
}
|
||||
|
||||
input[type="submit"] {
|
||||
float: right;
|
||||
clear: both;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
#form-messages {
|
||||
clear: both;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ $color-gray-light-200: lighten($color-base-gray,74%);
|
|||
$color-gray-light-100: lighten($color-base-gray,77%);
|
||||
|
||||
$color-error-red: #c92c2c;
|
||||
$color-alert-red: #d94f4f;
|
||||
|
||||
// Theme colors
|
||||
$color__background-input: $color-gray-light-200;
|
||||
|
|
|
@ -55,7 +55,10 @@ get_header();
|
|||
<h2><?php esc_html_e( 'About', 'wporg' ); ?></h2>
|
||||
|
||||
<div class="pledge-company-description">
|
||||
<?php echo wpautop( wp_kses_data( $post->{ META_PREFIX . 'org-description' } ) ); ?>
|
||||
<?php
|
||||
// phpcs:ignore WordPress.Security.EscapeOutput -- wp_kses_data escapes the content.
|
||||
echo wpautop( wp_kses_data( $post->{ META_PREFIX . 'org-description' } ) );
|
||||
?>
|
||||
</div>
|
||||
|
||||
<?php if ( ! empty( $contributors ) ) : ?>
|
||||
|
@ -130,9 +133,7 @@ get_header();
|
|||
</a>
|
||||
</div>
|
||||
|
||||
<div class="edit-pledge-wrapper">
|
||||
<?php do_shortcode( '[5ftf_pledge_form_manage_link]' ); ?>
|
||||
</div>
|
||||
<?php do_action( 'pledge_footer' ); ?>
|
||||
</footer>
|
||||
</article>
|
||||
|
||||
|
|
Loading…
Reference in a new issue