diff --git a/nala/studio/translations/translations.page.js b/nala/studio/translations/translations.page.js
index 2e2edd5d4..8312d3636 100644
--- a/nala/studio/translations/translations.page.js
+++ b/nala/studio/translations/translations.page.js
@@ -5,7 +5,7 @@ export default class TranslationsPage {
const translationHost = page.locator('mas-translation');
this.loadingIndicator = translationHost.locator('.loading-container sp-progress-circle');
- this.translationTable = translationHost.locator('sp-table.translation-table');
+ this.translationTable = translationHost.locator('sp-table.item-table');
this.tableHeaders = {
translationProject: translationHost.locator('sp-table-head-cell:has-text("Translation Project")'),
lastUpdatedBy: translationHost.locator('sp-table-head-cell:has-text("Last updated by")'),
@@ -14,9 +14,9 @@ export default class TranslationsPage {
};
this.emptyState = translationHost.locator('.translation-empty-state');
- this.tableRows = translationHost.locator('sp-table.translation-table sp-table-row');
+ this.tableRows = translationHost.locator('sp-table.item-table sp-table-row');
- this.firstRow = translationHost.locator('sp-table.translation-table sp-table-row').first();
+ this.firstRow = translationHost.locator('sp-table.item-table sp-table-row').first();
this.firstRowTitleCell = this.firstRow.locator('sp-table-cell').nth(0);
this.firstRowActionMenu = this.firstRow.locator('sp-action-menu');
diff --git a/studio/src/translation/mas-items-selector.css.js b/studio/src/common/components/mas-items-selector.css.js
similarity index 97%
rename from studio/src/translation/mas-items-selector.css.js
rename to studio/src/common/components/mas-items-selector.css.js
index ed86c5795..c288828e7 100644
--- a/studio/src/translation/mas-items-selector.css.js
+++ b/studio/src/common/components/mas-items-selector.css.js
@@ -1,5 +1,5 @@
import { css } from 'lit';
-import { ghostButtonStyles } from './translation-common-styles.css.js';
+import { ghostButtonStyles } from '../styles/table-styles.css.js';
export const styles = [
ghostButtonStyles,
diff --git a/studio/src/translation/mas-items-selector.js b/studio/src/common/components/mas-items-selector.js
similarity index 77%
rename from studio/src/translation/mas-items-selector.js
rename to studio/src/common/components/mas-items-selector.js
index 38ecd89a3..ed6ca9185 100644
--- a/studio/src/translation/mas-items-selector.js
+++ b/studio/src/common/components/mas-items-selector.js
@@ -1,14 +1,14 @@
import { LitElement, html, nothing } from 'lit';
import { repeat } from 'lit/directives/repeat.js';
-import Store from '../store.js';
-import ReactiveController from '../reactivity/reactive-controller.js';
-import { TABLE_TYPE } from '../constants.js';
-import { toggleSidebarIcon } from '../icons.js';
+import ReactiveController from '../../reactivity/reactive-controller.js';
+import { getItemsSelectionStore } from '../items-selection-store.js';
+import { TABLE_TYPE } from '../../constants.js';
+import { toggleSidebarIcon } from '../../icons.js';
import './mas-select-items-table.js';
import './mas-selected-items.js';
import './mas-search-and-filters.js';
import { styles } from './mas-items-selector.css.js';
-import { debounce } from '../utils.js';
+import { debounce } from '../../utils.js';
export const TABS = [
{ value: TABLE_TYPE.CARDS, label: 'Fragments' },
@@ -23,6 +23,9 @@ class MasItemsSelector extends LitElement {
viewOnly: { type: Boolean, state: true },
searchQuery: { type: String, state: true },
selectedTab: { type: String, state: true },
+ /** @type {(fragmentData: object) => string} */
+ getDisplayName: { type: Function },
+ renderFragmentStatusCell: { type: Function },
};
constructor() {
@@ -30,33 +33,33 @@ class MasItemsSelector extends LitElement {
this.viewOnly = false;
this.searchQuery = '';
this.selectedTab = TABLE_TYPE.CARDS;
+ this.getDisplayName = (fragmentData) => fragmentData?.path ?? '';
+ this.renderFragmentStatusCell = () => nothing;
}
connectedCallback() {
super.connectedCallback();
+ const s = getItemsSelectionStore();
this.storeController = new ReactiveController(this, [
- Store.translationProjects.inEdit,
- Store.translationProjects.showSelected,
- Store.translationProjects.selectedCards,
- Store.translationProjects.selectedCollections,
- Store.translationProjects.selectedPlaceholders,
+ s.inEdit,
+ s.showSelected,
+ s.selectedCards,
+ s.selectedCollections,
+ s.selectedPlaceholders,
]);
}
get showSelected() {
- return Store.translationProjects.showSelected.value;
+ return getItemsSelectionStore().showSelected.value;
}
get selectedCount() {
- return (
- Store.translationProjects.selectedCards.value.length +
- Store.translationProjects.selectedPlaceholders.value.length +
- Store.translationProjects.selectedCollections.value.length
- );
+ const s = getItemsSelectionStore();
+ return [...s.selectedCards.value, ...s.selectedPlaceholders.value, ...s.selectedCollections.value].length;
}
#toggleShowSelected() {
- Store.translationProjects.showSelected.set(!this.showSelected);
+ getItemsSelectionStore().showSelected.set(!this.showSelected);
}
#setSearchQuery = debounce((value) => {
@@ -79,7 +82,7 @@ class MasItemsSelector extends LitElement {
#getTabLabel(tab) {
if (this.viewOnly) {
const valueUppercase = tab.value.charAt(0).toUpperCase() + tab.value.slice(1);
- return `${tab.label} (${Store.translationProjects[`selected${valueUppercase}`].value.length})`;
+ return `${tab.label} (${getItemsSelectionStore()[`selected${valueUppercase}`].value.length})`;
}
return tab.label;
}
@@ -135,9 +138,13 @@ class MasItemsSelector extends LitElement {
- ${this.viewOnly ? nothing : html``}
+ ${this.viewOnly
+ ? nothing
+ : html``}
event.stopPropagation()}>
diff --git a/studio/src/translation/mas-search-and-filters.css.js b/studio/src/common/components/mas-search-and-filters.css.js
similarity index 100%
rename from studio/src/translation/mas-search-and-filters.css.js
rename to studio/src/common/components/mas-search-and-filters.css.js
diff --git a/studio/src/translation/mas-search-and-filters.js b/studio/src/common/components/mas-search-and-filters.js
similarity index 91%
rename from studio/src/translation/mas-search-and-filters.js
rename to studio/src/common/components/mas-search-and-filters.js
index b330dc524..f30072b99 100644
--- a/studio/src/translation/mas-search-and-filters.js
+++ b/studio/src/common/components/mas-search-and-filters.js
@@ -1,10 +1,11 @@
import { LitElement, html, nothing } from 'lit';
import { repeat } from 'lit/directives/repeat.js';
-import { VARIANTS } from '../editors/variant-picker.js';
+import { VARIANTS } from '../../editors/variant-picker.js';
import { styles } from './mas-search-and-filters.css.js';
-import Store from '../store.js';
-import { FILTER_TYPE, TABLE_TYPE } from '../constants.js';
-import ReactiveController from '../reactivity/reactive-controller.js';
+import Store from '../../store.js';
+import { getItemsSelectionStore } from '../items-selection-store.js';
+import { FILTER_TYPE, TABLE_TYPE } from '../../constants.js';
+import ReactiveController from '../../reactivity/reactive-controller.js';
class MasSearchAndFilters extends LitElement {
static styles = styles;
@@ -40,8 +41,8 @@ class MasSearchAndFilters extends LitElement {
connectedCallback() {
super.connectedCallback();
this.commonDataController = new ReactiveController(this, [
- Store.translationProjects[`all${this.typeUppercased}`],
- Store.translationProjects[`display${this.typeUppercased}`],
+ getItemsSelectionStore()[`all${this.typeUppercased}`],
+ getItemsSelectionStore()[`display${this.typeUppercased}`],
Store[this.type === TABLE_TYPE.PLACEHOLDERS ? 'placeholders' : 'fragments'].list.loading,
]);
const dataCallback = () => {
@@ -51,16 +52,16 @@ class MasSearchAndFilters extends LitElement {
this.#applyFilters();
this.requestUpdate();
};
- Store.translationProjects[`all${this.typeUppercased}`].subscribe(dataCallback);
+ getItemsSelectionStore()[`all${this.typeUppercased}`].subscribe(dataCallback);
this.dataSubscription = {
- unsubscribe: () => Store.translationProjects[`all${this.typeUppercased}`].unsubscribe(dataCallback),
+ unsubscribe: () => getItemsSelectionStore()[`all${this.typeUppercased}`].unsubscribe(dataCallback),
};
}
disconnectedCallback() {
super.disconnectedCallback();
- Store.translationProjects[`display${this.typeUppercased}`].set(
- Store.translationProjects[`all${this.typeUppercased}`].value,
+ getItemsSelectionStore()[`display${this.typeUppercased}`].set(
+ getItemsSelectionStore()[`all${this.typeUppercased}`].value,
);
this.dataSubscription?.unsubscribe();
}
@@ -105,7 +106,7 @@ class MasSearchAndFilters extends LitElement {
const marketSegments = new Map();
const customerSegments = new Map();
const products = new Map();
- for (const fragment of Store.translationProjects[`all${this.typeUppercased}`].value) {
+ for (const fragment of getItemsSelectionStore()[`all${this.typeUppercased}`].value) {
if (!fragment.tags) continue;
for (const tag of fragment.tags) {
@@ -271,7 +272,7 @@ class MasSearchAndFilters extends LitElement {
}
#applyFilters() {
- const source = Store.translationProjects[`all${this.typeUppercased}`].value || [];
+ const source = getItemsSelectionStore()[`all${this.typeUppercased}`].value || [];
const query = this.searchQuery?.toLowerCase();
const hasTemplate = this.templateFilter?.length > 0;
const hasMarket = this.marketSegmentFilter?.length > 0;
@@ -319,15 +320,15 @@ class MasSearchAndFilters extends LitElement {
if (this.type === TABLE_TYPE.CARDS) {
result.sort((a, b) => (b.groupedVariations?.length > 0 ? 1 : 0) - (a.groupedVariations?.length > 0 ? 1 : 0));
}
- Store.translationProjects[`display${this.typeUppercased}`].set(result);
+ getItemsSelectionStore()[`display${this.typeUppercased}`].set(result);
}
renderCount() {
return html`
${this.isLoading
? html``
- : html`${Store.translationProjects[`display${this.typeUppercased}`].value.length}
- result${Store.translationProjects[`display${this.typeUppercased}`].value.length !== 1 ? 's' : ''}`}
+ : html`${getItemsSelectionStore()[`display${this.typeUppercased}`].value.length}
+ result${getItemsSelectionStore()[`display${this.typeUppercased}`].value.length !== 1 ? 's' : ''}`}
`;
}
diff --git a/studio/src/translation/mas-select-items-table.css.js b/studio/src/common/components/mas-select-items-table.css.js
similarity index 98%
rename from studio/src/translation/mas-select-items-table.css.js
rename to studio/src/common/components/mas-select-items-table.css.js
index 37778408c..9a36fc6b6 100644
--- a/studio/src/translation/mas-select-items-table.css.js
+++ b/studio/src/common/components/mas-select-items-table.css.js
@@ -5,7 +5,7 @@ import {
tableColumnIconStyles,
tableSelectedRowStyles,
loadingContainerFlexStyles,
-} from './translation-common-styles.css.js';
+} from '../styles/table-styles.css.js';
export const styles = [
tableHeaderBaseStyles,
diff --git a/studio/src/translation/mas-select-items-table.js b/studio/src/common/components/mas-select-items-table.js
similarity index 83%
rename from studio/src/translation/mas-select-items-table.js
rename to studio/src/common/components/mas-select-items-table.js
index d377e6797..d56596850 100644
--- a/studio/src/translation/mas-select-items-table.js
+++ b/studio/src/common/components/mas-select-items-table.js
@@ -1,18 +1,17 @@
import { LitElement, html, nothing } from 'lit';
import { repeat } from 'lit/directives/repeat.js';
import { styles } from './mas-select-items-table.css.js';
-import Store from '../store.js';
-import StoreController from '../reactivity/store-controller.js';
-import { TABLE_TYPE } from '../constants.js';
-import { renderFragmentStatusCell } from './translation-utils.js';
-import ReactiveController from '../reactivity/reactive-controller.js';
-import { MasCollapsibleTableRow } from './mas-collapsible-table-row.js';
+import Store from '../../store.js';
+import { getItemsSelectionStore } from '../items-selection-store.js';
+import StoreController from '../../reactivity/store-controller.js';
+import { TABLE_TYPE } from '../../constants.js';
+import ReactiveController from '../../reactivity/reactive-controller.js';
import {
loadAllPlaceholders,
loadAllFragments,
loadSelectedPlaceholders,
loadSelectedFragments,
-} from './translation-items-loader.js';
+} from '../utils/items-loader.js';
class MasSelectItemsTable extends LitElement {
static styles = styles;
@@ -22,6 +21,8 @@ class MasSelectItemsTable extends LitElement {
viewOnly: { type: Boolean },
viewOnlyLoading: { type: Boolean, state: true },
viewOnlyFragments: { type: Array, state: true },
+ getDisplayName: { type: Function },
+ renderFragmentStatusCell: { type: Function },
};
hasMore = new StoreController(this, Store.fragments.list.hasMore);
@@ -43,6 +44,8 @@ class MasSelectItemsTable extends LitElement {
this.selectedPlaceholdersStoreController = null;
this.observedSentinel = null;
this.wasLoading = false;
+ this.getDisplayName = (fragmentData) => fragmentData?.path ?? '';
+ this.renderFragmentStatusCell = () => nothing;
}
connectedCallback() {
@@ -52,9 +55,9 @@ class MasSelectItemsTable extends LitElement {
this.dataState.pendingCards = null;
if (this.viewOnly) {
if (this.type === TABLE_TYPE.PLACEHOLDERS) {
- this.viewOnlyLoading = !!Store.translationProjects.selectedPlaceholders.value?.length;
+ this.viewOnlyLoading = !!getItemsSelectionStore().selectedPlaceholders.value?.length;
this.dataSubscription = loadSelectedPlaceholders(
- Store.translationProjects.selectedPlaceholders.value,
+ getItemsSelectionStore().selectedPlaceholders.value,
(items) => {
this.viewOnlyFragments = items;
if (!Store.placeholders.list.loading.get()) {
@@ -63,10 +66,10 @@ class MasSelectItemsTable extends LitElement {
},
);
} else {
- this.viewOnlyLoading = !!Store.translationProjects[`selected${this.typeUppercased}`].value?.length;
+ this.viewOnlyLoading = !!getItemsSelectionStore()[`selected${this.typeUppercased}`].value?.length;
this.processAbortController = new AbortController();
loadSelectedFragments(
- Store.translationProjects[`selected${this.typeUppercased}`].value,
+ getItemsSelectionStore()[`selected${this.typeUppercased}`].value,
this.type,
this.repository,
{
@@ -74,6 +77,7 @@ class MasSelectItemsTable extends LitElement {
onItems: (items) => {
this.viewOnlyFragments = items;
},
+ getDisplayName: this.getDisplayName,
},
).finally(() => {
this.viewOnlyLoading = false;
@@ -83,16 +87,18 @@ class MasSelectItemsTable extends LitElement {
if (this.type === TABLE_TYPE.PLACEHOLDERS) {
this.dataSubscription = loadAllPlaceholders();
} else {
- this.dataSubscription = loadAllFragments(this.type, this.repository, this.dataState);
+ this.dataSubscription = loadAllFragments(this.type, this.repository, this.dataState, {
+ getDisplayName: this.getDisplayName,
+ });
}
}
this[`selected${this.typeUppercased}StoreController`] = new ReactiveController(this, [
Store.fragments.list.loading,
Store.placeholders.list.loading,
- Store.translationProjects[`selected${this.typeUppercased}`],
+ getItemsSelectionStore()[`selected${this.typeUppercased}`],
]);
this[`display${this.typeUppercased}StoreController`] = new ReactiveController(this, [
- Store.translationProjects[`display${this.typeUppercased}`],
+ getItemsSelectionStore()[`display${this.typeUppercased}`],
]);
}
@@ -169,19 +175,19 @@ class MasSelectItemsTable extends LitElement {
if (this.viewOnly) {
return this.viewOnlyFragments;
}
- return Store.translationProjects[`display${this.typeUppercased}`].value;
+ return getItemsSelectionStore()[`display${this.typeUppercased}`].value;
}
get selectedInTable() {
- return new Set(Store.translationProjects[`selected${this.typeUppercased}`].value);
+ return new Set(getItemsSelectionStore()[`selected${this.typeUppercased}`].value);
}
get tableColumns() {
const TABLE_COLUMNS = {
cards: {
selectable: [
- { label: '', key: 'chevron', class: 'translation-table-icon-cell translation-table-icon-cell--chevron' },
- { label: '', key: 'checkbox', class: 'translation-table-icon-cell translation-table-icon-cell--checkbox' },
+ { label: '', key: 'chevron', class: 'table-icon-cell table-icon-cell--chevron' },
+ { label: '', key: 'checkbox', class: 'table-icon-cell table-icon-cell--checkbox' },
{ label: 'Offer', key: 'offer', sortable: true },
{ label: 'Fragment title', key: 'fragmentTitle' },
{ label: 'Offer ID', key: 'offerId' },
@@ -189,7 +195,7 @@ class MasSelectItemsTable extends LitElement {
{ label: 'Status', key: 'status' },
],
viewOnly: [
- { label: '', key: 'chevron', class: 'translation-table-icon-cell translation-table-icon-cell--chevron' },
+ { label: '', key: 'chevron', class: 'table-icon-cell table-icon-cell--chevron' },
{ label: 'Offer', key: 'offer', sortable: true },
{ label: 'Fragment title', key: 'fragmentTitle' },
{ label: 'Offer ID', key: 'offerId' },
@@ -200,7 +206,7 @@ class MasSelectItemsTable extends LitElement {
},
collections: {
selectable: [
- { label: '', key: 'checkbox', class: 'translation-table-icon-cell translation-table-icon-cell--checkbox' },
+ { label: '', key: 'checkbox', class: 'table-icon-cell table-icon-cell--checkbox' },
{ label: 'Collection title', key: 'collectionTitle' },
{ label: 'Path', key: 'path' },
{ label: 'Status', key: 'status' },
@@ -213,7 +219,7 @@ class MasSelectItemsTable extends LitElement {
},
placeholders: {
selectable: [
- { label: '', key: 'checkbox', class: 'translation-table-icon-cell translation-table-icon-cell--checkbox' },
+ { label: '', key: 'checkbox', class: 'table-icon-cell table-icon-cell--checkbox' },
{ label: 'Key', key: 'key' },
{ label: 'Value', key: 'value' },
{ label: 'Status', key: 'status' },
@@ -233,7 +239,7 @@ class MasSelectItemsTable extends LitElement {
const newSelected = this.selectedInTable.has(path)
? [...this.selectedInTable].filter((p) => p !== path)
: [...this.selectedInTable, path];
- Store.translationProjects[`selected${this.typeUppercased}`].set(newSelected);
+ getItemsSelectionStore()[`selected${this.typeUppercased}`].set(newSelected);
}
#renderTableBody() {
@@ -246,6 +252,8 @@ class MasSelectItemsTable extends LitElement {
html``,
)}`;
case TABLE_TYPE.COLLECTIONS:
@@ -260,7 +268,7 @@ class MasSelectItemsTable extends LitElement {
>
${!this.viewOnly
? html`
-
+
${fragment.title || '-'}
${fragment.studioPath}
- ${renderFragmentStatusCell(fragment.status)}
+ ${this.renderFragmentStatusCell(fragment.status)}
`,
)}`;
case TABLE_TYPE.PLACEHOLDERS:
@@ -285,7 +293,7 @@ class MasSelectItemsTable extends LitElement {
aria-selected=${!this.viewOnly && this.selectedInTable.has(fragment.path) ? 'true' : 'false'}
>
${!this.viewOnly
- ? html`
+ ? html`
${fragment.value?.length > 100 ? `${fragment.value.slice(0, 100)}...` : fragment.value || '-'}
- ${renderFragmentStatusCell(fragment.status)}
+ ${this.renderFragmentStatusCell(fragment.status)}
`,
)}`;
@@ -321,7 +329,7 @@ class MasSelectItemsTable extends LitElement {
`
: html`${this.itemsToDisplay.length > 0
- ? html`
+ ? html`
${repeat(
this.tableColumns,
diff --git a/studio/src/translation/mas-selected-items.css.js b/studio/src/common/components/mas-selected-items.css.js
similarity index 95%
rename from studio/src/translation/mas-selected-items.css.js
rename to studio/src/common/components/mas-selected-items.css.js
index 2fb70e7f8..2b02b4fec 100644
--- a/studio/src/translation/mas-selected-items.css.js
+++ b/studio/src/common/components/mas-selected-items.css.js
@@ -1,5 +1,5 @@
import { css } from 'lit';
-import { ghostButtonStyles } from './translation-common-styles.css.js';
+import { ghostButtonStyles } from '../styles/table-styles.css.js';
export const styles = [
ghostButtonStyles,
diff --git a/studio/src/translation/mas-selected-items.js b/studio/src/common/components/mas-selected-items.js
similarity index 65%
rename from studio/src/translation/mas-selected-items.js
rename to studio/src/common/components/mas-selected-items.js
index b745d67b7..8894c609d 100644
--- a/studio/src/translation/mas-selected-items.js
+++ b/studio/src/common/components/mas-selected-items.js
@@ -1,31 +1,36 @@
import { LitElement, html, nothing } from 'lit';
import { repeat } from 'lit/directives/repeat.js';
import { styles } from './mas-selected-items.css.js';
-import Store from '../store.js';
-import ReactiveController from '../reactivity/reactive-controller.js';
-import { Fragment } from '../aem/fragment.js';
-import { CARD_MODEL_PATH, COLLECTION_MODEL_PATH } from '../constants.js';
-import { fetchUnresolvedVariations } from './translation-items-loader.js';
+import Store from '../../store.js';
+import { getItemsSelectionStore } from '../items-selection-store.js';
+import ReactiveController from '../../reactivity/reactive-controller.js';
+import { CARD_MODEL_PATH, COLLECTION_MODEL_PATH } from '../../constants.js';
+import { getItemTypeLabel } from '../utils/render-utils.js';
+import { fetchUnresolvedVariations } from '../utils/items-loader.js';
class MasSelectedItems extends LitElement {
static styles = styles;
+ static properties = {
+ getDisplayName: { type: Function },
+ };
#lastFetchedSelectedCardsKey = null;
constructor() {
super();
+ this.getDisplayName = (fragmentData) => fragmentData?.path ?? '';
this.storeController = new ReactiveController(this, [
- Store.translationProjects.showSelected,
- Store.translationProjects.selectedCards,
- Store.translationProjects.selectedCollections,
- Store.translationProjects.selectedPlaceholders,
- Store.translationProjects.groupedVariationsByParent,
+ getItemsSelectionStore().showSelected,
+ getItemsSelectionStore().selectedCards,
+ getItemsSelectionStore().selectedCollections,
+ getItemsSelectionStore().selectedPlaceholders,
+ getItemsSelectionStore().groupedVariationsByParent,
Store.fragments.list.loading,
Store.placeholders.list.loading,
]);
this.fetchController = new ReactiveController(
this,
- [Store.translationProjects.showSelected, Store.translationProjects.selectedCards],
+ [getItemsSelectionStore().showSelected, getItemsSelectionStore().selectedCards],
this.maybeFetchUnresolvedVariations.bind(this),
);
}
@@ -35,16 +40,17 @@ class MasSelectedItems extends LitElement {
maybeFetchUnresolvedVariations() {
if (!this.showSelected || !this.repository) return;
- const selectedCards = Store.translationProjects.selectedCards.value || [];
+ const selectedCards = getItemsSelectionStore().selectedCards.value || [];
const selectedCardsKey = [...selectedCards].sort().join('\0');
if (selectedCardsKey === this.#lastFetchedSelectedCardsKey) return;
this.#lastFetchedSelectedCardsKey = selectedCardsKey;
fetchUnresolvedVariations(
selectedCards,
- Store.translationProjects.cardsByPaths.value,
- Store.translationProjects.groupedVariationsByParent.value,
+ getItemsSelectionStore().cardsByPaths.value,
+ getItemsSelectionStore().groupedVariationsByParent.value,
this.repository,
+ { getDisplayName: this.getDisplayName },
);
}
@@ -54,28 +60,28 @@ class MasSelectedItems extends LitElement {
}
get selectedItems() {
- const cards = Store.translationProjects.selectedCards.value
- ?.map(
+ const cards = getItemsSelectionStore()
+ .selectedCards.value?.map(
(path) =>
- Store.translationProjects.cardsByPaths.value?.get(path) ??
- Store.translationProjects.groupedVariationsData.value?.get(path),
+ getItemsSelectionStore().cardsByPaths.value?.get(path) ??
+ getItemsSelectionStore().groupedVariationsData.value?.get(path),
)
.filter(Boolean);
- const collections = Store.translationProjects.selectedCollections.value
- ?.map((path) => {
- return Store.translationProjects.collectionsByPaths.value.get(path);
+ const collections = getItemsSelectionStore()
+ .selectedCollections.value?.map((path) => {
+ return getItemsSelectionStore().collectionsByPaths.value.get(path);
})
.filter(Boolean);
- const placeholders = Store.translationProjects.selectedPlaceholders.value
- ?.map((path) => {
- return Store.translationProjects.placeholdersByPaths.value.get(path);
+ const placeholders = getItemsSelectionStore()
+ .selectedPlaceholders.value?.map((path) => {
+ return getItemsSelectionStore().placeholdersByPaths.value.get(path);
})
.filter(Boolean);
return [...cards, ...collections, ...placeholders];
}
get showSelected() {
- return Store.translationProjects.showSelected.value;
+ return getItemsSelectionStore().showSelected.value;
}
get isLoadingItems() {
@@ -83,15 +89,7 @@ class MasSelectedItems extends LitElement {
}
getType(item) {
- if (!item) return 'Unknown type';
- switch (item.model.path) {
- case CARD_MODEL_PATH:
- return Fragment.isGroupedVariationPath(item.path) ? 'Grouped variation' : 'Default card';
- case COLLECTION_MODEL_PATH:
- return 'Collection';
- default:
- return 'Placeholder';
- }
+ return getItemTypeLabel(item);
}
getTitle(item) {
@@ -120,8 +118,8 @@ class MasSelectedItems extends LitElement {
type = 'Placeholders';
break;
}
- Store.translationProjects[`selected${type}`].set(
- Store.translationProjects[`selected${type}`].value?.filter((selectedPath) => selectedPath !== item.path),
+ getItemsSelectionStore()[`selected${type}`].set(
+ getItemsSelectionStore()[`selected${type}`].value?.filter((selectedPath) => selectedPath !== item.path),
);
}
diff --git a/studio/src/common/items-selection-store.js b/studio/src/common/items-selection-store.js
new file mode 100644
index 000000000..8a28ee66e
--- /dev/null
+++ b/studio/src/common/items-selection-store.js
@@ -0,0 +1,22 @@
+let activeItemsSelectionStore = null;
+
+/**
+ * @param {{ allowUnset?: boolean }} [options] If "allowUnset" is true, returns null when no slice is bound instead of throwing.
+ * @returns {object|null}
+ */
+export function getItemsSelectionStore(options) {
+ if (activeItemsSelectionStore == null) {
+ if (options?.allowUnset) {
+ return null;
+ }
+ throw new Error('Items selection store not set.');
+ }
+ return activeItemsSelectionStore;
+}
+
+/**
+ * @param {object|null} slice
+ */
+export function setItemsSelectionStore(slice) {
+ activeItemsSelectionStore = slice;
+}
diff --git a/studio/src/common/styles/table-styles.css.js b/studio/src/common/styles/table-styles.css.js
new file mode 100644
index 000000000..3dd655b15
--- /dev/null
+++ b/studio/src/common/styles/table-styles.css.js
@@ -0,0 +1,152 @@
+import { css } from 'lit';
+
+export const ghostButtonStyles = css`
+ .ghost-button {
+ --mod-button-background-color-default: transparent;
+ --mod-button-background-color-hover: var(--spectrum-gray-200);
+ }
+`;
+
+export const loadingContainerFlexStyles = css`
+ .loading-container--flex {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ }
+`;
+
+export const tableHeaderBaseStyles = css`
+ .item-table {
+ --mod-table-header-background-color: var(--spectrum-gray-50);
+ --mod-table-border-radius: 0;
+ }
+
+ .item-table sp-table-head {
+ border-top: 1px solid var(--spectrum-gray-300);
+ border-left: 1px solid var(--spectrum-gray-300);
+ border-right: 1px solid var(--spectrum-gray-300);
+ border-radius: 12px 12px 0 0;
+ }
+
+ .item-table sp-table-head-cell {
+ align-content: center;
+ }
+
+ .item-table sp-table-head-cell:first-of-type {
+ border-top-left-radius: 12px;
+ }
+
+ .item-table sp-table-head-cell:last-of-type {
+ border-top-right-radius: 12px;
+ }
+`;
+
+export const tableColumnIconStyles = css`
+ .table-icon-cell {
+ display: flex;
+ align-items: center;
+ flex: 0;
+ }
+
+ .table-icon-cell--chevron {
+ padding: 29px;
+ }
+
+ .table-icon-cell--checkbox {
+ padding: 22px;
+ }
+`;
+
+export const tableCellBaseStyles = css`
+ .item-table sp-table-cell,
+ sp-table-cell {
+ display: flex;
+ align-items: center;
+ }
+
+ .status-cell {
+ display: flex;
+ align-items: center;
+ gap: 6px;
+
+ .status-dot {
+ width: 8px;
+ height: 8px;
+ border-radius: 50%;
+ background-color: var(--spectrum-gray-500);
+ }
+
+ .status-dot.green {
+ background-color: var(--spectrum-green-700);
+ }
+
+ .status-dot.blue {
+ background-color: var(--spectrum-blue-800);
+ }
+ }
+`;
+
+export const tableSelectedRowStyles = css`
+ sp-table-row[selected] {
+ --mod-table-row-background-color: var(--spectrum-blue-200);
+ --spectrum-table-cell-background-color: var(--spectrum-blue-200);
+ }
+`;
+
+export const selectItemsFormSectionStyles = css`
+ .select-items {
+ sp-button {
+ --mod-button-background-color-default: transparent;
+ --mod-button-background-color-hover: var(--spectrum-gray-200);
+ }
+
+ sp-icon-add {
+ width: 48px;
+ height: 48px;
+ }
+
+ .label {
+ align-content: center;
+ }
+ }
+
+ .items-empty-state {
+ display: flex;
+ flex-direction: row;
+ gap: 12px;
+ padding: 12px 24px;
+ border: 1px dashed var(--spectrum-gray-800);
+ border-radius: 10px;
+ }
+
+ .selected-items {
+ display: flex;
+ flex-direction: column;
+ gap: 20px;
+
+ .selected-items-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+
+ h2 {
+ margin: 0;
+
+ span {
+ font-weight: 500;
+ }
+ }
+
+ .toggle-btn {
+ --mod-button-background-color-down: var(--spectrum-gray-300);
+ --mod-button-content-color-default: var(--spectrum-gray-800);
+ --mod-button-content-color-hover: var(--spectrum-gray-900);
+ }
+ }
+
+ h2 sp-icon-asterisk100 {
+ width: 10px;
+ height: 10px;
+ }
+ }
+`;
diff --git a/studio/src/common/utils/item-loading-browser.js b/studio/src/common/utils/item-loading-browser.js
new file mode 100644
index 000000000..4bdc23053
--- /dev/null
+++ b/studio/src/common/utils/item-loading-browser.js
@@ -0,0 +1,54 @@
+import { getService } from '../../utils.js';
+
+/**
+ * Loads offer data for a fragment using its OSI field.
+ * @param {Object} fragment
+ * @param {Object} options
+ * @param {Map} [options.cache]
+ * @param {AbortSignal} [options.signal]
+ * @param {number} [options.timeoutMs]
+ * @returns {Promise