7 files added
3 files modified
527 ■■■■■ changed files
10 - course project/xno-prj-cmp-01-starting-setup/src/App.vue 62 ●●●● patch | view | raw | blame | history
10 - course project/xno-prj-cmp-01-starting-setup/src/components/UI/BaseButton.vue 39 ●●●●● patch | view | raw | blame | history
10 - course project/xno-prj-cmp-01-starting-setup/src/components/UI/BaseCard.vue 19 ●●●●● patch | view | raw | blame | history
10 - course project/xno-prj-cmp-01-starting-setup/src/components/UI/BaseDialog.vue 88 ●●●●● patch | view | raw | blame | history
10 - course project/xno-prj-cmp-01-starting-setup/src/components/layouts/TheHeader.vue 27 ●●●●● patch | view | raw | blame | history
10 - course project/xno-prj-cmp-01-starting-setup/src/components/learning-resource/AddResource.vue 96 ●●●●● patch | view | raw | blame | history
10 - course project/xno-prj-cmp-01-starting-setup/src/components/learning-resource/LearningResource.vue 63 ●●●● patch | view | raw | blame | history
10 - course project/xno-prj-cmp-01-starting-setup/src/components/learning-resource/StoredResource.vue 34 ●●●●● patch | view | raw | blame | history
10 - course project/xno-prj-cmp-01-starting-setup/src/components/learning-resource/TheResources.vue 87 ●●●●● patch | view | raw | blame | history
10 - course project/xno-prj-cmp-01-starting-setup/src/main.js 12 ●●●●● patch | view | raw | blame | history
10 - course project/xno-prj-cmp-01-starting-setup/src/App.vue
@@ -1,39 +1,37 @@
<template>
    <ul>
        <learning-resource v-for="res in storedResouces"
        :key="res.id"
        :title="res.title"
        :description="res.description"
        :link="res.link"
        >
        </learning-resource>
    </ul>
  <TheHeader :title="rememberMe"></TheHeader>
  <TheResources></TheResources>
</template>
<script>
import LearningResource from './components/learning-resource/LearningResource.vue';
import TheHeader from './components/layouts/TheHeader.vue';
import TheResources from './components/learning-resource/TheResources.vue';
export default {
    components:{
        LearningResource: LearningResource,
    },
    data(){
        return {
            storedResouces: [
                {
                    id:'official-guide',
                    title: 'Official Guide',
                    description: 'The official Vue.js documentation',
                    link: 'https://vuejs.org'
                },
                {
                    id:'google',
                    title: 'Google',
                    description: 'Learn to google...',
                    link: 'https://google.com'
                },
            ]
        }
    }
  components: {
    TheHeader: TheHeader,
    TheResources: TheResources,
  },
  data() {
    return {
      rememberMe: 'Remember Me',
    };
  },
};
</script>
<style>
@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap');
* {
  box-sizing: border-box;
}
</script>
html {
  font-family: 'Roboto', sans-serif;
}
body {
  margin: 0;
}
</style>
10 - course project/xno-prj-cmp-01-starting-setup/src/components/UI/BaseButton.vue
New file
@@ -0,0 +1,39 @@
<template>
  <button :type="type" :class="mode">
    <slot></slot>
  </button>
</template>
<script>
export default {
  props: ['type', 'mode'],
};
</script>
<style scoped>
button {
  padding: 0.75rem 1.5rem;
  font-family: inherit;
  background-color: #3a0061;
  border: 1px solid #3a0061;
  color: white;
  cursor: pointer;
}
button:hover,
button:active {
  background-color: #270041;
  border-color: #270041;
}
.flat {
  background-color: transparent;
  color: #3a0061;
  border: none;
}
.flat:hover,
.flat:active {
  background-color: #edd2ff;
}
</style>
10 - course project/xno-prj-cmp-01-starting-setup/src/components/UI/BaseCard.vue
New file
@@ -0,0 +1,19 @@
<template>
  <div>
    <slot></slot>
  </div>
</template>
<script>
export default {};
</script>
<style scoped>
div {
  border-radius: 12px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.26);
  padding: 1rem;
  margin: 2rem auto;
  max-width: 40rem;
}
</style>
10 - course project/xno-prj-cmp-01-starting-setup/src/components/UI/BaseDialog.vue
New file
@@ -0,0 +1,88 @@
<template>
  <teleport to="body">
    <div @click="$emit('close')"></div>
    <dialog open>
      <header>
        <slot name="header">
          <h2>{{ title }}</h2>
        </slot>
      </header>
      <section>
        <slot></slot>
      </section>
      <menu>
        <slot name="action">
          <base-button @click="$emit('close')"></base-button>
        </slot>
      </menu>
    </dialog>
  </teleport>
</template>
<script>
export default {
  //   props: ['title'],
  props: {
    title: {
      type: String,
      required: false,
    },
  },
  emits: ['close'],
};
</script>
<style scoped>
div {
  position: fixed;
  top: 0;
  left: 0;
  height: 100vh;
  width: 100%;
  background-color: rgba(0, 0, 0, 0.75);
  z-index: 10;
}
dialog {
  position: fixed;
  top: 20vh;
  left: 10%;
  width: 80%;
  z-index: 100;
  border-radius: 12px;
  border: none;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.26);
  padding: 0;
  margin: 0;
  overflow: hidden;
}
header {
  background-color: #3a0061;
  color: white;
  width: 100%;
  padding: 1rem;
}
header h2 {
  margin: 0;
}
section {
  padding: 1rem;
}
menu {
  padding: 1rem;
  display: flex;
  justify-content: flex-end;
  margin: 0;
}
@media (min-width: 768px) {
  dialog {
    left: calc(50% - 20rem);
    width: 40rem;
  }
}
</style>
10 - course project/xno-prj-cmp-01-starting-setup/src/components/layouts/TheHeader.vue
New file
@@ -0,0 +1,27 @@
<template>
  <header>
    <h1>{{ title }}</h1>
  </header>
</template>
<script>
export default {
  props: ['title'],
};
</script>
<style scoped>
header {
  width: 100%;
  height: 5rem;
  background-color: #640032;
  display: flex;
  justify-content: center;
  align-items: center;
}
header h1 {
  color: white;
  margin: 0;
}
</style>
10 - course project/xno-prj-cmp-01-starting-setup/src/components/learning-resource/AddResource.vue
New file
@@ -0,0 +1,96 @@
<template>
  <base-dialog v-if="inputIsInvalid" title="Invalid Input" @close="confirmError">
    <template #default>
      <p>Sfortunatamente un input รจ invalido</p>
      <p>Controlla che tutti gli input contengano qualche carattere valido.</p>
    </template>
    <template #action>
      <base-button @click="confirmError">Okay</base-button>
    </template>
  </base-dialog>
  <base-card>
    <form @submit.prevent="submitData">
      <div class="form-control">
        <label for="title">Title</label>
        <input id="title" name="title" type="text" ref="titleInput" />
      </div>
      <div class="form-control">
        <label for="description">Description</label>
        <textarea
          id="description"
          name="description"
          type="text"
          rows="3"
          ref="descInput"
        ></textarea>
      </div>
      <div class="form-control">
        <label for="link">Link</label>
        <input id="link" name="link" type="url" ref="linkInput" />
      </div>
      <div>
        <base-button type="submit">Add Resource</base-button>
      </div>
    </form>
  </base-card>
</template>
<script>
export default {
  inject: ['addResource'],
  data() {
    return {
      inputIsInvalid: false,
    };
  },
  methods: {
    submitData() {
      const enteredTitle = this.$refs.titleInput.value;
      const enteredDescription = this.$refs.descInput.value;
      const enteredUrl = this.$refs.linkInput.value;
      if (
        enteredTitle.trim() === '' ||
        enteredDescription.trim() === '' ||
        enteredUrl.trim() === ''
      ) {
        this.inputIsInvalid = true;
        return;
      }
      this.addResource(enteredTitle, enteredDescription, enteredUrl);
    },
    confirmError() {
      this.inputIsInvalid = false;
    },
  },
};
</script>
<style scoped>
label {
  font-weight: bold;
  display: block;
  margin-bottom: 0.5rem;
}
input,
textarea {
  display: block;
  width: 100%;
  font: inherit;
  padding: 0.15rem;
  border: 1px solid #ccc;
}
input:focus,
textarea:focus {
  outline: none;
  border-color: #3a0061;
  background-color: #f7ebff;
}
.form-control {
  margin: 1rem 0;
}
</style>
10 - course project/xno-prj-cmp-01-starting-setup/src/components/learning-resource/LearningResource.vue
@@ -1,20 +1,55 @@
<template>
    <li>
        <div>
            <header>
                <h3>{{ title }}</h3>
                <button>Delete</button>
            </header>
        </div>
        <p>{{ description }}</p>
        <nav>
            <a :href="link">View Resource</a>
        </nav>
    </li>
  <li>
    <base-card>
      <header>
        <h3>{{ title }}</h3>
        <base-button mode="flat" @click="deleteResource(id)"
          >Delete</base-button
        >
      </header>
      <p>{{ description }}</p>
      <nav>
        <a :href="link">View Resource</a>
      </nav>
    </base-card>
  </li>
</template>
<script>
export default {
    props: ['title', 'description', 'link']
  props: ['id', 'title', 'description', 'link'],
  inject: ['deleteResource'],
};
</script>
<style scoped>
li {
  margin: auto;
  max-width: 40rem;
}
</script>
header {
  display: flex;
  justify-content: space-between;
  align-items: center;
}
h3 {
  font-size: 1.25rem;
  margin: 0.5rem 0;
}
p {
  margin: 0.5rem 0;
}
a {
  text-decoration: none;
  color: #ce5c00;
}
a:hover,
a:active {
  color: #c89300;
}
</style>
10 - course project/xno-prj-cmp-01-starting-setup/src/components/learning-resource/StoredResource.vue
New file
@@ -0,0 +1,34 @@
<template>
  <ul>
    <learning-resource
      v-for="res in resources"
      :key="res.id"
      :id="res.id"
      :title="res.title"
      :description="res.description"
      :link="res.link"
    >
    </learning-resource>
  </ul>
</template>
<script>
import LearningResource from './LearningResource.vue';
export default {
  components: {
    LearningResource: LearningResource,
  },
  inject: ['resources'],
};
</script>
<style>
ul {
  list-style: none;
  margin: 0;
  padding: 0;
  margin: auto;
  max-width: 40rem;
}
</style>
10 - course project/xno-prj-cmp-01-starting-setup/src/components/learning-resource/TheResources.vue
New file
@@ -0,0 +1,87 @@
<template>
  <base-card>
    <base-button
      @click="setSelectedTab('stored-resources')"
      :mode="storedResButtonMode"
      >Stored Resource</base-button
    >
    <base-button
      @click="setSelectedTab('add-resource')"
      :mode="addResButtonMode"
      >Add Resource</base-button
    >
  </base-card>
  <KeepAlive>
    <component :is="selectedTab"></component>
  </KeepAlive>
</template>
<script>
import StoredResources from './StoredResource.vue';
import AddResource from './AddResource.vue';
export default {
  components: {
    StoredResources: StoredResources,
    AddResource: AddResource,
  },
  data() {
    return {
      selectedTab: 'stored-resources',
      storedResouces: [
        {
          id: 'official-guide',
          title: 'Official Guide',
          description: 'The official Vue.js documentation',
          link: 'https://vuejs.org',
        },
        {
          id: 'google',
          title: 'Google',
          description: 'Learn to google...',
          link: 'https://google.com',
        },
      ],
    };
  },
  computed: {
    storedResButtonMode() {
      return this.selectedTab === 'stored-resources' ? null : 'flat';
    },
    addResButtonMode() {
      return this.selectedTab === 'add-resource' ? null : 'flat';
    },
  },
  provide() {
    return {
      resources: this.storedResouces,
      addResource: this.addResource,
      deleteResource: this.removeResource,
    };
  },
  methods: {
    setSelectedTab(tab) {
      this.selectedTab = tab;
    },
    addResource(title, description, url) {
      const newResource = {
        id: new Date().toISOString(),
        title: title,
        description: description,
        link: url,
      };
      this.storedResouces.unshift(newResource);
      this.selectedTab = 'stored-resources';
    },
    removeResource(resId) {
      const resIndex = this.storedResouces.findIndex(
        (res) => res.id === resId
      );
      this.storedResouces.splice(resIndex, 1);
    },
  },
};
</script>
<style scoped></style>
10 - course project/xno-prj-cmp-01-starting-setup/src/main.js
@@ -1,5 +1,15 @@
import { createApp } from 'vue';
import App from './App.vue';
import BaseCard from './components/UI/BaseCard.vue';
import BaseButton from './components/UI/BaseButton.vue';
import BaseDialog from './components/UI/BaseDialog.vue';
createApp(App).mount('#app');
const app = createApp(App);
app.component('base-card', BaseCard);
app.component('base-button', BaseButton);
app.component('base-dialog', BaseDialog);
app.mount('#app');