codevise/pageflow

View on GitHub
entry_types/scrolled/package/src/editor/models/ContentElement.js

Summary

Maintainability
A
40 mins
Test Coverage
import Backbone from 'backbone';
import {editor} from '../api';

import {
  configurationContainer,
  entryTypeEditorControllerUrls,
  failureTracking,
  delayedDestroying
} from 'pageflow/editor';

import {features} from 'pageflow/frontend';
import {ContentElementConfiguration} from './ContentElementConfiguration';

const widths = {
  xxs: -3,
  xs: -2,
  s: -1,
  md: 0,
  l: 1,
  xl: 2,
  full: 3
};

export const ContentElement = Backbone.Model.extend({
  paramRoot: 'content_element',

  mixins: [
    configurationContainer({
      autoSave: true,
      includeAttributesInJSON: ['position', 'typeName'],
      configurationModel: ContentElementConfiguration
    }),
    delayedDestroying,
    entryTypeEditorControllerUrls.forModel({resources: 'content_elements'}),
    failureTracking
  ],

  initialize() {
    this.transientState = new Backbone.Model(this.get('transientState'));

    this.listenTo(this, 'change:transientState', () =>
      this.transientState.set(this.get('transientState'), {skipCommand: true})
    );

    this.listenTo(this.transientState, 'change', (model, {skipCommand}) => {
      if (!skipCommand) {
        this.postCommand({
          type: 'TRANSIENT_STATE_UPDATE',
          payload: model.changed
        });
      }
    });
  },

  getType(contentElement) {
    return editor.contentElementTypes.findByTypeName(this.get('typeName'));
  },

  postCommand(command) {
    this.trigger('postCommand', this.id, command);
  },

  getAdjacentContentElements() {
    const section = this.section;
    const index = section.contentElements.indexOf(this);

    return [
      section.contentElements.at(index - 1),
      section.contentElements.at(index + 1)
    ];
  },

  applyDefaultConfiguration(sibling) {
    const defaultConfig = {...this.getType().defaultConfig};
    const defaultPosition = sibling?.getPosition();
    const supportedPositions = this.getType().supportedPositions || [];

    if (this.configuration.has('position')) {
      delete defaultConfig.position;
    }
    else if (defaultPosition &&
             defaultPosition !== 'inline' &&
             supportedPositions.includes(defaultPosition)) {
      defaultConfig.position = defaultPosition;
    }

    this.configuration.set(defaultConfig);
  },

  getPosition() {
    return this.configuration.get('position');
  },

  getResolvedPosition() {
    const position = this.getPosition();
    return this.getAvailablePositions().includes(position) ? position : 'inline';
  },

  getAvailablePositions() {
    const layout = this.section.configuration.get('layout');
    const backdrop = features.isEnabled('backdrop_content_elements') ? 'backdrop' : null;
    const supportedByLayout =
      layout === 'center' || layout === 'centerRagged' ?
      ['inline', 'left', 'right', 'standAlone', backdrop] :
      ['inline', 'sticky', 'standAlone', backdrop];
    const supportedByType = this.getType().supportedPositions;

    if (supportedByType) {
      return supportedByLayout.filter(position => supportedByType.includes(position));
    }
    else {
      return supportedByLayout;
    }
  },

  getWidth() {
    return this.clampWidthByPosition(this.configuration.get('width') || 0);
  },

  getAvailableMinWidth() {
    return this.clampWidthByPosition(
      widths[this.getType().supportedWidthRange?.[0] || 'md']
    );
  },

  getAvailableMaxWidth() {
    return this.clampWidthByPosition(
      widths[this.getType().supportedWidthRange?.[1] || 'md']
    );
  },

  clampWidthByPosition(width) {
    if (this.getPosition() === 'backdrop') {
      return 0;
    }
    else if (['sticky', 'left', 'right'].includes(this.getResolvedPosition())) {
      return Math.min(Math.max(width, -2), 2);
    }
    else {
      return width;
    }
  },

  getEditorPath() {
    return this.getType().editorPath?.call(null, this) ||
           `/scrolled/content_elements/${this.id}`;
  }
});