Commit 8ef9ed9f authored by Дмитрий Малюгин's avatar Дмитрий Малюгин 🕓
Browse files

image settings in progress

parent 2dd967a9
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -7,6 +7,9 @@ export {}
/* prettier-ignore */
declare module 'vue' {
  export interface GlobalComponents {
    AlignCenterIcon: typeof import('./../shared/icons/AlignCenterIcon.vue')['default']
    AlignLeftIcon: typeof import('./../shared/icons/AlignLeftIcon.vue')['default']
    AlignRightIcon: typeof import('./../shared/icons/AlignRightIcon.vue')['default']
    App: typeof import('./App.vue')['default']
    AuthorizationForm: typeof import('./../modules/authorization/AuthorizationForm.vue')['default']
    BaseLoader: typeof import('./../shared/BaseLoader.vue')['default']
@@ -35,6 +38,7 @@ declare module 'vue' {
    ImageMenu: typeof import('./../components/entities/settings/ImageMenu.vue')['default']
    ImagePositionMenu: typeof import('./../components/entities/image/ImagePositionMenu.vue')['default']
    ImageSettings: typeof import('./../components/entities/settings/ImageSettings.vue')['default']
    ImageSettingsList: typeof import('./../components/entities/settings/lists/ImageSettingsList.vue')['default']
    ImageSizeMenu: typeof import('./../components/entities/image/ImageSizeMenu.vue')['default']
    ImageStateMenu: typeof import('./../components/entities/image/ImageStateMenu.vue')['default']
    LogoAndLabel: typeof import('./../components/LogoAndLabel.vue')['default']
@@ -68,6 +72,7 @@ declare module 'vue' {
    TextPositionMenu: typeof import('./../components/entities/text/TextPositionMenu.vue')['default']
    TextSettings: typeof import('./../components/entities/settings/TextSettings.vue')['default']
    TextStateMenu: typeof import('./../components/entities/text/TextStateMenu.vue')['default']
    ToggleButton: typeof import('./../shared/ui/ToggleButton.vue')['default']
    ToggleSwitch: typeof import('./../shared/ui/ToggleSwitch.vue')['default']
    TrashIcon: typeof import('./../shared/icons/TrashIcon.vue')['default']
    Tree: typeof import('./../shared/ui/Tree.vue')['default']
+2 −1
Original line number Diff line number Diff line
@@ -54,11 +54,12 @@ export const editEntity = (newState: IEntity) => {
export const deleteEntity = (entityUuid: string) => {
  const dataStore = useDataStore();
  const websocketStore = useWebsocketStore();
  const page_uuid = cookies.get('current_page_uuid');
  const entities = dataStore.entities;
  const entityToDelete = entities.find((entity) => entity.entity_uuid === entityUuid);
  const data = {
    event: 'deleteEntity',
    body: { ...entityToDelete }
    body: { ...entityToDelete, page_uuid }
  };
  websocketStore.sendData(data);
};
+15 −0
Original line number Diff line number Diff line
import type { TTheme } from '@/app/interfaces/environment';

export interface IToggleButtonItem {
  label: string;
  textColor?: TTheme;
  backgroundColor?: TTheme;
  isLabelHidden?: boolean;
  iconPos?: string;
  textStyle?: 'bold' | 'italic';
}
export interface ISliderOption {
  label: string;
  value: number;
  color?: string;
}
+254 −64
Original line number Diff line number Diff line
<script setup lang="ts">
import { useVModel } from '@vueuse/core';
import { useDataStore } from '@/app/stores/data';
import type { IImage } from '@/app/interfaces/entities';
import { deleteEntity, editEntity } from '@/app/helpers';
import { convertThemeToColorWhiteDefault, deleteEntity, editEntity } from '@/app/helpers';
import type { TTheme } from '@/app/interfaces/environment';
import cookies from '@/app/plugins/Cookie';
import type { IToggleButtonItem } from '@/app/interfaces/ui';

interface Props {
  entityData: IImage;
}
const props = defineProps<Props>();
const emit = defineEmits(['update:entityData', 'openCropImageModal']);
const entityData = useVModel(props, 'entityData', emit);
const emit = defineEmits(['saveChanges']);
const entityData = computed(() => props.entityData);
const newEntityData = ref({ ...entityData.value });
watch(entityData, () => (newEntityData.value = entityData.value));
const isModal = ref<boolean>(false);

const dataStore = useDataStore();
const entities = computed(() => dataStore.entities);

const addTitle = () => {
  editEntity({
    ...entityData.value,
    title: 'Title',
    entity_title_position: 'center',
    font_size: entityData.value.font_size ?? '24'
  });
  entityData.value = { ...entityData.value, title: 'Title', entity_title_position: 'center' };
};
const removeTitle = () => {
  const newState = { ...entityData.value };
  newState.title = null;
  editEntity({ ...newState });
  entityData.value = newState;
};
const addText = () => {
  editEntity({
    ...entityData.value,
@@ -37,7 +23,7 @@ const addText = () => {
    font_size: entityData.value.font_size ?? '24',
    paragraph_size: 'full'
  });
  entityData.value = { ...entityData.value, text: 'Text' };
  newEntityData.value = { ...entityData.value, text: 'Text' };
};
const removeText = () => {
  const newState = { ...entityData.value };
@@ -45,55 +31,259 @@ const removeText = () => {
    newState[item] = null;
  });
  editEntity({ ...newState });
  entityData.value = newState;
};
const editPosition = (position: 'left' | 'center' | 'right') => {
  entityData.value.entity_position = position;
  editEntity({ ...entityData.value, entity_position: position });
};
const editTitlePosition = (position: 'left' | 'center' | 'right') => {
  entityData.value.entity_title_position = position;
  editEntity({ ...entityData.value, entity_title_position: position });
};
const editTextPosition = (position: 'left' | 'right') => {
  entityData.value.text_position = position;
  editEntity({ ...entityData.value, text_position: position });
};
const editParagraphWidth = (widthMode: 'full' | 'half') => {
  entityData.value.paragraph_size = widthMode;
  editEntity({ ...entityData.value, paragraph_size: widthMode });
  newEntityData.value = newState;
};
const changeFontSize = (newSize: '16' | '20' | '24' | '40' | '64') => {
  entityData.value.font_size = newSize;
  editEntity({ ...entityData.value, font_size: newSize });
};
const themeColor: TTheme = cookies.get('favorite_color');
const themeColorConverted = convertThemeToColorWhiteDefault(themeColor);
const isTitle = ref(!!entityData.value.title);
const isText = ref(!!entityData.value.text);
const isEntityWidthFull = ref(entityData.value.paragraph_size === 'full');

const maxLines = computed(() => {
  if (isTitle.value) {
    return Math.floor(168 / 24);
  } else {
    return Math.floor(240 / 24);
  }
});
const entityIsTitleOptions = ref([
  {
    label: 'Off',
    value: false,
    textStyle: 'bold'
  },
  {
    label: 'On',
    value: true,
    textStyle: 'bold'
  }
]);
const entityIsTextOptions = ref([
  {
    label: 'Off',
    value: false,
    textStyle: 'bold'
  },
  {
    label: 'On',
    value: true,
    textStyle: 'bold'
  }
]);
const entityPositionOptions = ref([
  {
    label: 'left',
    isLabelHidden: true
  },
  {
    label: 'center',
    isLabelHidden: true
  },
  {
    label: 'right',
    isLabelHidden: true
  }
]);
const entityTitlePositionOptions = ref([
  {
    label: 'left',
    isLabelHidden: true
  },
  {
    label: 'center',
    isLabelHidden: true
  },
  {
    label: 'right',
    isLabelHidden: true
  }
]);
const entityTextPositionOptions = ref([
  {
    label: 'Left',
    value: 'left',
    textStyle: 'bold'
  },
  {
    label: 'Right',
    value: 'right',
    textStyle: 'bold'
  }
]);
const entityParagraphSizeOptions = ref([
  {
    label: 'Half',
    value: 'half',
    textStyle: 'bold'
  },
  {
    label: 'Full',
    value: 'full',
    textStyle: 'bold'
  }
]);
const imageScaleOptions = ref([
  {
    label: 'x0.25',
    value: 0,
    color: 'var(--purple-700)'
  },
  {
    label: 'x0.5',
    value: 1,
    color: 'var(--indigo-500)'
  },
  {
    label: 'x0.75',
    value: 2,
    color: 'var(--sky-500)'
  },
  {
    label: 'x1',
    value: 3,
    color: 'var(--green-500)'
  },
  {
    label: 'x1.25',
    value: 4,
    color: 'var(--yellow-500)'
  },
  {
    label: 'x1.5',
    value: 5,
    color: 'var(--orange-500)'
  },
  {
    label: 'x1.75',
    value: 6,
    color: 'var(--red-500)'
  },
  {
    label: 'x2',
    value: 7,
    color: 'var(--red-800)'
  }
]);
const saveChanges = () => {
  const entityPosition = isEntityWidthFull.value ? 'full' : 'half';
  if (entityPosition !== entityData.value.entity_position) {
    newEntityData.value.paragraph_size = entityPosition;
  }
  if (isTitle.value !== !!entityData.value.title) {
    if (isTitle.value) {
      newEntityData.value.title = 'Title';
    } else {
      newEntityData.value.title = null;
    }
  }
  if (isText.value !== !!entityData.value.title) {
    if (isText.value) {
      newEntityData.value.title = 'Text';
    } else {
      newEntityData.value.title = null;
    }
  }
  if (JSON.stringify(entityData) !== JSON.stringify(newEntityData.value)) {
    emit('saveChanges', newEntityData.value);
  }
  isModal.value = false;
};
</script>

<template>
  <section style="height: 146px" class="speedDial absolute left-2 top-0 transition-all select-none">
    <ImageStateMenu
      :entityData="entityData"
      class="h-12"
      @deleteEntity="deleteEntity"
      @addTitle="addTitle"
      @removeTitle="removeTitle"
      @addText="addText"
      @removeText="removeText"
      @openCropImageModal="$emit('openCropImageModal')"
  <button
    :style="`background-color: ${themeColorConverted}`"
    class="settings absolute left-2 top-0 select-none size-10 hover:brightness-75 transition-all cursor-pointer"
    @click.prevent="isModal = true"
  >
    <SettingsIcon color="white" size="25" />
  </button>
  <Modal v-model:isVisible="isModal" theme="black" width="90%"
    ><template #header><h3 class="w-max mx-auto">Edit paragraph</h3></template>
    <div class="p-10 flex gap-16 items-center">
      <ImageSettingsList
        v-model:newEntityData="newEntityData"
        v-model:isTitle="isTitle"
        v-model:isText="isText"
        v-model:isEntityWidthFull="isEntityWidthFull"
        :themeColor="themeColor"
        :entityIsTitleOptions="entityIsTitleOptions"
        :entityIsTextOptions="entityIsTextOptions"
        :entityPositionOptions="entityPositionOptions"
        :entityTitlePositionOptions="entityTitlePositionOptions"
        :entityTextPositionOptions="entityTextPositionOptions"
        :entityParagraphSizeOptions="entityParagraphSizeOptions"
        :imageScaleOptions="imageScaleOptions"
      />
    <div v-if="entityData?.text || entityData?.title">
      <TextFontMenu :entityData="entityData" class="h-12" @changeFontSize="changeFontSize" />
      <section
        :style="`border-color: var(--${themeColor}-200); height: 450px`"
        class="grow flex flex-col gap-4 p-4 min-h-full border-2 border-slate-100 border-dashed rounded-2xl"
      >
        <div :style="`justify-content: ${newEntityData.entity_position};`" class="flex">
          <div
            v-show="isTitle"
            :style="`border-color: var(--${themeColor}-800);
            justify-content: ${newEntityData.entity_title_position}; width: ${isEntityWidthFull ? '100%' : '50%'}`"
            class="flex text-2xl font-bold text-center px-2 py-4 border-2 border-dashed rounded-2xl"
          >
            <h3 class="w-2/3 overflow-ellipsis overflow-hidden whitespace-nowrap">
              {{ newEntityData.title ?? 'Title' }}
            </h3>
          </div>
    <div v-if="entities.length > 1">
      <ImagePositionMenu
        :entityData="entityData"
        @editPosition="editPosition"
        @editTitlePosition="editTitlePosition"
        @editTextPosition="editTextPosition"
        @editParagraphWidth="editParagraphWidth"
      />
        </div>
        <div class="flex">
          <div :style="`justify-content: ${newEntityData.entity_position}`" class="flex">
            <div
              v-show="isText"
              :style="`border-color: var(--${themeColor}-400); width: ${isEntityWidthFull ? '100%' : '50%'};`"
              class="h-full p-4 pb-2 border-2 border-dashed rounded-2xl overflow-hidden"
            >
              <p class="pb-0 overflow-hidden contain-inline-size text">
                {{ newEntityData.text ?? 'Text' }}
              </p>
            </div>
          </div>
          <img class="h-max" :src="newEntityData.imageUrl" alt="" />
        </div>
      </section>
      <div class="absolute top-4 right-16 z-10 hover:brightness-80 transition-all">
        <Button label="Delete" textColor="white" theme="red" textStyle="bold" size="medium">
          <template #icon>
            <TrashIcon color="white" size="25" />
          </template>
        </Button>
      </div>
      <div
        class="absolute top-4 left-4 z-10 hover:brightness-80 transition-all"
        @click.prevent="saveChanges"
      >
        <Button label="Save" textColor="white" :theme="themeColor" textStyle="bold" size="medium">
          <template #icon>
            <SaveIcon color="white" size="25" />
          </template>
        </Button>
      </div>
    </div>
  </Modal>
</template>

<style scoped></style>
<style scoped>
.text {
  --max-lines: v-bind(maxLines);
  overflow: hidden;

  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: var(--max-lines);
}
.settings {
  display: flex;
  justify-content: center;
  align-items: center;
  border-radius: 50%;
}
</style>
+78 −25
Original line number Diff line number Diff line
<script setup lang="ts">
import { convertThemeToColorWhiteDefault, editEntity } from '@/app/helpers';
import { convertThemeToColorWhiteDefault, deleteEntity, editEntity } from '@/app/helpers';
import type { IParagraph } from '@/app/interfaces/entities';
import type { TTheme } from '@/app/interfaces/environment';
import cookies from '@/app/plugins/Cookie';
@@ -9,18 +9,19 @@ interface Props {
}
const props = defineProps<Props>();
const emit = defineEmits(['saveChanges']);
const entityData = props.entityData;
let newEntityData = ref({ ...entityData });
const entityData = computed(() => props.entityData);
const newEntityData = ref({ ...entityData.value });
watch(entityData, () => (newEntityData.value = entityData.value));
const isModal = ref<boolean>(false);

const isModalToDeleteParagraph = ref<boolean>(false);
const changeFontSize = (newSize: '16' | '20' | '24' | '40' | '64') => {
  entityData.font_size = newSize;
  editEntity({ ...entityData, font_size: newSize });
  entityData.value.font_size = newSize;
  editEntity({ ...entityData.value, font_size: newSize });
};
const themeColor: TTheme = cookies.get('favorite_color');
const themeColorConverted = convertThemeToColorWhiteDefault(themeColor);
const isTitle = ref(!!entityData.title);
const isEntityWidthFull = ref(entityData.paragraph_size === 'full');
const isTitle = ref(!!entityData.value.title);
const isEntityWidthFull = ref(entityData.value.paragraph_size === 'full');

const maxLines = computed(() => {
  if (isTitle.value) {
@@ -29,51 +30,81 @@ const maxLines = computed(() => {
    return Math.floor(240 / 24);
  }
});
const entityPositionOptions = [
const entityIsTitleOptions = ref([
  {
    label: 'Off',
    value: false,
    textStyle: 'bold'
  },
  {
    label: 'On',
    value: true,
    textStyle: 'bold'
  }
]);
const isEntityWidthFullOptions = ref([
  {
    label: 'Half',
    value: false,
    textStyle: 'bold'
  },
  {
    label: 'Full',
    value: true,
    textStyle: 'bold'
  }
]);
const entityPositionOptions = ref([
  {
    label: 'left',
    value: 0
    isLabelHidden: true
  },
  {
    label: 'center',
    value: 1
    isLabelHidden: true
  },
  {
    label: 'right',
    value: 2
    isLabelHidden: true
  }
];
const entityTitlePositionOptions = [
]);
const entityTitlePositionOptions = ref([
  {
    label: 'left',
    value: 0
    isLabelHidden: true
  },
  {
    label: 'center',
    value: 1
    isLabelHidden: true
  },
  {
    label: 'right',
    value: 2
    isLabelHidden: true
  }
];
]);
const saveChanges = () => {
  const entityPosition = isEntityWidthFull.value ? 'full' : 'half';
  if (entityPosition !== entityData.entity_position) {
  if (entityPosition !== entityData.value.entity_position) {
    newEntityData.value.paragraph_size = entityPosition;
  }
  if (isTitle.value !== !!entityData.title) {
  if (isTitle.value !== !!entityData.value.title) {
    if (isTitle.value) {
      newEntityData.value.title = 'Title';
    } else {
      newEntityData.value.title = null;
    }
  }
  if (JSON.stringify(entityData) !== JSON.stringify(newEntityData.value)) {
    console.log('they are different, I will save it. New data:', newEntityData.value);
  if (JSON.stringify(entityData.value) !== JSON.stringify(newEntityData.value)) {
    emit('saveChanges', newEntityData.value);
  }
  console.log('newEntityData.value :', newEntityData.value);
  isModal.value = false;
};
const toggleConfirmToDeleteParagraph = () => {
  isModalToDeleteParagraph.value = !isModalToDeleteParagraph.value;
};
const deleteParagraph = () => {
  deleteEntity(entityData.value.entity_uuid);
  isModalToDeleteParagraph.value = false;
  isModal.value = false;
};
</script>
@@ -86,14 +117,33 @@ const saveChanges = () => {
  >
    <SettingsIcon color="white" size="25" />
  </button>
  <Modal v-model:isVisible="isModal" theme="black" width="70%"
  <Modal v-model:isVisible="isModal" theme="black" width="90%"
    ><template #header><h3 class="w-max mx-auto">Edit paragraph</h3></template>
    <Modal v-model:isVisible="isModalToDeleteParagraph" theme="black" width="30%"
      ><p class="font-bold pt-4 mb-4 text-center">Are you sure you want to delete this element?</p>
      <div class="flex justify-between">
        <Button
          label="Yes, delete"
          theme="red"
          textColor="white"
          textStyle="bold"
          @click.prevent="deleteParagraph"
        />
        <Button
          label="Cancel"
          theme="white"
          textColor="black"
          @click.prevent="toggleConfirmToDeleteParagraph"
        /></div
    ></Modal>
    <div class="p-10 flex gap-16 items-center">
      <ParagraphSettingsList
        v-model:newEntityData="newEntityData"
        v-model:isTitle="isTitle"
        v-model:isEntityWidthFull="isEntityWidthFull"
        :themeColor="themeColor"
        :entityIsTitleOptions="entityIsTitleOptions"
        :isEntityWidthFullOptions="isEntityWidthFullOptions"
        :entityPositionOptions="entityPositionOptions"
        :entityTitlePositionOptions="entityTitlePositionOptions"
      />
@@ -121,7 +171,10 @@ const saveChanges = () => {
          </div>
        </div>
      </section>
      <div class="absolute top-4 right-16 z-10 hover:brightness-80 transition-all">
      <div
        class="absolute top-4 right-16 z-10 hover:brightness-80 transition-all"
        @click.prevent="toggleConfirmToDeleteParagraph"
      >
        <Button label="Delete" textColor="white" theme="red" textStyle="bold" size="medium">
          <template #icon>
            <TrashIcon color="white" size="25" />
Loading