- ${this.closeText||"Close"}
-
-
-
-
- diff --git a/creativecloud/blocks/interactive-metadata/interactive-metadata.css b/creativecloud/blocks/interactive-metadata/interactive-metadata.css
index 1d892b469..6720397fc 100644
--- a/creativecloud/blocks/interactive-metadata/interactive-metadata.css
+++ b/creativecloud/blocks/interactive-metadata/interactive-metadata.css
@@ -277,6 +277,10 @@
order: 1;
}
+ .marquee.large.interactive-enabled.content-top-mobile .foreground {
+ gap: 0;
+ }
+
.aside.interactive-enabled .foreground.container .image {
display: block;
width: 100%;
@@ -407,6 +411,10 @@
.marquee.large.interactive-enabled.content-top-tablet .text {
order: 1;
}
+
+ .marquee.large.interactive-enabled.content-top-tablet .foreground {
+ gap: 0;
+ }
}
@media screen and (max-width: 1200px) {
diff --git a/creativecloud/blocks/nonprofit/constants.js b/creativecloud/blocks/nonprofit/constants.js
new file mode 100644
index 000000000..8d68a177d
--- /dev/null
+++ b/creativecloud/blocks/nonprofit/constants.js
@@ -0,0 +1,427 @@
+// eslint-disable-next-line import/prefer-default-export
+export const countries = [
+ {
+ code: 'AFG',
+ name: 'Afghanistan',
+ },
+ {
+ code: 'ALA',
+ name: 'Ă…land Islands',
+ },
+ {
+ code: 'ALB',
+ name: 'Albania',
+ },
+ {
+ code: 'DZA',
+ name: 'Algeria',
+ },
+ {
+ code: 'ASM',
+ name: 'American Samoa',
+ },
+ {
+ code: 'AND',
+ name: 'Andorra',
+ },
+ {
+ code: 'AGO',
+ name: 'Angola',
+ },
+ {
+ code: 'AIA',
+ name: 'Anguilla',
+ },
+ {
+ code: 'ATA',
+ name: 'Antarctica',
+ },
+ {
+ code: 'ARG',
+ name: 'Argentina',
+ },
+ {
+ code: 'ABW',
+ name: 'Aruba',
+ },
+ {
+ code: 'AUS',
+ name: 'Australia',
+ },
+ {
+ code: 'AUT',
+ name: 'Austria',
+ },
+ {
+ code: 'BHR',
+ name: 'Bahrain',
+ },
+ {
+ code: 'BGD',
+ name: 'Bangladesh',
+ },
+ {
+ code: 'BRB',
+ name: 'Barbados',
+ },
+ {
+ code: 'BLR',
+ name: 'Belarus',
+ },
+ {
+ code: 'BEL',
+ name: 'Belgium',
+ },
+ {
+ code: 'BMU',
+ name: 'Bermuda',
+ },
+ {
+ code: 'BTN',
+ name: 'Bhutan',
+ },
+ {
+ code: 'BIH',
+ name: 'Bosnia and Herzegovina',
+ },
+ {
+ code: 'BWA',
+ name: 'Botswana',
+ },
+ {
+ code: 'BRA',
+ name: 'Brazil',
+ },
+ {
+ code: 'BGR',
+ name: 'Bulgaria',
+ },
+ {
+ code: 'CPV',
+ name: 'Cabo Verde',
+ },
+ {
+ code: 'KHM',
+ name: 'Cambodia',
+ },
+ {
+ code: 'CAN',
+ name: 'Canada',
+ },
+ {
+ code: 'CHL',
+ name: 'Chile',
+ },
+ {
+ code: 'COL',
+ name: 'Colombia',
+ },
+ {
+ code: 'COM',
+ name: 'Comoros (the)',
+ },
+ {
+ code: 'COG',
+ name: 'Congo (the)',
+ },
+ {
+ code: 'COD',
+ name: 'Congo (the Democratic Republic of the)',
+ },
+ {
+ code: 'COK',
+ name: 'Cook Islands (the)',
+ },
+ {
+ code: 'HRV',
+ name: 'Croatia',
+ },
+ {
+ code: 'CYP',
+ name: 'Cyprus',
+ },
+ {
+ code: 'CZE',
+ name: 'Czechia',
+ },
+ {
+ code: 'DNK',
+ name: 'Denmark',
+ },
+ {
+ code: 'DJI',
+ name: 'Djibouti',
+ },
+ {
+ code: 'DMA',
+ name: 'Dominica',
+ },
+ {
+ code: 'DOM',
+ name: 'Dominican Republic (the)',
+ },
+ {
+ code: 'ECU',
+ name: 'Ecuador',
+ },
+ {
+ code: 'EGY',
+ name: 'Egypt',
+ },
+ {
+ code: 'EST',
+ name: 'Estonia',
+ },
+ {
+ code: 'FIN',
+ name: 'Finland',
+ },
+ {
+ code: 'FRA',
+ name: 'France',
+ },
+ {
+ code: 'GEO',
+ name: 'Georgia',
+ },
+ {
+ code: 'DEU',
+ name: 'Germany',
+ },
+ {
+ code: 'GHA',
+ name: 'Ghana',
+ },
+ {
+ code: 'GGY',
+ name: 'Guernsey',
+ },
+ {
+ code: 'GNB',
+ name: 'Guinea-Bissau',
+ },
+ {
+ code: 'HKG',
+ name: 'Hong Kong',
+ },
+ {
+ code: 'HUN',
+ name: 'Hungary',
+ },
+ {
+ code: 'ISL',
+ name: 'Iceland',
+ },
+ {
+ code: 'IND',
+ name: 'India',
+ },
+ {
+ code: 'IRL',
+ name: 'Ireland',
+ },
+ {
+ code: 'IMN',
+ name: 'Isle of Man',
+ },
+ {
+ code: 'ISR',
+ name: 'Israel',
+ },
+ {
+ code: 'ITA',
+ name: 'Italy',
+ },
+ {
+ code: 'JPN',
+ name: 'Japan',
+ },
+ {
+ code: 'JOR',
+ name: 'Jordan',
+ },
+ {
+ code: 'XKX',
+ name: 'Kosovo',
+ },
+ {
+ code: 'KGZ',
+ name: 'Kyrgyzstan',
+ },
+ {
+ code: 'LVA',
+ name: 'Latvia',
+ },
+ {
+ code: 'LBN',
+ name: 'Lebanon',
+ },
+ {
+ code: 'LUX',
+ name: 'Luxembourg',
+ },
+ {
+ code: 'MAC',
+ name: 'Macao',
+ },
+ {
+ code: 'MKD',
+ name: 'Macedonia (the former Yugoslav Republic of)',
+ },
+ {
+ code: 'MYS',
+ name: 'Malaysia',
+ },
+ {
+ code: 'MLT',
+ name: 'Malta',
+ },
+ {
+ code: 'MEX',
+ name: 'Mexico',
+ },
+ {
+ code: 'FSM',
+ name: 'Micronesia (Federated States of)',
+ },
+ {
+ code: 'MDA',
+ name: 'Moldova (the Republic of)',
+ },
+ {
+ code: 'MNE',
+ name: 'Montenegro',
+ },
+ {
+ code: 'MSR',
+ name: 'Montserrat',
+ },
+ {
+ code: 'MAR',
+ name: 'Morocco',
+ },
+ {
+ code: 'MOZ',
+ name: 'Mozambique',
+ },
+ {
+ code: 'NPL',
+ name: 'Nepal',
+ },
+ {
+ code: 'NLD',
+ name: 'Netherlands (the)',
+ },
+ {
+ code: 'NZL',
+ name: 'New Zealand',
+ },
+ {
+ code: 'NOR',
+ name: 'Norway',
+ },
+ {
+ code: 'PSE',
+ name: 'Palestine, State of',
+ },
+ {
+ code: 'PAN',
+ name: 'Panama',
+ },
+ {
+ code: 'PHL',
+ name: 'Philippines (the)',
+ },
+ {
+ code: 'POL',
+ name: 'Poland',
+ },
+ {
+ code: 'ROU',
+ name: 'Romania',
+ },
+ {
+ code: 'RUS',
+ name: 'Russian Federation (the)',
+ },
+ {
+ code: 'SRB',
+ name: 'Serbia',
+ },
+ {
+ code: 'SGP',
+ name: 'Singapore',
+ },
+ {
+ code: 'SVK',
+ name: 'Slovakia',
+ },
+ {
+ code: 'SVN',
+ name: 'Slovenia',
+ },
+ {
+ code: 'ZAF',
+ name: 'South Africa',
+ },
+ {
+ code: 'ESP',
+ name: 'Spain',
+ },
+ {
+ code: 'LKA',
+ name: 'Sri Lanka',
+ },
+ {
+ code: 'SWE',
+ name: 'Sweden',
+ },
+ {
+ code: 'CHE',
+ name: 'Switzerland',
+ },
+ {
+ code: 'TWN',
+ name: 'Taiwan (Province of China)',
+ },
+ {
+ code: 'TZA',
+ name: 'Tanzania, United Republic of',
+ },
+ {
+ code: 'TUV',
+ name: 'Tuvalu',
+ },
+ {
+ code: 'UKR',
+ name: 'Ukraine',
+ },
+ {
+ code: 'GBR',
+ name: 'United Kingdom of Great Britain and Northern Ireland (the)',
+ },
+ {
+ code: 'USA',
+ name: 'United States of America (the)',
+ },
+ {
+ code: 'VUT',
+ name: 'Vanuatu',
+ },
+ {
+ code: 'VEN',
+ name: 'Venezuela (Bolivarian Republic of)',
+ },
+ {
+ code: 'VNM',
+ name: 'Viet Nam',
+ },
+ {
+ code: 'ESH',
+ name: 'Western Sahara*',
+ },
+ {
+ code: 'YEM',
+ name: 'Yemen',
+ },
+];
diff --git a/creativecloud/blocks/nonprofit/icons.js b/creativecloud/blocks/nonprofit/icons.js
new file mode 100644
index 000000000..fe173edc5
--- /dev/null
+++ b/creativecloud/blocks/nonprofit/icons.js
@@ -0,0 +1,28 @@
+const nonprofitIcons = {
+ CHEVRON_DOWN:
+ '',
+ CHEVRON_RIGHT:
+ '',
+ BACK: '',
+ CLOSE:
+ '',
+ UPLOAD:
+ '',
+};
+
+export const NONPRFIT_ICONS = Object.freeze({
+ CHEVRON_DOWN: 'CHEVRON_DOWN',
+ CHEVRON_RIGHT: 'CHEVRON_RIGHT',
+ BACK: 'BACK',
+ CLOSE: 'CLOSE',
+ UPLOAD: 'UPLOAD',
+});
+
+export function getNonprofitIconTag(type) {
+ const iconString = nonprofitIcons[type];
+ const wrapper = document.createElement('div');
+ wrapper.innerHTML = iconString;
+ const icon = wrapper.querySelector('svg');
+ icon.classList.add('np-icon');
+ return icon;
+}
diff --git a/creativecloud/blocks/nonprofit/nonprofit-select.js b/creativecloud/blocks/nonprofit/nonprofit-select.js
new file mode 100644
index 000000000..49be58110
--- /dev/null
+++ b/creativecloud/blocks/nonprofit/nonprofit-select.js
@@ -0,0 +1,300 @@
+/* eslint-disable chai-friendly/no-unused-expressions */
+/* eslint-disable max-len */
+import ReactiveStore from './reactiveStore.js';
+import { getNonprofitIconTag, NONPRFIT_ICONS } from './icons.js';
+
+export default function nonprofitSelect(props) {
+ const {
+ createTag,
+ name,
+ label,
+ placeholder,
+ noOptionsText = window.mph['nonprofit-no-search-result-found'],
+ loadingText = window.mph['nonprofit-loading'],
+ required = true,
+ disabled = false,
+ hideIcon = false,
+ options = [],
+ store,
+ debounce,
+ labelKey = 'label',
+ valueKey = 'value',
+ renderOption,
+ footerTag,
+ } = props;
+
+ let onInput;
+ let onSelect;
+
+ const optionsStore = store || new ReactiveStore(options);
+ let localOptions = [...options];
+ let localSelection = null;
+
+ const controlTag = createTag('div', { class: 'np-control' });
+ const labelTag = createTag('label', { class: 'np-label', for: name }, label);
+ const searchTag = createTag('input', {
+ class: 'np-input np-select-search',
+ type: 'text',
+ placeholder,
+ 'data-for': name,
+ });
+ const valueTag = createTag('input', {
+ class: `np-select-value${required ? ' np-required-field' : ''}`,
+ name,
+ type: 'hidden',
+ });
+
+ if (required) {
+ searchTag.setAttribute('required', 'required');
+ valueTag.setAttribute('required', 'required');
+ }
+ if (disabled) {
+ searchTag.setAttribute('disabled', 'disabled');
+ valueTag.setAttribute('disabled', 'disabled');
+ }
+
+ const listContainerTag = createTag('div', { class: 'np-select-list-container' });
+ const listTag = createTag('ul', { class: 'np-select-list', 'data-for': name });
+
+ let searchTimeout;
+ let abortController;
+
+ const showList = () => {
+ listContainerTag.style.display = 'block';
+ };
+
+ const hideList = () => {
+ abortController?.abort();
+ listTag.scrollTop = 0;
+ listContainerTag.style.display = 'none';
+
+ // Handle loss of focus depending on whether there's a selection
+ if (!localSelection) {
+ searchTag.value = '';
+ if (!store) {
+ optionsStore.update(localOptions);
+ }
+ } else {
+ searchTag.value = localSelection[labelKey];
+ optionsStore.update([localSelection]);
+ }
+ };
+
+ let hasNewInput = false;
+
+ // Search onChange
+ searchTag.addEventListener('input', () => {
+ clearTimeout(searchTimeout);
+ abortController?.abort();
+
+ hasNewInput = true;
+
+ abortController = new AbortController();
+ searchTimeout = setTimeout(() => {
+ onInput && onInput(searchTag.value, abortController);
+ if (!store) {
+ const filteredOptions = localOptions.filter((option) => option[labelKey].toLowerCase().includes(searchTag.value.toLowerCase()));
+ optionsStore.update(filteredOptions);
+ }
+ showList();
+ }, debounce);
+ });
+
+ let focusedFromList = false;
+ searchTag.addEventListener('focus', () => {
+ if (debounce && !searchTag.value) return;
+ if (!focusedFromList) searchTag.select();
+ else focusedFromList = false;
+ showList();
+ });
+
+ searchTag.addEventListener('keydown', (ev) => {
+ if (ev.code !== 'ArrowDown') return;
+ ev.preventDefault();
+ if (ev.code === 'ArrowDown') {
+ const listItem = listContainerTag.querySelector('.np-select-item');
+ if (listItem) listItem.focus();
+ }
+ });
+
+ let rerendering = false;
+ const focusOut = (ev) => {
+ if (rerendering) return;
+ if (!ev.relatedTarget) {
+ hideList();
+ return;
+ }
+ const selectTag = ev.relatedTarget.closest('.np-select-list-tag');
+ // If the newly focused item is part of the select, don't hide list
+ if (selectTag || ev.relatedTarget === searchTag) return;
+ hideList();
+ };
+
+ searchTag.addEventListener('focusout', focusOut);
+
+ let keyboardFocusedId;
+
+ // Render select elements
+ optionsStore.subscribe((storeOptions, loading) => {
+ rerendering = true;
+
+ // Empty the list
+ listTag.replaceChildren();
+
+ storeOptions.forEach((option) => {
+ const itemTag = createTag('li', {
+ class: 'np-select-list-tag np-select-item',
+ tabindex: -1,
+ 'data-value': option[valueKey],
+ });
+
+ if (renderOption) {
+ renderOption(option, itemTag);
+ } else {
+ itemTag.textContent = option[labelKey];
+ }
+
+ // Keyboard navigation and selection handing
+
+ const selectItem = () => {
+ onSelect && onSelect(option);
+ searchTag.value = option[labelKey];
+ valueTag.value = option[valueKey];
+ valueTag.dispatchEvent(new Event('input'));
+ localSelection = option;
+ hasNewInput = false;
+ hideList();
+ };
+
+ itemTag.addEventListener('keydown', (ev) => {
+ if (!['ArrowDown', 'ArrowUp', 'Enter'].includes(ev.code)) {
+ focusedFromList = true;
+ searchTag.focus();
+ return;
+ }
+ ev.preventDefault();
+
+ // Select on Enter
+ if (ev.code === 'Enter') {
+ selectItem();
+ return;
+ }
+
+ // Navigate on ArrowDown/Up
+ let sibling;
+ if (ev.code === 'ArrowDown') {
+ sibling = ev.target.nextElementSibling;
+ }
+ if (ev.code === 'ArrowUp') {
+ sibling = ev.target.previousElementSibling;
+ }
+ if (sibling && !sibling.classList.contains('np-select-loader')) {
+ sibling.focus();
+ }
+ });
+
+ itemTag.addEventListener('click', selectItem);
+
+ itemTag.addEventListener('focus', (ev) => {
+ keyboardFocusedId = ev.target.getAttribute('data-value');
+ });
+
+ itemTag.addEventListener('focusout', (ev) => {
+ if (rerendering) return;
+ keyboardFocusedId = null;
+ focusOut(ev);
+ });
+
+ listTag.append(itemTag);
+ });
+
+ const infoTagKeydown = () => {
+ focusedFromList = true;
+ searchTag.focus();
+ };
+
+ if (!loading && storeOptions.length === 0) {
+ const noOptionsTag = createTag(
+ 'div',
+ { class: 'np-select-list-tag np-select-no-options', tabindex: -1 },
+ noOptionsText,
+ );
+ noOptionsTag.addEventListener('keydown', infoTagKeydown);
+ noOptionsTag.addEventListener('focusout', focusOut);
+ listTag.append(noOptionsTag);
+ }
+
+ if (loading) {
+ const loadingTag = createTag(
+ 'div',
+ { class: 'np-select-list-tag np-select-loader', tabindex: -1 },
+ `${loadingText}...`,
+ );
+ loadingTag.addEventListener('keydown', infoTagKeydown);
+ loadingTag.addEventListener('focusout', focusOut);
+ listTag.append(loadingTag);
+ }
+
+ if (keyboardFocusedId) {
+ const itemToFocus = listTag.querySelector(`li[data-value='${keyboardFocusedId}']`);
+ if (itemToFocus) itemToFocus.focus();
+ }
+
+ rerendering = false;
+ });
+
+ listContainerTag.append(listTag);
+ if (footerTag) {
+ listContainerTag.append(footerTag);
+ footerTag.addEventListener('focusout', focusOut);
+ }
+
+ controlTag.append(labelTag, searchTag, valueTag, listContainerTag);
+
+ if (!hideIcon) {
+ const arrowIconTag = getNonprofitIconTag(NONPRFIT_ICONS.CHEVRON_DOWN);
+ controlTag.append(arrowIconTag);
+ }
+
+ controlTag.onInput = (handler) => {
+ onInput = handler;
+ };
+
+ controlTag.onSelect = (handler) => {
+ onSelect = handler;
+ };
+
+ controlTag.onScroll = (handler) => {
+ listTag.addEventListener('scroll', (ev) => {
+ abortController = new AbortController();
+ handler(ev.target, abortController, hasNewInput);
+ });
+ };
+
+ controlTag.enable = () => {
+ searchTag.removeAttribute('disabled');
+ valueTag.removeAttribute('disabled');
+ };
+
+ controlTag.clear = (withFocus = true) => {
+ searchTag.value = '';
+ valueTag.value = '';
+ valueTag.dispatchEvent(new Event('input'));
+ localSelection = null;
+ if (!store) {
+ optionsStore.update(localOptions);
+ }
+ if (withFocus) {
+ searchTag.focus();
+ }
+ };
+
+ controlTag.updateOptions = (newOptions) => {
+ optionsStore.update(newOptions);
+ localOptions = newOptions;
+ };
+
+ controlTag.getValue = () => valueTag.value;
+
+ return controlTag;
+}
diff --git a/creativecloud/blocks/nonprofit/nonprofit.css b/creativecloud/blocks/nonprofit/nonprofit.css
new file mode 100644
index 000000000..ffaca8512
--- /dev/null
+++ b/creativecloud/blocks/nonprofit/nonprofit.css
@@ -0,0 +1,504 @@
+:root {
+ --np-content-width: 378px;
+ --np-container-padding: 86px;
+ --np-stepper-max-height: 27px;
+ --np-content-max-height: 727px;
+ --np-input-placeholder-color: #757575;
+ --np-organization-item-height: 72px;
+ --np-organization-cannot-find-height: 51px;
+ --np-selected-organization-fallback-background: #da408b;
+ --np-button-disabled-background: #e6e6e6;
+ --np-button-disabled-color: #b1b1b1;
+}
+
+.np-container {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ padding-block: var(--np-container-padding);
+ position: relative;
+ height: calc(
+ var(--np-stepper-max-height) + var(--np-content-max-height) + 2 * var(--np-container-padding)
+ );
+ box-sizing: border-box;
+}
+
+.np-icon path {
+ fill: var(--color-gray-600);
+}
+
+.np-stepper-container {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ max-height: var(--np-stepper-max-height);
+}
+
+.np-step-container {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ flex-shrink: 0;
+}
+
+.np-step-container span {
+ user-select: none;
+}
+
+.np-step-icon {
+ width: 20px;
+ height: 20px;
+ border-radius: 10px;
+ background-color: var(--color-gray-300);
+ color: var(--color-white);
+ font-weight: 700;
+ font-size: 16px;
+ line-height: 18px;
+ text-align: center;
+ vertical-align: middle;
+}
+
+.np-step-name {
+ font-size: 16px;
+ color: var(--color-gray-300);
+}
+
+.np-step-container.is-cleared .np-step-icon {
+ background-color: var(--color-gray-600);
+}
+
+.np-step-container.is-cleared .np-step-name {
+ color: var(--color-gray-600);
+}
+
+.np-step-container.is-active .np-step-icon {
+ background-color: var(--link-color);
+}
+
+.np-step-container.is-active .np-step-name {
+ color: var(--link-color);
+}
+
+.np-step-separator {
+ width: 12px;
+ height: 12px;
+}
+
+.np-step-separator.np-icon path {
+ fill: none;
+}
+
+.np-stepper-back {
+ position: absolute;
+ top: 440px;
+ left: 10%;
+ width: 50px;
+ height: 50px;
+ background-color: var(--color-gray-200);
+ border-radius: 25px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ cursor: pointer;
+ user-select: none;
+ transition: left 200ms;
+}
+
+.np-stepper-back:hover,
+.np-stepper-back:focus {
+ background-color: var(--color-gray-300);
+}
+
+.np-stepper-back:hover .np-icon path,
+.np-stepper-back:focus .np-icon path {
+ fill: var(--color-gray-800);
+}
+
+.np-stepper-back.disabled {
+ background-color: var(--color-gray-100);
+}
+
+.np-content-container {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ max-height: var(--np-content-max-height);
+}
+
+.np-description {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ padding-block: 56px;
+ gap: 16px;
+}
+
+.np-title {
+ font-weight: 700;
+ line-height: 35px;
+ font-size: 24px;
+ text-align: center;
+}
+
+.np-subtitle {
+ text-align: center;
+ max-width: 800px;
+}
+
+.np-form {
+ width: var(--np-content-width);
+ display: flex;
+ flex-direction: column;
+ gap: 25px;
+}
+
+.np-button {
+ background-color: var(--link-color);
+ color: var(--color-white);
+ font-weight: 700;
+ font-size: 16px;
+ line-height: 20px;
+ border: none;
+ border-radius: 20px;
+ padding: 10px 20px;
+ cursor: pointer;
+}
+
+.np-button:disabled {
+ background-color: var(--np-button-disabled-background);
+ color: var(--np-button-disabled-color);
+ cursor: default;
+}
+
+.np-form .np-button {
+ align-self: start;
+}
+
+.np-control {
+ display: flex;
+ flex-direction: column;
+ gap: 7px;
+ position: relative;
+}
+
+.np-label {
+ font-weight: 400;
+ font-size: 14px;
+ line-height: 18px;
+ color: var(--color-gray-700);
+}
+
+.np-input {
+ width: 100%;
+ border: 1px solid var(--color-gray-500);
+ line-height: 20px;
+ padding: 9px 15px;
+ box-sizing: border-box;
+ border-radius: 4px;
+ appearance: none;
+}
+
+.np-input:disabled {
+ border: 1px solid var(--color-gray-300);
+}
+
+.np-input::placeholder {
+ color: var(--np-input-placeholder-color);
+}
+
+.np-control .np-icon {
+ position: absolute;
+ height: 12px;
+ right: 15px;
+ top: 39px;
+ display: flex;
+ cursor: pointer;
+ pointer-events: none;
+}
+
+.np-input:disabled ~ .np-icon path,
+.np-stepper-back.disabled .np-icon path {
+ fill: var(--color-gray-400);
+}
+
+.np-input[type='file'] ~ .np-input {
+ cursor: pointer;
+}
+
+.np-select-search {
+ appearance: none;
+ padding-right: 40px;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.np-select-list-container {
+ display: none;
+ position: absolute;
+ list-style-type: none;
+ border: 1px solid var(--color-gray-500);
+ background-color: var(--color-white);
+ border-radius: 4px;
+ box-sizing: border-box;
+ width: 100%;
+ top: 100%;
+ margin-top: 4px;
+ min-height: var(--np-organization-cannot-find-height);
+ max-height: calc(
+ 4 * var(--np-organization-item-height) + var(--np-organization-cannot-find-height) + 1px
+ );
+ overflow: hidden;
+ z-index: 1;
+ box-shadow: 0 2px 6px -1px rgb(0 0 0 / 10%);
+}
+
+.np-select-list {
+ list-style-type: none;
+ width: 100%;
+ margin: 0;
+ padding: 5px 0;
+ max-height: calc(4 * var(--np-organization-item-height));
+ overflow-x: hidden;
+ overflow-y: auto;
+}
+
+.np-select-list-tag {
+ padding: 10px 15px;
+ font-size: 16px;
+ line-height: 21px;
+ cursor: pointer;
+ display: flex;
+ flex-direction: column;
+ outline: none;
+ overflow: hidden;
+}
+
+.np-select-item:hover {
+ background-color: var(--color-gray-100);
+}
+
+.np-select-item:focus {
+ background-color: var(--color-gray-200);
+}
+
+.np-select-item span {
+ line-height: 21px;
+ width: 100%;
+}
+
+.np-organization-select-name {
+ font-weight: 700;
+ padding-bottom: 10px;
+}
+
+.np-organization-select-id {
+ font-weight: 400;
+}
+
+.np-organization-select-name,
+.np-organization-select-id {
+ text-wrap: nowrap;
+ text-overflow: ellipsis;
+ overflow: hidden;
+}
+
+.np-select-no-options {
+ color: var(--np-input-placeholder-color);
+}
+
+.np-select-no-options,
+.np-select-loader {
+ user-select: none;
+}
+
+.np-selected-organization-container {
+ display: none;
+ border: 1px solid var(--color-gray-500);
+ border-radius: 4px;
+ box-sizing: border-box;
+ flex-direction: column;
+ padding-inline: 15px;
+ position: relative;
+}
+
+.np-selected-organization-container .np-selected-organization-detail {
+ font-size: 16px;
+ font-weight: 400;
+ line-height: 21px;
+ padding-block: 10px;
+}
+
+.np-selected-organization-container > .np-selected-organization-detail {
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ overflow: hidden;
+}
+
+.np-selected-organization-header {
+ display: flex;
+ align-items: center;
+ padding-block: 10px;
+ gap: 10px;
+}
+
+.np-selected-organization-header .np-selected-organization-detail {
+ padding-right: 15px;
+}
+
+.np-selected-organization-avatar {
+ width: 40px;
+ height: 40px;
+ border-radius: 20px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ flex-shrink: 0;
+}
+
+.np-selected-organization-avatar.loading {
+ animation: loadingAvatar 1s infinite;
+}
+
+.np-selected-organization-avatar.fallback {
+ background-color: var(--np-selected-organization-fallback-background);
+}
+
+.np-selected-organization-logo {
+ object-fit: contain;
+}
+
+.np-selected-organization-avatar.fallback .np-selected-organization-logo {
+ display: none;
+}
+
+.np-selected-organization-initials {
+ color: var(--color-white);
+ font-size: 18px;
+ font-weight: 700;
+}
+
+.np-selected-organization-avatar:not(.fallback) .np-selected-organization-initials {
+ display: none;
+}
+
+.np-selected-organization-separator {
+ height: 1px;
+ background-color: var(--color-gray-300);
+}
+
+.np-selected-organization-clear {
+ position: absolute;
+ display: flex;
+ top: 15px;
+ right: 15px;
+ cursor: pointer;
+}
+
+.np-selected-organization-clear:hover .np-icon path,
+.np-selected-organization-clear:focus .np-icon path {
+ fill: var(--color-gray-800);
+}
+
+.np-personal-data-disclaimer {
+ font-size: 14px;
+ line-height: 18px;
+ padding-bottom: 15px;
+ color: var(--color-gray-600);
+}
+
+.np-application-review-container {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ margin-block: 56px;
+}
+
+.np-application-review-container .np-title {
+ margin-bottom: 16px;
+}
+
+.np-application-review-detail {
+ margin-block: 16px;
+ font-size: 18px;
+ line-height: 24px;
+ font-weight: 400;
+}
+
+@media screen and (max-width: 900px) {
+ .np-stepper-back {
+ left: 4%;
+ }
+}
+
+@keyframes loadingAvatar {
+ 0% {
+ background-color: var(--color-gray-200);
+ }
+
+ 50% {
+ background-color: var(--color-gray-100);
+ }
+
+ 100% {
+ background-color: var(--color-gray-200);
+ }
+}
+
+/* Temporary stuff below (TODO - remove) */
+.nonprofit {
+ display: flex;
+ flex-direction: column;
+}
+
+.np-controller-buffer {
+ flex-grow: 1;
+}
+
+.np-controller-container {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ margin-bottom: 50px;
+ gap: 10px;
+}
+
+.np-controller-title {
+ font-weight: 700;
+ font-size: 16px;
+ color: var(--link-color);
+}
+
+.np-controller-button {
+ border: 2px solid var(--link-color);
+ background-color: var(--color-white);
+ color: var(--link-color);
+ font-weight: 700;
+ font-size: 16px;
+ line-height: 20px;
+ border-radius: 20px;
+ padding: 10px 20px;
+ align-self: start;
+ cursor: pointer;
+ width: 200px;
+ margin-right: 12px;
+ white-space: nowrap;
+
+ &.is-step {
+ width: 100px;
+ margin-right: 3px;
+ }
+
+ &.selected {
+ background-color: var(--link-color);
+ color: var(--color-white);
+ }
+}
+
+.np-controller-section {
+ display: flex;
+ width: 415px;
+
+ & .np-controller-button:last-child {
+ margin: 0;
+ }
+}
diff --git a/creativecloud/blocks/nonprofit/nonprofit.js b/creativecloud/blocks/nonprofit/nonprofit.js
new file mode 100644
index 000000000..61f172ef5
--- /dev/null
+++ b/creativecloud/blocks/nonprofit/nonprofit.js
@@ -0,0 +1,970 @@
+/* eslint-disable no-alert */
+/* eslint-disable no-plusplus */
+/* eslint-disable no-underscore-dangle */
+/* eslint-disable max-len */
+import ReactiveStore from './reactiveStore.js';
+import { setLibs } from '../../scripts/utils.js';
+import { countries } from './constants.js';
+import { getNonprofitIconTag, NONPRFIT_ICONS } from './icons.js';
+import nonprofitSelect from './nonprofit-select.js';
+
+const miloLibs = setLibs('/libs');
+const { createTag } = await import(`${miloLibs}/utils/utils.js`);
+
+const removeOptionElements = (element) => {
+ const children = element.querySelectorAll(':scope > div');
+ children.forEach((child) => {
+ child.remove();
+ });
+};
+
+// #region Constants
+
+const PERCENT_API_URL = 'https://sandbox-api.poweredbypercent.com/v1';
+const PERCENT_VALIDATION_API_URL = 'https://sandbox-validate.poweredbypercent.com/adobe-acrobat';
+const PERCENT_PUBLISHABLE_KEY = 'sandbox_pk_8b320cc4-5950-4263-a3ac-828c64f6e19b';
+export const SCENARIOS = Object.freeze({
+ FOUND_IN_SEARCH: 'FOUND_IN_SEARCH',
+ NOT_FOUND_IN_SEARCH: 'NOT_FOUND_IN_SEARCH',
+});
+const SEARCH_DEBOUNCE = 500; // ms
+const FETCH_ON_SCROLL_OFFSET = 100; // px
+// #endregion
+
+const nonprofitFormData = JSON.parse('{}');
+
+// #region Stores
+export const stepperStore = new ReactiveStore({
+ step: 1,
+ scenario: SCENARIOS.FOUND_IN_SEARCH,
+ pending: false,
+});
+
+export const organizationsStore = new ReactiveStore([]);
+
+export const registriesStore = new ReactiveStore([]);
+
+const selectedOrganizationStore = new ReactiveStore();
+// #endregion
+
+// #region Percent API integration
+
+// #region Helpers
+
+function getPercentErrorString(result) {
+ return `${result.error.title}: ${result.error.message}${result.error.reasons ? ` (${result.error.reasons.join(', ')})` : ''}`;
+}
+
+async function validatePercentResponse(response) {
+ const result = await response.json();
+
+ if (!response.ok) {
+ throw new Error(getPercentErrorString(result));
+ }
+
+ return result;
+}
+
+// #endregion
+
+let nextOrganizationsPageUrl;
+
+async function fetchOrganizations(search, countryCode, abortController) {
+ try {
+ organizationsStore.startLoading(true);
+ const response = await fetch(
+ `${PERCENT_API_URL}/organisations?countryCode=${countryCode}&query=${search}`,
+ {
+ cache: 'force-cache',
+ signal: abortController.signal,
+ headers: { Authorization: PERCENT_PUBLISHABLE_KEY },
+ },
+ );
+
+ const result = await validatePercentResponse(response);
+
+ if (!result._links) {
+ nextOrganizationsPageUrl = null;
+ window.lana?.log('No next organization page link provided.');
+ } else nextOrganizationsPageUrl = result._links.next || null;
+ organizationsStore.update(result.data);
+ } catch (error) {
+ organizationsStore.update((prev) => prev);
+ window.lana?.log(`Could not fetch organizations: ${error}`);
+ }
+}
+
+async function fetchNextOrganizations(abortController) {
+ if (!nextOrganizationsPageUrl) return;
+ try {
+ organizationsStore.startLoading();
+ const response = await fetch(nextOrganizationsPageUrl, {
+ cache: 'force-cache',
+ signal: abortController.signal,
+ headers: { Authorization: PERCENT_PUBLISHABLE_KEY },
+ });
+
+ const result = await validatePercentResponse(response);
+
+ nextOrganizationsPageUrl = result._links.next;
+ organizationsStore.update((prev) => [...prev, ...result.data]);
+ } catch (error) {
+ organizationsStore.update((prev) => prev);
+ window.lana?.log(`Could not fetch next organizations: ${error}`);
+ }
+}
+
+async function fetchRegistries(countryCode, abortController) {
+ try {
+ registriesStore.startLoading(true);
+ const response = await fetch(`${PERCENT_API_URL}/registries?countryCode=${countryCode}`, {
+ cache: 'force-cache',
+ signal: abortController.signal,
+ headers: { Authorization: PERCENT_PUBLISHABLE_KEY },
+ });
+
+ const result = await validatePercentResponse(response);
+
+ registriesStore.update(result.data);
+ } catch (error) {
+ registriesStore.update((prev) => prev);
+ window.lana?.log(`Could not fetch registries: ${error}`);
+ }
+}
+
+async function sendOrganizationData() {
+ try {
+ const inviteResponse = await fetch(PERCENT_VALIDATION_API_URL, {
+ method: 'POST',
+ headers: { Authorization: `Bearer ${PERCENT_PUBLISHABLE_KEY}` },
+ });
+
+ const inviteResult = await validatePercentResponse(inviteResponse);
+
+ const { validationInviteId } = inviteResult.data;
+
+ const foundInSearch = stepperStore.data.scenario === SCENARIOS.FOUND_IN_SEARCH;
+
+ if (!foundInSearch) {
+ const evidenceUploadData = new FormData();
+ evidenceUploadData.append('file', nonprofitFormData.evidenceNonProfitStatus);
+ evidenceUploadData.append('validationInviteId', validationInviteId);
+
+ const uploadResponse = await fetch(`${PERCENT_API_URL}/validation-submission-documents`, {
+ method: 'POST',
+ headers: { Authorization: PERCENT_PUBLISHABLE_KEY },
+ body: evidenceUploadData,
+ });
+
+ await validatePercentResponse(uploadResponse);
+ }
+
+ let body;
+ if (foundInSearch) {
+ body = JSON.stringify({
+ validationInviteId,
+ organisationId: nonprofitFormData.organizationId,
+ firstName: nonprofitFormData.firstName,
+ lastName: nonprofitFormData.lastName,
+ email: nonprofitFormData.email,
+ language: 'en-US',
+ });
+ } else {
+ body = JSON.stringify({
+ validationInviteId,
+ countryCode: nonprofitFormData.countryCode,
+ organisationName: nonprofitFormData.organizationName,
+ registryId: nonprofitFormData.organizationRegistrationId,
+ registryName: nonprofitFormData.registryName,
+ website: nonprofitFormData.website,
+ addressLine1: nonprofitFormData.streetAddress,
+ addressLine2: nonprofitFormData.addressDetails,
+ city: nonprofitFormData.city,
+ postal: nonprofitFormData.zipCode,
+ state: nonprofitFormData.state,
+ firstName: nonprofitFormData.firstName,
+ lastName: nonprofitFormData.lastName,
+ email: nonprofitFormData.email,
+ language: 'en-US',
+ });
+ }
+
+ const submissionResponse = await fetch(`${PERCENT_API_URL}/validation-submissions`, {
+ method: 'POST',
+ body,
+ headers: {
+ Authorization: PERCENT_PUBLISHABLE_KEY,
+ 'Content-Type': 'application/json; charset=utf-8',
+ },
+ });
+
+ await validatePercentResponse(submissionResponse);
+
+ return true;
+ } catch (error) {
+ window.lana?.log(`Could not send organization data: ${error}`);
+ return false;
+ }
+}
+
+// #endregion
+
+// UI
+
+function getStepBackTag() {
+ const buttonTag = createTag('div', { class: 'np-stepper-back', tabindex: 0 });
+ const backIconTag = getNonprofitIconTag(NONPRFIT_ICONS.BACK);
+ buttonTag.append(backIconTag);
+
+ stepperStore.subscribe(({ step, scenario, pending }) => {
+ if (pending) buttonTag.classList.add('disabled');
+ else buttonTag.classList.remove('disabled');
+ if (step === 1 || (step === 3 && scenario === SCENARIOS.FOUND_IN_SEARCH) || step === 5) {
+ buttonTag.style.display = 'none';
+ return;
+ }
+ buttonTag.style.display = 'flex';
+ });
+
+ buttonTag.addEventListener('click', () => {
+ if (stepperStore.data.pending) return;
+ stepperStore.update((prev) => ({ ...prev, step: prev.step - 1 }));
+ });
+
+ buttonTag.addEventListener('keydown', (ev) => {
+ if (stepperStore.data.pending) return;
+ if (ev.code !== 'Enter') return;
+ ev.preventDefault();
+ buttonTag.click();
+ });
+
+ return buttonTag;
+}
+
+function renderStepper(containerTag) {
+ const stepperContainerTag = createTag('div', { class: 'np-stepper-container' });
+ const getStepTag = (number) => {
+ const stepContainerTag = createTag('div', { class: 'np-step-container', 'data-step': number });
+ const stepIconTag = createTag('span', { class: 'np-step-icon' }, number);
+ const stepNameTag = createTag(
+ 'span',
+ { class: 'np-step-name' },
+ window.mph[`nonprofit-step-${number}`],
+ );
+ stepContainerTag.append(stepIconTag, stepNameTag);
+ return stepContainerTag;
+ };
+
+ const step1 = getStepTag(1);
+ const step2 = getStepTag(2);
+ const step3 = getStepTag(3);
+
+ stepperStore.subscribe(({ step, scenario }) => {
+ // Reset steps
+ step1.classList.remove('is-cleared', 'is-active');
+ step2.classList.remove('is-cleared', 'is-active');
+ step3.classList.remove('is-cleared', 'is-active');
+
+ if (step === 1) {
+ step1.classList.add('is-active');
+ }
+ if (step === 2) {
+ step1.classList.add('is-cleared');
+ step2.classList.add('is-active');
+ }
+ if (step === 3) {
+ if (scenario === SCENARIOS.FOUND_IN_SEARCH) {
+ step1.classList.add('is-cleared');
+ step2.classList.add('is-cleared');
+ step3.classList.add('is-active');
+ } else {
+ step1.classList.add('is-cleared');
+ step2.classList.add('is-active');
+ }
+ }
+ if (step === 4) {
+ step1.classList.add('is-cleared');
+ step2.classList.add('is-active');
+ }
+ if (step === 5) {
+ step1.classList.add('is-cleared');
+ step2.classList.add('is-cleared');
+ step3.classList.add('is-active');
+ }
+ });
+
+ const separatorIconTag = getNonprofitIconTag(NONPRFIT_ICONS.CHEVRON_RIGHT);
+ separatorIconTag.classList.add('np-step-separator');
+
+ stepperContainerTag.append(
+ step1,
+ separatorIconTag.cloneNode(true),
+ step2,
+ separatorIconTag.cloneNode(true),
+ step3,
+ );
+
+ const stepBackTag = getStepBackTag();
+
+ containerTag.append(stepperContainerTag, stepBackTag);
+}
+
+// #region Render form
+
+function getDescriptionTag(title, subtitle) {
+ const descriptionTag = createTag('div', { class: 'np-description' });
+ const titleTag = createTag('span', { class: 'np-title' }, title);
+
+ descriptionTag.append(titleTag);
+
+ if (subtitle) {
+ const subtitleTag = createTag('span', { class: 'np-subtitle' }, subtitle);
+
+ descriptionTag.append(subtitleTag);
+ }
+
+ return descriptionTag;
+}
+
+function getSubmitTag() {
+ return createTag('input', {
+ class: 'np-button',
+ type: 'submit',
+ value: window.mph['nonprofit-continue'],
+ disabled: 'disabled',
+ });
+}
+
+function getNonprofitInput(params) {
+ const { type, name, label, placeholder, required } = params;
+ const baseParams = { name, placeholder };
+ if (required) baseParams.required = 'required';
+ const controlTag = createTag('div', { class: 'np-control' });
+ const labelTag = createTag('label', { class: 'np-label', for: name }, label);
+ const inputTag = createTag('input', {
+ class: `np-input${required ? ' np-required-field' : ''}`,
+ type,
+ ...baseParams,
+ });
+ controlTag.append(labelTag, inputTag);
+
+ // File validation
+ if (type === 'file') {
+ // Hide input and render a text one
+ inputTag.style.display = 'none';
+ const textTag = createTag('input', {
+ type: 'text',
+ class: 'np-input',
+ placeholder,
+ readonly: 'readonly',
+ 'data-for': name,
+ });
+
+ textTag.addEventListener('click', () => {
+ inputTag.click();
+ });
+
+ textTag.addEventListener('keypress', (ev) => {
+ if (ev.code !== 'Enter') return;
+ ev.preventDefault();
+ inputTag.click();
+ });
+
+ // Validation
+ inputTag.addEventListener('change', () => {
+ if (!inputTag.files || inputTag.files.length === 0) {
+ textTag.value = '';
+ return;
+ }
+
+ const file = inputTag.files[0];
+
+ // Percent only accepts jpg, png and pdf files
+ const extensionRegex = /(\.jpg|\.jpeg|\.png|\.pdf)$/i;
+ if (!extensionRegex.exec(file.name)) {
+ inputTag.value = '';
+ inputTag.dispatchEvent(new Event('input'));
+ alert(window.mph['nonprofit-invalid-file-type']);
+ return;
+ }
+
+ // Percent acceps files up to 5 mb
+ const size = file.size / 1024 ** 2;
+ if (size > 5) {
+ inputTag.value = '';
+ inputTag.dispatchEvent(new Event('input'));
+ alert(window.mph['nonprofit-file-size-exceeded']);
+ return;
+ }
+
+ textTag.value = file.name;
+ });
+
+ const uploadIconTag = getNonprofitIconTag(NONPRFIT_ICONS.UPLOAD);
+
+ controlTag.append(textTag, uploadIconTag);
+ }
+
+ return controlTag;
+}
+
+function getSelectedOrganizationTag() {
+ const containerTag = createTag('div', { class: 'np-selected-organization-container' });
+
+ const headerTag = createTag('div', { class: 'np-selected-organization-header' });
+
+ const avatarTag = createTag('div', { class: 'np-selected-organization-avatar' });
+
+ const initialsTag = createTag('span', { class: 'np-selected-organization-initials' });
+ const showInitials = () => {
+ avatarTag.classList.add('fallback');
+ const initialWords = selectedOrganizationStore.data.name
+ .split(' ')
+ .filter((word) => Boolean(word))
+ .slice(0, 2);
+ const initials = initialWords.map((word) => word.substring(0, 1).toUpperCase()).join('');
+ initialsTag.textContent = initials;
+ };
+
+ const logoTag = createTag('img', { class: 'np-selected-organization-logo' });
+ logoTag.addEventListener('error', () => {
+ avatarTag.classList.remove('loading');
+ showInitials();
+ });
+ logoTag.addEventListener('load', () => {
+ avatarTag.classList.remove('loading');
+ });
+
+ avatarTag.append(initialsTag, logoTag);
+
+ const nameTag = createTag('span', { class: 'np-selected-organization-detail' });
+ headerTag.append(avatarTag, nameTag);
+
+ const separatorTag = createTag('div', { class: 'np-selected-organization-separator' });
+
+ const addressTag = createTag('span', { class: 'np-selected-organization-detail' });
+ const idTag = createTag('span', { class: 'np-selected-organization-detail' });
+
+ const clearTag = createTag('div', { class: 'np-selected-organization-clear', tabindex: 0 });
+ const clearIconTag = getNonprofitIconTag(NONPRFIT_ICONS.CLOSE);
+ clearTag.append(clearIconTag);
+
+ clearTag.addEventListener('keydown', (ev) => {
+ if (ev.code !== 'Enter') return;
+ clearTag.click();
+ });
+
+ containerTag.append(headerTag, separatorTag, addressTag, idTag, clearTag);
+
+ selectedOrganizationStore.subscribe((organization) => {
+ if (!organization) {
+ containerTag.style.display = 'none';
+ return;
+ }
+
+ // Load avatar
+ if (organization.logo) {
+ avatarTag.classList.add('loading');
+ avatarTag.classList.remove('fallback');
+ logoTag.src = organization.logo;
+ } else {
+ showInitials();
+ }
+
+ nameTag.textContent = organization.name;
+
+ addressTag.textContent = organization.address || '-';
+ addressTag.setAttribute('title', organization.address);
+ idTag.textContent = organization.id;
+ idTag.setAttribute('title', organization.id);
+
+ containerTag.style.display = 'flex';
+ }, false);
+
+ containerTag.onClear = (handler) => {
+ clearTag.addEventListener('click', handler);
+ };
+
+ return containerTag;
+}
+
+function trackSubmitCondition(formTag) {
+ const requiredInputs = formTag.querySelectorAll('.np-required-field');
+ const submitTag = formTag.querySelector('.np-button[type=submit]');
+
+ for (let index = 0; index < requiredInputs.length; index++) {
+ const requiredInput = requiredInputs[index];
+ requiredInput.addEventListener('input', () => {
+ if (!requiredInput.value) {
+ submitTag.setAttribute('disabled', 'disabled');
+ } else {
+ let hasEmptyFields = false;
+ requiredInputs.forEach((input) => {
+ if (input === requiredInput) return;
+ if (!input.value) hasEmptyFields = true;
+ });
+ if (hasEmptyFields) {
+ submitTag.setAttribute('disabled', 'disabled');
+ } else {
+ submitTag.removeAttribute('disabled');
+ }
+ }
+ });
+ }
+}
+
+// Select non-profit
+function renderSelectNonprofit(containerTag) {
+ // Description
+ const descriptionTag = getDescriptionTag(
+ window.mph['nonprofit-title-select-non-profit'],
+ window.mph['nonprofit-subtitle-select-non-profit'],
+ );
+
+ // Form
+ const formTag = createTag('form', { class: 'np-form' });
+
+ const countryTag = nonprofitSelect({
+ createTag,
+ name: 'country',
+ label: window.mph['nonprofit-country'],
+ placeholder: window.mph['nonprofit-country-placeholder'],
+ options: countries,
+ labelKey: 'name',
+ valueKey: 'code',
+ });
+
+ // #region Organization select
+ const organizationTag = nonprofitSelect({
+ createTag,
+ name: 'organizationId',
+ label: window.mph['nonprofit-organization-name-or-id'],
+ placeholder: window.mph['nonprofit-organization-name-or-id-search-placeholder'],
+ noOptionsText: window.mph['nonprofit-not-found-in-database'],
+ debounce: SEARCH_DEBOUNCE,
+ store: organizationsStore,
+ disabled: true,
+ hideIcon: true,
+ clearable: true,
+ labelKey: 'name',
+ valueKey: 'id',
+ renderOption: (option, itemTag) => {
+ const nameTag = createTag(
+ 'span',
+ { class: 'np-organization-select-name', title: option.name },
+ option.name,
+ );
+ const idTag = createTag(
+ 'span',
+ { class: 'np-organization-select-id', title: option.id },
+ option.id,
+ );
+
+ itemTag.append(nameTag, idTag);
+ },
+ footerTag: (() => {
+ const cannotFindTag = createTag('div', { class: 'np-select-list-tag np-organization-cannot-find' });
+ const cannotFindLinkTag = createTag(
+ 'a',
+ { tabindex: 0 },
+ window.mph['nonprofit-organization-cannot-find'],
+ );
+ // Cannot find action handler
+ const switchToNotFound = () => {
+ stepperStore.update((prev) => ({
+ ...prev,
+ step: 2,
+ scenario: SCENARIOS.NOT_FOUND_IN_SEARCH,
+ }));
+ };
+ cannotFindLinkTag.addEventListener('click', switchToNotFound);
+ cannotFindLinkTag.addEventListener('keydown', (ev) => {
+ if (ev.code !== 'Enter') return;
+ switchToNotFound();
+ });
+
+ cannotFindTag.append(cannotFindLinkTag);
+
+ return cannotFindTag;
+ })(),
+ });
+
+ organizationTag.onInput((value, abortController) => {
+ if (!value) return;
+ fetchOrganizations(value, countryTag.getValue(), abortController);
+ });
+
+ organizationTag.onSelect((option) => {
+ selectedOrganizationStore.update(option);
+ });
+
+ organizationTag.onScroll((listTag, abortController, hasNewInput) => {
+ if (
+ (Boolean(selectedOrganizationStore.data) && !hasNewInput)
+ || organizationsStore.loading
+ || !nextOrganizationsPageUrl
+ ) return;
+ if (listTag.scrollTop + listTag.clientHeight + FETCH_ON_SCROLL_OFFSET >= listTag.scrollHeight) {
+ fetchNextOrganizations(abortController);
+ }
+ });
+
+ countryTag.onSelect(() => {
+ organizationTag.enable();
+ organizationTag.clear();
+ if (selectedOrganizationStore.data) {
+ selectedOrganizationStore.update(null);
+ }
+ });
+
+ // #endregion
+
+ const selectedOrganizationTag = getSelectedOrganizationTag();
+
+ selectedOrganizationTag.onClear(() => {
+ organizationTag.clear();
+ selectedOrganizationStore.update(null);
+ });
+
+ const submitTag = getSubmitTag();
+
+ formTag.append(countryTag, organizationTag, selectedOrganizationTag, submitTag);
+
+ trackSubmitCondition(formTag);
+
+ formTag.addEventListener('submit', (ev) => {
+ ev.preventDefault();
+
+ const formData = new FormData(formTag);
+ nonprofitFormData.countryCode = formData.get('country');
+ nonprofitFormData.organizationId = formData.get('organizationId');
+
+ stepperStore.update((prev) => ({ ...prev, scenario: SCENARIOS.FOUND_IN_SEARCH, step: 2 }));
+ });
+
+ containerTag.replaceChildren(descriptionTag, formTag);
+}
+
+// Organization details
+function renderOrganizationDetails(containerTag) {
+ // Description
+ const descriptionTag = getDescriptionTag(window.mph['nonprofit-title-organization-details']);
+
+ // Form
+ const formTag = createTag('form', { class: 'np-form' });
+
+ let abortController;
+
+ const countryTag = nonprofitSelect({
+ createTag,
+ name: 'country',
+ label: window.mph['nonprofit-country'],
+ placeholder: window.mph['nonprofit-country-placeholder'],
+ options: countries,
+ labelKey: 'name',
+ valueKey: 'code',
+ });
+
+ countryTag.onSelect((option) => {
+ abortController?.abort();
+ abortController = new AbortController();
+ fetchRegistries(option.code, abortController);
+ });
+
+ const organizationNameTag = getNonprofitInput({
+ type: 'text',
+ name: 'organizationName',
+ label: window.mph['nonprofit-organization-name'],
+ placeholder: window.mph['nonprofit-organization-name-placeholder'],
+ required: true,
+ });
+
+ const registryTag = nonprofitSelect({
+ createTag,
+ name: 'registry',
+ label: window.mph['nonprofit-registry'],
+ placeholder: window.mph['nonprofit-registry-placeholder'],
+ labelKey: 'name',
+ valueKey: 'name',
+ disabled: true,
+ });
+
+ registriesStore.subscribe((registries, loading) => {
+ if (!countryTag.getValue()) return;
+ if (loading) {
+ registryTag.clear(false);
+ return;
+ }
+ registryTag.enable();
+ registryTag.updateOptions(registries);
+ });
+
+ const organizationRegistrationIdTag = getNonprofitInput({
+ type: 'text',
+ name: 'organizationRegistrationId',
+ label: window.mph['nonprofit-organization-registration-id'],
+ placeholder: window.mph['nonprofit-organization-registration-id-placeholder'],
+ required: true,
+ });
+
+ const evidenceNonProfitStatusTag = getNonprofitInput({
+ type: 'file',
+ name: 'evidenceNonProfitStatus',
+ label: window.mph['nonprofit-evidence-non-profit-status'],
+ placeholder: window.mph['nonprofit-evidence-non-profit-status-placeholder'],
+ required: true,
+ });
+
+ const websiteTag = getNonprofitInput({
+ type: 'text',
+ name: 'website',
+ label: window.mph['nonprofit-website'],
+ placeholder: window.mph['nonprofit-website-placeholder'],
+ required: true,
+ });
+
+ const submitTag = getSubmitTag();
+
+ formTag.append(
+ countryTag,
+ organizationNameTag,
+ registryTag,
+ organizationRegistrationIdTag,
+ evidenceNonProfitStatusTag,
+ websiteTag,
+ submitTag,
+ );
+
+ trackSubmitCondition(formTag);
+
+ formTag.addEventListener('submit', (ev) => {
+ ev.preventDefault();
+
+ const formData = new FormData(formTag);
+ nonprofitFormData.countryCode = formData.get('country');
+ nonprofitFormData.organizationName = formData.get('organizationName');
+ nonprofitFormData.registryName = formData.get('registry');
+ nonprofitFormData.organizationRegistrationId = formData.get('organizationRegistrationId');
+ nonprofitFormData.evidenceNonProfitStatus = formData.get('evidenceNonProfitStatus');
+ nonprofitFormData.website = formData.get('website');
+
+ stepperStore.update((prev) => ({ ...prev, scenario: SCENARIOS.NOT_FOUND_IN_SEARCH, step: 3 }));
+ });
+
+ containerTag.replaceChildren(descriptionTag, formTag);
+}
+
+// Organization address
+function renderOrganizationAddress(containerTag) {
+ // Description
+ const descriptionTag = getDescriptionTag(window.mph['nonprofit-title-organization-address']);
+
+ // Form
+ const formTag = createTag('form', { class: 'np-form' });
+
+ const streetAddressTag = getNonprofitInput({
+ type: 'text',
+ name: 'streetAddress',
+ label: window.mph['nonprofit-street-address'],
+ placeholder: window.mph['nonprofit-street-address-placeholder'],
+ required: true,
+ });
+
+ const addressDetailsTag = getNonprofitInput({
+ type: 'text',
+ name: 'addressDetails',
+ label: window.mph['nonprofit-address-details'],
+ placeholder: window.mph['nonprofit-address-details-placeholder'],
+ });
+
+ const stateTag = getNonprofitInput({
+ type: 'text',
+ name: 'state',
+ label: window.mph['nonprofit-state'],
+ placeholder: window.mph['nonprofit-state-placeholder'],
+ });
+
+ const cityTag = getNonprofitInput({
+ type: 'text',
+ name: 'city',
+ label: window.mph['nonprofit-city'],
+ placeholder: window.mph['nonprofit-city-placeholder'],
+ required: true,
+ });
+
+ const zipCodeTag = getNonprofitInput({
+ type: 'text',
+ name: 'zipCode',
+ label: window.mph['nonprofit-zip-code'],
+ placeholder: window.mph['nonprofit-zip-code-placeholder'],
+ required: true,
+ });
+
+ const submitTag = getSubmitTag();
+
+ formTag.append(streetAddressTag, addressDetailsTag, stateTag, cityTag, zipCodeTag, submitTag);
+
+ trackSubmitCondition(formTag);
+
+ formTag.addEventListener('submit', (ev) => {
+ ev.preventDefault();
+
+ const formData = new FormData(formTag);
+ nonprofitFormData.streetAddress = formData.get('streetAddress');
+ nonprofitFormData.addressDetails = formData.get('addressDetails');
+ nonprofitFormData.state = formData.get('state');
+ nonprofitFormData.city = formData.get('city');
+ nonprofitFormData.zipCode = formData.get('zipCode');
+
+ stepperStore.update((prev) => ({ ...prev, scenario: SCENARIOS.NOT_FOUND_IN_SEARCH, step: 4 }));
+ });
+
+ containerTag.replaceChildren(descriptionTag, formTag);
+}
+
+// Personal data
+function renderPersonalData(containerTag) {
+ // Description
+ const descriptionTag = getDescriptionTag(
+ window.mph['nonprofit-title-personal-details'],
+ window.mph['nonprofit-subtitle-personal-details'],
+ );
+
+ // Form
+ const formTag = createTag('form', { class: 'np-form' });
+
+ const firstNameTag = getNonprofitInput({
+ type: 'text',
+ name: 'firstName',
+ label: window.mph['nonprofit-first-name'],
+ placeholder: window.mph['nonprofit-first-name-placeholder'],
+ required: true,
+ });
+
+ const lastNameTag = getNonprofitInput({
+ type: 'text',
+ name: 'lastName',
+ label: window.mph['nonprofit-last-name'],
+ placeholder: window.mph['nonprofit-last-name-placeholder'],
+ required: true,
+ });
+
+ const emailTag = getNonprofitInput({
+ type: 'text',
+ name: 'email',
+ label: window.mph['nonprofit-email'],
+ placeholder: window.mph['nonprofit-email-placeholder'],
+ required: true,
+ });
+
+ const disclaimerTag = createTag(
+ 'span',
+ { class: 'np-personal-data-disclaimer' },
+ window.mph['nonprofit-personal-data-disclaimer'],
+ );
+
+ const submitTag = getSubmitTag();
+
+ formTag.append(firstNameTag, lastNameTag, emailTag, disclaimerTag, submitTag);
+
+ trackSubmitCondition(formTag);
+
+ formTag.addEventListener('submit', async (ev) => {
+ ev.preventDefault();
+
+ const formData = new FormData(formTag);
+ nonprofitFormData.firstName = formData.get('firstName');
+ nonprofitFormData.lastName = formData.get('lastName');
+ nonprofitFormData.email = formData.get('email');
+
+ const inputs = formTag.querySelectorAll('input');
+ inputs.forEach((input) => {
+ input.setAttribute('disabled', 'disabled');
+ });
+
+ stepperStore.update((prev) => ({ ...prev, pending: true }));
+
+ const ok = await sendOrganizationData();
+
+ if (!ok) {
+ inputs.forEach((input) => {
+ input.removeAttribute('disabled');
+ });
+
+ stepperStore.update((prev) => ({ ...prev, pending: false }));
+ } else {
+ stepperStore.update((prev) => ({ ...prev, step: prev.step + 1 }));
+ }
+ });
+
+ containerTag.replaceChildren(descriptionTag, formTag);
+}
+
+function renderApplicationReview(containerTag) {
+ const applicationReviewTag = createTag('div', { class: 'np-application-review-container' });
+
+ const titleTag = createTag(
+ 'span',
+ { class: 'np-title' },
+ window.mph['nonprofit-title-application-review'],
+ );
+ const detail1Tag = createTag(
+ 'span',
+ { class: 'np-application-review-detail' },
+ window.mph['nonprofit-detail-1-application-review'],
+ );
+ const detail2Tag = createTag(
+ 'span',
+ { class: 'np-application-review-detail' },
+ window.mph['nonprofit-detail-2-application-review']?.replace(
+ '__EMAIL__',
+ nonprofitFormData.email,
+ ),
+ );
+
+ applicationReviewTag.append(titleTag, detail1Tag, detail2Tag);
+
+ const returnToAcrobatForNonprofitsTag = createTag(
+ 'button',
+ { class: 'np-button' },
+ window.mph['nonprofit-return-to-acrobat-for-nonprofits'],
+ );
+
+ containerTag.replaceChildren(applicationReviewTag, returnToAcrobatForNonprofitsTag);
+}
+
+function renderStepContent(containerTag) {
+ const contentContainerTag = createTag('div', { class: 'np-content-container' });
+
+ let currentStep;
+ let currentScenario;
+ stepperStore.subscribe(({ step, scenario }) => {
+ if (step === currentStep && scenario === currentScenario) return;
+ currentStep = step;
+ currentScenario = scenario;
+
+ if (step === 1) renderSelectNonprofit(contentContainerTag);
+ if (step === 2 && scenario === SCENARIOS.FOUND_IN_SEARCH) renderPersonalData(contentContainerTag);
+ if (step === 2 && scenario === SCENARIOS.NOT_FOUND_IN_SEARCH) renderOrganizationDetails(contentContainerTag);
+ if (step === 3 && scenario === SCENARIOS.FOUND_IN_SEARCH) renderApplicationReview(contentContainerTag);
+ if (step === 3 && scenario === SCENARIOS.NOT_FOUND_IN_SEARCH) renderOrganizationAddress(contentContainerTag);
+ if (step === 4 && scenario === SCENARIOS.NOT_FOUND_IN_SEARCH) renderPersonalData(contentContainerTag);
+ if (step === 5 && scenario === SCENARIOS.NOT_FOUND_IN_SEARCH) renderApplicationReview(contentContainerTag);
+ });
+
+ containerTag.append(contentContainerTag);
+}
+// #endregion
+
+function initNonprofit(element) {
+ const containerTag = createTag('div', { class: 'np-container' });
+ renderStepper(containerTag);
+ renderStepContent(containerTag);
+ element.append(containerTag);
+}
+
+export default function init(element) {
+ // Get metadata
+ removeOptionElements(element);
+ initNonprofit(element);
+}
diff --git a/creativecloud/blocks/nonprofit/reactiveStore.js b/creativecloud/blocks/nonprofit/reactiveStore.js
new file mode 100644
index 000000000..856dd704d
--- /dev/null
+++ b/creativecloud/blocks/nonprofit/reactiveStore.js
@@ -0,0 +1,50 @@
+export default class ReactiveStore {
+ data = null;
+
+ loaded = false;
+
+ loading = false;
+
+ #initialData = null;
+
+ #subscribers = [];
+
+ constructor(initialData = null) {
+ if (initialData) this.#initialData = initialData;
+ this.data = initialData;
+ }
+
+ subscribe(fn, withTrigger = true) {
+ if (!fn) return;
+ if (!this.#subscribers.includes(fn)) this.#subscribers.push(fn);
+ if (!withTrigger) return;
+ fn(this.data, this.loading);
+ }
+
+ unsubscribe(fn) {
+ const indexOfFn = this.#subscribers.indexOf(fn);
+ if (indexOfFn !== -1) this.#subscribers.splice(indexOfFn, 1);
+ }
+
+ unsubscribeAll() {
+ this.#subscribers = [];
+ }
+
+ update(data) {
+ if (typeof data === 'function') this.data = data(this.data);
+ else this.data = data;
+ this.loaded = true;
+ this.loading = false;
+ this.#subscribers.forEach((subscriber) => {
+ subscriber(this.data, this.loading);
+ });
+ }
+
+ startLoading(resetData = false) {
+ this.loading = true;
+ if (resetData) this.data = this.#initialData;
+ this.#subscribers.forEach((subscriber) => {
+ subscriber(this.data, this.loading);
+ });
+ }
+}
diff --git a/creativecloud/blocks/sidenav/sidenav.js b/creativecloud/blocks/sidenav/sidenav.js
index 368ba273c..d6d8fcdf7 100644
--- a/creativecloud/blocks/sidenav/sidenav.js
+++ b/creativecloud/blocks/sidenav/sidenav.js
@@ -163,7 +163,7 @@ export default async function init(el) {
const libs = getLibs();
const [mainRow, categoryRow] = Array.from(el.children);
const deps = Promise.all([
- import('../../deps/merch-sidenav.js'),
+ import(`${libs}/deps/mas/merch-sidenav.js`),
// eslint-disable-next-line import/no-unresolved, import/no-absolute-path
import(`${libs}/deps/lit-all.min.js`),
import(`${libs}/features/spectrum-web-components/dist/theme.js`),
diff --git a/creativecloud/deps/merch-sidenav.js b/creativecloud/deps/merch-sidenav.js
deleted file mode 100644
index 09057e8ec..000000000
--- a/creativecloud/deps/merch-sidenav.js
+++ /dev/null
@@ -1,141 +0,0 @@
-import{html as k,css as H,LitElement as P}from"/libs/deps/lit-all.min.js";var r=class{constructor(e,t){this.key=Symbol("match-media-key"),this.matches=!1,this.host=e,this.host.addController(this),this.media=window.matchMedia(t),this.matches=this.media.matches,this.onChange=this.onChange.bind(this),e.addController(this)}hostConnected(){var e;(e=this.media)==null||e.addEventListener("change",this.onChange)}hostDisconnected(){var e;(e=this.media)==null||e.removeEventListener("change",this.onChange)}onChange(e){this.matches!==e.matches&&(this.matches=e.matches,this.host.requestUpdate(this.key,!this.matches))}};import{css as L}from"/libs/deps/lit-all.min.js";var c=L`
- h2 {
- font-size: 11px;
- font-style: normal;
- font-weight: 500;
- height: 32px;
- letter-spacing: 0.06em;
- padding: 0 12px;
- line-height: 32px;
- color: #747474;
- }
-`;import{html as N,LitElement as R}from"/libs/deps/lit-all.min.js";function d(s,e){let t;return function(){let o=this,i=arguments;clearTimeout(t),t=setTimeout(()=>s.apply(o,i),e)}}var x="merch-search:change";var v="merch-sidenav:select";var g="hashchange";function n(s=window.location.hash){let e=[],t=s.replace(/^#/,"").split("&");for(let o of t){let[i,l=""]=o.split("=");i&&e.push([i,decodeURIComponent(l.replace(/\+/g," "))])}return Object.fromEntries(e)}function a(s,e){if(s.deeplink){let t={};t[s.deeplink]=e,A(t)}}function A(s){let e=new URLSearchParams(window.location.hash.slice(1));Object.entries(s).forEach(([i,l])=>{l?e.set(i,l):e.delete(i)}),e.sort();let t=e.toString();if(t===window.location.hash)return;let o=window.scrollY||document.documentElement.scrollTop;window.location.hash=t,window.scrollTo(0,o)}function b(s){let e=()=>{if(window.location.hash&&!window.location.hash.includes("="))return;let t=n(window.location.hash);s(t)};return e(),window.addEventListener(g,e),()=>{window.removeEventListener(g,e)}}var p=class extends R{static properties={deeplink:{type:String}};get search(){return this.querySelector("sp-search")}constructor(){super(),this.handleInput=()=>{a(this,this.search.value),this.search.value&&this.dispatchEvent(new CustomEvent(x,{bubbles:!0,composed:!0,detail:{type:"search",value:this.search.value}}))},this.handleInputDebounced=d(this.handleInput.bind(this))}connectedCallback(){super.connectedCallback(),this.search&&(this.search.addEventListener("input",this.handleInputDebounced),this.search.addEventListener("submit",this.handleInputSubmit),this.updateComplete.then(()=>{this.setStateFromURL()}),this.startDeeplink())}disconnectedCallback(){super.disconnectedCallback(),this.search.removeEventListener("input",this.handleInputDebounced),this.search.removeEventListener("submit",this.handleInputSubmit),this.stopDeeplink?.()}setStateFromURL(){let t=n()[this.deeplink];t&&(this.search.value=t)}startDeeplink(){this.stopDeeplink=b(({search:e})=>{this.search.value=e??""})}handleInputSubmit(e){e.preventDefault()}render(){return N`