Vue

setup에서 CSS Module에 접근하는 방법

milliwonkim 2022. 6. 18. 23:52
반응형
SMALL
// @/views/TeacherPage/TeacherCard.vue

<template>
  <div
    :class="classes.cardViewBox"
    @click="handleClickCard"
    @keypress="handleClickCard"
  >
    <teacher-image :dynamicShow="true" :imageUrl="card.image" />
    <div :class="classes.teacherInfoBox">
      <p :class="classes.teacherNameLabel">
        {{ card.name }} <span :class="classes.teacherLabel">선생님</span>
      </p>
      <p class="current-job-label">{{ card.currentJob }}</p>
      <div :class="classes.sectionBox">
        <plain-text
          :cssModuleProps="{ fontWeight: 'fontWeight500', color: 'colorRed' }"
          >주요분야:
        </plain-text>
        <plain-text
          :cssModuleProps="{ fontWeight: 'fontWeight700', color: 'colorBlue' }"
          >{{ card.mainSection }}</plain-text
        >
      </div>
      <div class="price-box">
        <p>{{ card.price }}</p>
        <p>{{ card.evaluationIndex }} / 10.0</p>
      </div>
    </div>
    <font-awesome-icon
      :class="classes.teacherRouteButton"
      icon="fa-solid fa-chevron-right"
    />
  </div>
</template>
<script>
import { defineComponent, toRefs } from 'vue';
import TeacherImage from '@/components/TeacherImage.vue';
import PlainText from '@/components/PlainText.vue';

// this is must have
import { library } from '@fortawesome/fontawesome-svg-core';
// import { name of your icon in camelCase } from "@fortawesome/free-solid-svg-icons";
// For example, I want to use fa-enveloper-open-text, then it's faEnvelopeOpenText
import { faChevronRight } from '@fortawesome/free-solid-svg-icons';
// Then add it to library
library.add(faChevronRight);

export default defineComponent({
  name: 'teacher-card',
  props: ['teacherInfo', 'handleClick'],
  components: { 'teacher-image': TeacherImage, 'plain-text': PlainText },
  setup(props, { emit }) {
    const card = toRefs(props).teacherInfo;
    function handleClickCard() {
      emit('handleClick');
    }

    return { handleClickCard, card };
  },
});
</script>
<style lang="scss" module="classes">
.cardViewBox {
  display: flex;
  gap: 16px;
  justify-content: space-between;
}

.teacherInfoBox {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: 8px;
  @media (max-width: 450px) {
    width: 100%;
  }
}

.teacherNameLabel {
  display: flex;
  align-items: flex-end;
  gap: 8px;
  color: #000000;
  font-size: 24px;
  font-weight: 800;
}

.teacherLabel {
  color: #7a7a7a;
  font-size: 16px;
  font-weight: 600;
}

.current-job-label {
  color: #7a7a7a;
  font-weight: 700;
}

.sectionBox {
  display: flex;
  gap: 8px;
  align-items: center;
  font-weight: 600;
  color: #7a7a7a;
}

.price-box {
  display: flex;
  gap: 8px;
  justify-content: space-between;
  color: #7a7a7a;
}

.teacherRouteButton {
  display: inline-block;
  margin: auto 0;
}
</style>
// @hooks/useGetCssModule.ts

import { useCssModule } from 'vue';

interface CssModuleProps {
  [key: string]: {
    [key: string]: string;
  };
}

interface ICssModule {
  [key: string]: string;
}

function useGetCssModule({ cssModuleProps }: CssModuleProps) {
  const cssModuleObject = cssModuleProps.value;

  const $style = useCssModule();
  const cssModuleObj = {};

  const cssModules = Object.entries(cssModuleObject).reduce((obj, target) => {
    const [key, val] = target;
    if (!obj[key]) {
      obj[key] = $style[val];
    }

    return obj;
  }, cssModuleObj as ICssModule);

  return { cssModules };
}

export default useGetCssModule;
// PlainText.vue

<template>
  <p :class="[$style.label, fontWeight, color]">
    <slot />
  </p>
</template>
<script>
import { defineComponent, toRefs } from 'vue';
import useGetCssModule from '../hooks/useGetCssModule';

export default defineComponent({
  props: ['cssModuleProps'],
  setup(props) {
    const cssModuleProps = toRefs(props);
    const { cssModules } = useGetCssModule(cssModuleProps);
    const { fontWeight, color } = cssModules;

    return { fontWeight, color };
  },
});
</script>
<style lang="scss" module>
.label {
  display: flex;
  box-sizing: border-box;
  border-radius: 8px;
}

.fontWeight500 {
  font-weight: 500;
}

.fontWeight600 {
  font-weight: 600;
}

.fontWeight700 {
  font-weight: 700;
}

.colorRed {
  color: red;
}

.colorBlue {
  color: blue;
}
</style>

반응형
LIST