Initial commit
This commit is contained in:
commit
8a1acb4bd0
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
# Auto detect text files and perform LF normalization
|
||||
* text=auto
|
||||
14
Contributing.md
Normal file
14
Contributing.md
Normal file
@ -0,0 +1,14 @@
|
||||
# Contributing to Apress Source Code
|
||||
|
||||
Copyright for Apress source code belongs to the author(s). However, under fair use you are encouraged to fork and contribute minor corrections and updates for the benefit of the author(s) and other readers.
|
||||
|
||||
## How to Contribute
|
||||
|
||||
1. Make sure you have a GitHub account.
|
||||
2. Fork the repository for the relevant book.
|
||||
3. Create a new branch on which to make your change, e.g.
|
||||
`git checkout -b my_code_contribution`
|
||||
4. Commit your change. Include a commit message describing the correction. Please note that if your commit message is not clear, the correction will not be accepted.
|
||||
5. Submit a pull request.
|
||||
|
||||
Thank you for your contribution!
|
||||
27
LICENSE.txt
Normal file
27
LICENSE.txt
Normal file
@ -0,0 +1,27 @@
|
||||
Freeware License, some rights reserved
|
||||
|
||||
Copyright (c) Eric Sarrion 2024
|
||||
|
||||
Permission is hereby granted, free of charge, to anyone obtaining a copy
|
||||
of this software and associated documentation files (the "Software"),
|
||||
to work with the Software within the limits of freeware distribution and fair use.
|
||||
This includes the rights to use, copy, and modify the Software for personal use.
|
||||
Users are also allowed and encouraged to submit corrections and modifications
|
||||
to the Software for the benefit of other users.
|
||||
|
||||
It is not allowed to reuse, modify, or redistribute the Software for
|
||||
commercial use in any way, or for a user’s educational materials such as books
|
||||
or blog articles without prior permission from the copyright holder.
|
||||
|
||||
The above copyright notice and this permission notice need to be included
|
||||
in all copies or substantial portions of the software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS OR APRESS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
|
||||
16
README.md
Normal file
16
README.md
Normal file
@ -0,0 +1,16 @@
|
||||
# Apress Source Code
|
||||
|
||||
This repository accompanies [*Master Vue.js in 6 Days*](https://www.link.springer.com/book/10.1007/979-8-8688-0364-2) by Eric Sarrion (Apress, 2024).
|
||||
|
||||
[comment]: #cover
|
||||

|
||||
|
||||
Download the files as a zip using the green button, or clone the repository to your machine using Git.
|
||||
|
||||
## Releases
|
||||
|
||||
Release v1.0 corresponds to the code in the published book, without corrections or updates.
|
||||
|
||||
## Contributions
|
||||
|
||||
See the file Contributing.md for more information on how you can contribute to this repository.
|
||||
11
Source Code/src/App.vue
Normal file
11
Source Code/src/App.vue
Normal file
@ -0,0 +1,11 @@
|
||||
<script setup>
|
||||
|
||||
import MyCounter from "./components/MyCounter.vue"
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
<MyCounter />
|
||||
|
||||
</template>
|
||||
BIN
Source Code/src/assets/logo.png
Normal file
BIN
Source Code/src/assets/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.7 KiB |
58
Source Code/src/components/HelloWorld.vue
Normal file
58
Source Code/src/components/HelloWorld.vue
Normal file
@ -0,0 +1,58 @@
|
||||
<template>
|
||||
<div class="hello">
|
||||
<h1>{{ msg }}</h1>
|
||||
<p>
|
||||
For a guide and recipes on how to configure / customize this project,<br>
|
||||
check out the
|
||||
<a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
|
||||
</p>
|
||||
<h3>Installed CLI Plugins</h3>
|
||||
<ul>
|
||||
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" target="_blank" rel="noopener">babel</a></li>
|
||||
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint" target="_blank" rel="noopener">eslint</a></li>
|
||||
</ul>
|
||||
<h3>Essential Links</h3>
|
||||
<ul>
|
||||
<li><a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a></li>
|
||||
<li><a href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a></li>
|
||||
<li><a href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a></li>
|
||||
<li><a href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a></li>
|
||||
<li><a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a></li>
|
||||
</ul>
|
||||
<h3>Ecosystem</h3>
|
||||
<ul>
|
||||
<li><a href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a></li>
|
||||
<li><a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a></li>
|
||||
<li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener">vue-devtools</a></li>
|
||||
<li><a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a></li>
|
||||
<li><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'HelloWorld',
|
||||
props: {
|
||||
msg: String
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||
<style scoped>
|
||||
h3 {
|
||||
margin: 40px 0 0;
|
||||
}
|
||||
ul {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
}
|
||||
li {
|
||||
display: inline-block;
|
||||
margin: 0 10px;
|
||||
}
|
||||
a {
|
||||
color: #42b983;
|
||||
}
|
||||
</style>
|
||||
32
Source Code/src/components/MyCounter.vue
Normal file
32
Source Code/src/components/MyCounter.vue
Normal file
@ -0,0 +1,32 @@
|
||||
<script setup>
|
||||
|
||||
import useToggleTheme from '../composables/useToggleTheme.js';
|
||||
|
||||
const [toggleTheme, themeStyles] = useToggleTheme(
|
||||
{color:"red", backgroundColor:"black"},
|
||||
{color:"green", textAlign:"right", backgroundColor:"gainsboro"}
|
||||
);
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
<h3>MyCounter Component</h3>
|
||||
<div>
|
||||
<button @click="toggleTheme">Toggle Theme</button>
|
||||
<p :style="themeStyles">Paragraph 1</p>
|
||||
<p :style="themeStyles">Paragraph 2</p>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
p {
|
||||
height : 30px;
|
||||
padding-top: 10px;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
</style>
|
||||
15
Source Code/src/components/MyCounters.vue
Normal file
15
Source Code/src/components/MyCounters.vue
Normal file
@ -0,0 +1,15 @@
|
||||
<script setup>
|
||||
|
||||
import MyCounter from "./MyCounter.vue";
|
||||
import { defineProps } from "vue";
|
||||
|
||||
const props = defineProps(["nb"]);
|
||||
const nb = props.nb || 1;
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
<MyCounter v-for="i in nb" :key="i" :index="i" />
|
||||
|
||||
</template>
|
||||
25
Source Code/src/components/MyCountries.vue
Normal file
25
Source Code/src/components/MyCountries.vue
Normal file
@ -0,0 +1,25 @@
|
||||
<script setup>
|
||||
import useFetchCountries from "../composables/useFetchCountries"
|
||||
|
||||
import { ref } from "vue";
|
||||
const data = ref();
|
||||
|
||||
const url = "https://restcountries.com/v3.1/all";
|
||||
const [startFetch] = useFetchCountries(url);
|
||||
|
||||
const initData = async () => {
|
||||
data.value = await startFetch();
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
<button @click="initData">Start Fetch</button>
|
||||
<br><br>
|
||||
<b>Data</b> :
|
||||
<ul>
|
||||
<li v-for="(country, i) in data" :key="i">{{country}}</li>
|
||||
</ul>
|
||||
|
||||
</template>
|
||||
26
Source Code/src/components/MyForm.vue
Normal file
26
Source Code/src/components/MyForm.vue
Normal file
@ -0,0 +1,26 @@
|
||||
<script setup>
|
||||
|
||||
import { ref } from "vue"
|
||||
|
||||
const name = ref("");
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
<h3>Input Form</h3>
|
||||
Name: <input type="text" v-model.lazy="name" />
|
||||
<br/><br/>
|
||||
|
||||
<h3>Reactive Variables</h3>
|
||||
name: <b>{{name}}</b>
|
||||
<br/><br/>
|
||||
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
h3 {
|
||||
background-color: gainsboro;
|
||||
padding: 5px;
|
||||
}
|
||||
</style>
|
||||
14
Source Code/src/composables/useCounter.js
Normal file
14
Source Code/src/composables/useCounter.js
Normal file
@ -0,0 +1,14 @@
|
||||
import { ref } from "vue";
|
||||
|
||||
const useCounter = (init) => {
|
||||
const count = ref(init);
|
||||
const increment = () => {
|
||||
count.value++;
|
||||
}
|
||||
const decrement = () => {
|
||||
count.value--;
|
||||
}
|
||||
return [count, increment, decrement];
|
||||
}
|
||||
|
||||
export default useCounter;
|
||||
16
Source Code/src/composables/useCounterMax.js
Normal file
16
Source Code/src/composables/useCounterMax.js
Normal file
@ -0,0 +1,16 @@
|
||||
import useCounter from "../composables/useCounter";
|
||||
|
||||
const useCounterMax = (init, max) => {
|
||||
const [count, increment, decrement] = useCounter(init);
|
||||
const incrementMax = () => {
|
||||
if (count.value >= max) {
|
||||
return; // Avoid incrementing
|
||||
}
|
||||
else {
|
||||
increment(); // Increment
|
||||
}
|
||||
}
|
||||
return [count, incrementMax, decrement];
|
||||
}
|
||||
|
||||
export default useCounterMax;
|
||||
28
Source Code/src/composables/useCounterMaxWithError.js
Normal file
28
Source Code/src/composables/useCounterMaxWithError.js
Normal file
@ -0,0 +1,28 @@
|
||||
import useCounter from "../composables/useCounter";
|
||||
import { ref, onMounted } from "vue";
|
||||
|
||||
const useCounterMaxWithError = (init, max) => {
|
||||
const [count, increment, decrement] = useCounter(init);
|
||||
const error = ref("");
|
||||
const incrementMax = () => {
|
||||
if (count.value >= max) {
|
||||
error.value = "Maximum value reached!";
|
||||
}
|
||||
else {
|
||||
increment();
|
||||
error.value = "";
|
||||
}
|
||||
}
|
||||
const decrementMax = () => {
|
||||
decrement();
|
||||
if (count.value <= max) {
|
||||
error.value = "";
|
||||
}
|
||||
}
|
||||
onMounted(()=> {
|
||||
if (count.value > max) error.value = "Maximum value reached!";
|
||||
});
|
||||
return [count, incrementMax, decrementMax, error];
|
||||
}
|
||||
|
||||
export default useCounterMaxWithError;
|
||||
10
Source Code/src/composables/useFetch.js
Normal file
10
Source Code/src/composables/useFetch.js
Normal file
@ -0,0 +1,10 @@
|
||||
const useFetch = (url) => {
|
||||
const startFetch = async () => {
|
||||
const res = await fetch(url);
|
||||
const d = await res.text();
|
||||
return JSON.parse(d); // Returning the data read from the server in JSON format
|
||||
}
|
||||
return [startFetch];
|
||||
}
|
||||
|
||||
export default useFetch;
|
||||
17
Source Code/src/composables/useFetchCountries.js
Normal file
17
Source Code/src/composables/useFetchCountries.js
Normal file
@ -0,0 +1,17 @@
|
||||
import useFetch from "./useFetch";
|
||||
|
||||
const useFetchCountries = (url) => {
|
||||
const [startFetch] = useFetch(url);
|
||||
let countries;
|
||||
const startFetchCountries = async () => {
|
||||
const data = await startFetch();
|
||||
countries = data.map(function(elem) {
|
||||
return elem.name.common; // Retain only the common.name property
|
||||
});
|
||||
countries = countries.sort((n1, n2) => (n1 > n2)); // In ascending alphabetical order
|
||||
return countries;
|
||||
}
|
||||
return [startFetchCountries];
|
||||
}
|
||||
|
||||
export default useFetchCountries;
|
||||
34
Source Code/src/composables/useFormatDate.js
Normal file
34
Source Code/src/composables/useFormatDate.js
Normal file
@ -0,0 +1,34 @@
|
||||
import { customRef } from "vue";
|
||||
|
||||
const formatDate = (date, format) => {
|
||||
const options = { year: 'numeric', month: '2-digit', day: '2-digit' };
|
||||
|
||||
if (format == "MM-DD-YYYY")
|
||||
return date.toLocaleDateString('en-US', options).replace(/\//g, '-');
|
||||
else if (format == "DD-MM-YYYY")
|
||||
return date.toLocaleDateString('en-GB', options).replace(/\//g, '-');
|
||||
else if (format == "MM/DD/YYYY")
|
||||
return date.toLocaleDateString('en-US', options);
|
||||
else if (format == "DD/MM/YYYY")
|
||||
return date.toLocaleDateString('en-GB', options);
|
||||
}
|
||||
|
||||
const useFormatDate = (date, format) => {
|
||||
return customRef((track, trigger) => {
|
||||
let value = date; // value will be the tracked variable
|
||||
return {
|
||||
get() {
|
||||
// track the dependency when the value is read
|
||||
track();
|
||||
return formatDate(value, format);
|
||||
},
|
||||
set(newValue) {
|
||||
// update the value and trigger reactivity
|
||||
value = newValue;
|
||||
trigger();
|
||||
}
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
export default useFormatDate;
|
||||
27
Source Code/src/composables/useGeolocation.js
Normal file
27
Source Code/src/composables/useGeolocation.js
Normal file
@ -0,0 +1,27 @@
|
||||
import { ref, onMounted } from "vue";
|
||||
|
||||
const useGeolocation = () => {
|
||||
const latitude = ref(null);
|
||||
const longitude = ref(null);
|
||||
|
||||
const handleGeolocation = (position) => {
|
||||
latitude.value = position.coords.latitude;
|
||||
longitude.value = position.coords.longitude;
|
||||
};
|
||||
|
||||
const errorGeolocation = (error) => {
|
||||
console.log("Geolocation error:", error.message);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
if (navigator.geolocation) {
|
||||
navigator.geolocation.getCurrentPosition(handleGeolocation, errorGeolocation);
|
||||
} else {
|
||||
console.log("Geolocation is not available in this browser.");
|
||||
}
|
||||
});
|
||||
|
||||
return [latitude, longitude];
|
||||
}
|
||||
|
||||
export default useGeolocation;
|
||||
29
Source Code/src/composables/useGeolocationWithDetails.js
Normal file
29
Source Code/src/composables/useGeolocationWithDetails.js
Normal file
@ -0,0 +1,29 @@
|
||||
import { ref, watchEffect } from "vue";
|
||||
|
||||
import useGeolocation from '../composables/useGeolocation';
|
||||
|
||||
const useGeolocationWithDetails = () => {
|
||||
const [latitude, longitude] = useGeolocation();
|
||||
const country = ref("");
|
||||
const city = ref("");
|
||||
|
||||
// To find the country and city corresponding to the latitude/longitude
|
||||
watchEffect(async ()=>{
|
||||
if (latitude.value && longitude.value) {
|
||||
const response = await fetch(
|
||||
`https://nominatim.openstreetmap.org/reverse?format=json&lat=${latitude.value}&lon=${longitude.value}`
|
||||
);
|
||||
const data = await response.json();
|
||||
if (data && data.address && data.address.country) {
|
||||
country.value = data.address.country;
|
||||
}
|
||||
if (data && data.address) {
|
||||
city.value = data.address.city || data.address.town;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return [latitude, longitude, country, city];
|
||||
}
|
||||
|
||||
export default useGeolocationWithDetails;
|
||||
21
Source Code/src/composables/useMap.js
Normal file
21
Source Code/src/composables/useMap.js
Normal file
@ -0,0 +1,21 @@
|
||||
import L from "leaflet"
|
||||
|
||||
const useMap = (latitude, longitude, idMap) => {
|
||||
const zoom = 13;
|
||||
|
||||
// To position the map at the indicated location
|
||||
const map = L.map(idMap).setView([latitude, longitude], zoom);
|
||||
|
||||
// To display the corresponding map
|
||||
L.tileLayer("https://a.tile.openstreetmap.org/{z}/{x}/{y}.png", {
|
||||
maxZoom: 20,
|
||||
}).addTo(map);
|
||||
|
||||
// To display a marker on the map to indicate the specified location
|
||||
L.marker([latitude, longitude]).addTo(map);
|
||||
|
||||
// We return the map object created by Leaflet
|
||||
return map;
|
||||
}
|
||||
|
||||
export default useMap;
|
||||
22
Source Code/src/composables/useMaximum.js
Normal file
22
Source Code/src/composables/useMaximum.js
Normal file
@ -0,0 +1,22 @@
|
||||
import { customRef } from "vue";
|
||||
|
||||
const useMaximum = (max) => {
|
||||
// Create a custom reference (customRef)
|
||||
return customRef((track, trigger) => {
|
||||
let value = 0; // value will be the variable being tracked, initialized here to 0.
|
||||
return {
|
||||
get() {
|
||||
// Track the dependency when the value is read.
|
||||
track();
|
||||
return value;
|
||||
},
|
||||
set(newValue) {
|
||||
// Update the value and trigger reactivity.
|
||||
if (newValue <= max) value = newValue;
|
||||
trigger();
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export default useMaximum;
|
||||
17
Source Code/src/composables/useToggleTheme.js
Normal file
17
Source Code/src/composables/useToggleTheme.js
Normal file
@ -0,0 +1,17 @@
|
||||
import { ref } from 'vue';
|
||||
|
||||
const useThemeToggle = (theme0, theme1) => {
|
||||
const themes = [theme0, theme1];
|
||||
let theme = 0; // theme0 by default
|
||||
const themeStyles = ref(themes[theme]);
|
||||
|
||||
const toggleTheme = () => {
|
||||
if (theme == 0) theme = 1;
|
||||
else theme = 0;
|
||||
themeStyles.value = themes[theme];
|
||||
};
|
||||
|
||||
return [toggleTheme, themeStyles];
|
||||
}
|
||||
|
||||
export default useThemeToggle;
|
||||
21
Source Code/src/composables/useUpperCase.js
Normal file
21
Source Code/src/composables/useUpperCase.js
Normal file
@ -0,0 +1,21 @@
|
||||
import { customRef } from 'vue';
|
||||
|
||||
const useUpperCase = (initValue) => {
|
||||
return customRef((track, trigger) => {
|
||||
let value = initValue; // value will be the tracked variable
|
||||
return {
|
||||
get() {
|
||||
// track the dependency when the value is read
|
||||
track();
|
||||
return value.toUpperCase();
|
||||
},
|
||||
set(newValue) {
|
||||
// update the value and trigger reactivity
|
||||
value = newValue;
|
||||
trigger();
|
||||
}
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
export default useUpperCase;
|
||||
27
Source Code/src/composables/useWindowSize.js
Normal file
27
Source Code/src/composables/useWindowSize.js
Normal file
@ -0,0 +1,27 @@
|
||||
import { ref, onMounted, onBeforeUnmount } from "vue";
|
||||
|
||||
const useWindowSize = () => {
|
||||
const windowSize = ref({
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight,
|
||||
});
|
||||
|
||||
const updateWindowSize = () => {
|
||||
windowSize.value = {
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight,
|
||||
};
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener('resize', updateWindowSize);
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('resize', updateWindowSize);
|
||||
});
|
||||
|
||||
return windowSize;
|
||||
}
|
||||
|
||||
export default useWindowSize;
|
||||
18
Source Code/src/directives.js
Normal file
18
Source Code/src/directives.js
Normal file
@ -0,0 +1,18 @@
|
||||
import focus from "./directives/focus";
|
||||
import integersOnly from "./directives/integers-only";
|
||||
import maxValue from "./directives/max-value";
|
||||
import clearable from "./directives/clearable";
|
||||
import timer from "./directives/timer";
|
||||
import map from "./directives/map";
|
||||
import color from "./directives/color";
|
||||
|
||||
export default {
|
||||
focus,
|
||||
integersOnly,
|
||||
maxValue,
|
||||
clearable,
|
||||
timer,
|
||||
map,
|
||||
color,
|
||||
}
|
||||
|
||||
24
Source Code/src/directives/clearable.js
Normal file
24
Source Code/src/directives/clearable.js
Normal file
@ -0,0 +1,24 @@
|
||||
const clearable = {
|
||||
mounted(el) {
|
||||
const clearButton = document.createElement("button");
|
||||
clearButton.innerHTML = "Clear";
|
||||
clearButton.style = "position:relative; left:10px;";
|
||||
|
||||
// Handle the click on the button (clear the content of the input field).
|
||||
clearButton.addEventListener("click", () => {
|
||||
// Clear the content of the input field.
|
||||
el.value = "";
|
||||
// Simulate an input event to mimic a keyboard key press
|
||||
// (mandatory to ensure that the reactive variable linked to the input field is updated)
|
||||
el.dispatchEvent(new Event("input"));
|
||||
// Give focus to the input field
|
||||
el.focus();
|
||||
});
|
||||
|
||||
// Insert the button after the input field
|
||||
el.parentNode.insertBefore(clearButton, el.nextSibling);
|
||||
}
|
||||
};
|
||||
|
||||
export default clearable;
|
||||
|
||||
17
Source Code/src/directives/color.js
Normal file
17
Source Code/src/directives/color.js
Normal file
@ -0,0 +1,17 @@
|
||||
const color = {
|
||||
mounted(el, binding) {
|
||||
let colorStyle;
|
||||
if (binding.modifiers.toggle) {
|
||||
const colors = binding.value;
|
||||
el.addEventListener("click", () => {
|
||||
if (colorStyle == colors[0]) colorStyle = colors[1];
|
||||
else if (colorStyle == colors[1]) colorStyle = colors[0];
|
||||
else colorStyle = colors[0];
|
||||
// Change the background color of the element
|
||||
el.style.backgroundColor = colorStyle;
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export default color;
|
||||
27
Source Code/src/directives/focus.js
Normal file
27
Source Code/src/directives/focus.js
Normal file
@ -0,0 +1,27 @@
|
||||
const focusDirective = {
|
||||
mounted(el, binding) {
|
||||
const arg = binding.arg;
|
||||
const value = binding.value;
|
||||
// Position the handling of the focus and blur events
|
||||
el.addEventListener("focus", () => {
|
||||
if (arg == "color") el.style.color = value;
|
||||
if (arg == "backgroundcolor") el.style.backgroundColor = value;
|
||||
if (arg == "colors") {
|
||||
el.style.color = value.color;
|
||||
el.style.backgroundColor = value.backgroundcolor;
|
||||
}
|
||||
});
|
||||
el.addEventListener("blur", () => {
|
||||
if (arg == "color") el.style.color = "";
|
||||
if (arg == "backgroundcolor") el.style.backgroundColor = "";
|
||||
if (arg == "colors") {
|
||||
el.style.color = "";
|
||||
el.style.backgroundColor = "";
|
||||
}
|
||||
});
|
||||
// and then give focus to the input field
|
||||
el.focus();
|
||||
}
|
||||
};
|
||||
|
||||
export default focusDirective;
|
||||
56
Source Code/src/directives/integers-only.js
Normal file
56
Source Code/src/directives/integers-only.js
Normal file
@ -0,0 +1,56 @@
|
||||
const integersOnly = {
|
||||
mounted(el, binding) {
|
||||
|
||||
if (binding.modifiers.upper) {
|
||||
// Convert the displayed field to uppercase with an initial value
|
||||
// (for this to work, the v-model directive must be written before this one in the input field)
|
||||
el.value = el.value.toUpperCase();
|
||||
|
||||
// Simulate an input event to mimic a keyboard keypress
|
||||
// (necessary for the reactive variable linked to the field to be updated)
|
||||
el.dispatchEvent(new Event("input"));
|
||||
}
|
||||
|
||||
el.addEventListener("keydown", (event) => {
|
||||
const numbers = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"];
|
||||
const letters = ["a", "b", "c", "d", "e", "f", "A", "B", "C", "D", "E", "F"];
|
||||
const moves = ["Backspace", "ArrowLeft", "ArrowRight",
|
||||
"Delete", "Tab", "Home", "End"];
|
||||
|
||||
let authorized; // Allowed keys in the input field
|
||||
|
||||
// Allow hexadecimal characters if the hexa modifier is present
|
||||
if (binding.modifiers.hexa) authorized = [...numbers, ...letters, ...moves];
|
||||
else authorized = [...numbers, ...moves];
|
||||
|
||||
// If the key is not allowed, do not take it into account
|
||||
if (!authorized.includes(event.key)) event.preventDefault();
|
||||
|
||||
// Handle the upper modifier
|
||||
if (binding.modifiers.upper) {
|
||||
// If the key is a hexadecimal letter, convert it to uppercase
|
||||
if (letters.includes(event.key)) {
|
||||
const start = el.selectionStart;
|
||||
const end = el.selectionEnd;
|
||||
const text = el.value;
|
||||
|
||||
// Insert the character at the cursor position
|
||||
const newText = text.substring(0, start) + event.key + text.substring(end);
|
||||
// Update the value of the input field (in uppercase)
|
||||
el.value = newText.toUpperCase();
|
||||
// Move the cursor after the inserted character
|
||||
el.setSelectionRange(start + 1, start + 1);
|
||||
|
||||
// Prevent further processing of the key (as it has already been handled above)
|
||||
event.preventDefault();
|
||||
|
||||
// Simulate an input event to mimic a keyboard keypress
|
||||
// (necessary for the reactive variable linked to the field to be updated)
|
||||
el.dispatchEvent(new Event("input"));
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
export default integersOnly;
|
||||
19
Source Code/src/directives/map.js
Normal file
19
Source Code/src/directives/map.js
Normal file
@ -0,0 +1,19 @@
|
||||
import useMap from "../composables/useMap.js"
|
||||
|
||||
const map = {
|
||||
updated(el, binding) {
|
||||
const latitude = binding.value.latitude;
|
||||
const longitude = binding.value.longitude;
|
||||
if (latitude && longitude) {
|
||||
if (el._map) el._map.remove();
|
||||
el._map = useMap(latitude, longitude, el.id);
|
||||
}
|
||||
else if (el._map) {
|
||||
el._map.remove();
|
||||
el._map = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default map;
|
||||
|
||||
28
Source Code/src/directives/max-value.js
Normal file
28
Source Code/src/directives/max-value.js
Normal file
@ -0,0 +1,28 @@
|
||||
const treatment = (el, binding) => {
|
||||
const maxValue = binding.value || 100; // 100 by default
|
||||
const value = el.value || 0; // Value in the field
|
||||
const bold = binding.modifiers.bold;
|
||||
if (value > maxValue) {
|
||||
el.style.color = "red";
|
||||
if (bold) {
|
||||
el.style.fontWeight = "bold";
|
||||
el.style.fontFamily = "arial";
|
||||
}
|
||||
}
|
||||
else {
|
||||
el.style.color = "";
|
||||
el.style.fontWeight = ""; // Removal of "bold"
|
||||
el.style.fontFamily = ""; // Removal of "arial"
|
||||
}
|
||||
}
|
||||
|
||||
const maxValue = {
|
||||
mounted(el, binding) {
|
||||
treatment(el, binding);
|
||||
},
|
||||
updated(el, binding) {
|
||||
treatment(el, binding);
|
||||
},
|
||||
}
|
||||
|
||||
export default maxValue;
|
||||
141
Source Code/src/directives/timer.js
Normal file
141
Source Code/src/directives/timer.js
Normal file
@ -0,0 +1,141 @@
|
||||
const timer = {
|
||||
mounted(el, binding) {
|
||||
const ms = binding.modifiers.ms;
|
||||
const chrono = binding.modifiers.chrono;
|
||||
|
||||
// Initialization of the clock or stopwatch.
|
||||
if (!chrono) {
|
||||
let time = getCurrentTime(ms);
|
||||
el.innerHTML = time;
|
||||
}
|
||||
else {
|
||||
if (!ms) el.innerHTML = "00:00:00";
|
||||
else el.innerHTML = "00:00:00.0";
|
||||
}
|
||||
|
||||
setInterval(()=>{
|
||||
if (!chrono) {
|
||||
let time = getCurrentTime(ms);
|
||||
el.innerHTML = time;
|
||||
}
|
||||
else {
|
||||
const chronoTime = getChronoTime(ms);
|
||||
el.innerHTML = chronoTime;
|
||||
}
|
||||
}, 100);
|
||||
},
|
||||
}
|
||||
|
||||
function getCurrentTime(ms = false) {
|
||||
const now = new Date();
|
||||
const hours = now.getHours().toString().padStart(2, '0');
|
||||
const minutes = now.getMinutes().toString().padStart(2, '0');
|
||||
const seconds = now.getSeconds().toString().padStart(2, '0');
|
||||
let formattedTime = `${hours}:${minutes}:${seconds}`;
|
||||
|
||||
if (ms) {
|
||||
const milliseconds = now.getMilliseconds().toString().slice(0, 1); // Obtaining tenths of a second
|
||||
formattedTime += `.${milliseconds}`;
|
||||
}
|
||||
|
||||
return formattedTime;
|
||||
}
|
||||
|
||||
let startChronoTime = new Date(); // Starting time of the stopwatch
|
||||
|
||||
function getChronoTime(ms = false) {
|
||||
const now = new Date();
|
||||
const elapsedMilliseconds = now.getTime() - startChronoTime.getTime();
|
||||
|
||||
const hours = Math.floor(elapsedMilliseconds / (3600 * 1000));
|
||||
const remainingMilliseconds1 = elapsedMilliseconds % (3600 * 1000);
|
||||
|
||||
const minutes = Math.floor(remainingMilliseconds1 / (60 * 1000));
|
||||
const remainingMilliseconds2 = remainingMilliseconds1 % (60 * 1000);
|
||||
|
||||
const seconds = Math.floor(remainingMilliseconds2 / 1000);
|
||||
|
||||
let formattedTime = `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
|
||||
|
||||
if (ms) {
|
||||
const milliseconds = Math.floor(remainingMilliseconds2 % 1000);
|
||||
const tenthsOfSecond = Math.floor((milliseconds % 1000) / 100);
|
||||
formattedTime += `.${tenthsOfSecond.toString()}`;
|
||||
}
|
||||
|
||||
return formattedTime;
|
||||
}
|
||||
|
||||
export default timer;
|
||||
|
||||
|
||||
|
||||
//~ const timer = {
|
||||
//~ mounted(el, binding) {
|
||||
//~ const ms = binding.modifiers.ms;
|
||||
//~ const chrono = binding.modifiers.chrono;
|
||||
|
||||
//~ // Initialization of the clock or stopwatch.
|
||||
//~ if (!chrono) {
|
||||
//~ let time = getCurrentTime(ms);
|
||||
//~ el.innerHTML = time;
|
||||
//~ }
|
||||
//~ else {
|
||||
//~ if (!ms) el.innerHTML = "00:00:00";
|
||||
//~ else el.innerHTML = "00:00:00.0";
|
||||
//~ }
|
||||
|
||||
//~ setInterval(()=>{
|
||||
//~ if (!chrono) {
|
||||
//~ let time = getCurrentTime(ms);
|
||||
//~ el.innerHTML = time;
|
||||
//~ }
|
||||
//~ else {
|
||||
//~ const chronoTime = getChronoTime(ms);
|
||||
//~ el.innerHTML = chronoTime;
|
||||
//~ }
|
||||
//~ }, 100);
|
||||
//~ },
|
||||
//~ }
|
||||
|
||||
//~ function getCurrentTime(ms = false) {
|
||||
//~ const now = new Date();
|
||||
//~ const hours = now.getHours().toString().padStart(2, '0');
|
||||
//~ const minutes = now.getMinutes().toString().padStart(2, '0');
|
||||
//~ const seconds = now.getSeconds().toString().padStart(2, '0');
|
||||
//~ let formattedTime = `${hours}:${minutes}:${seconds}`;
|
||||
|
||||
//~ if (ms) {
|
||||
//~ const milliseconds = now.getMilliseconds().toString().slice(0, 1); // Obtaining tenths of a second.
|
||||
//~ formattedTime += `.${milliseconds}`;
|
||||
//~ }
|
||||
|
||||
//~ return formattedTime;
|
||||
//~ }
|
||||
|
||||
//~ let startChronoTime = new Date(); // Starting time of the stopwatch.
|
||||
|
||||
//~ function getChronoTime(ms = false) {
|
||||
//~ const now = new Date();
|
||||
//~ const elapsedMilliseconds = now.getTime() - startChronoTime.getTime();
|
||||
|
||||
//~ const hours = Math.floor(elapsedMilliseconds / (3600 * 1000));
|
||||
//~ const remainingMilliseconds1 = elapsedMilliseconds % (3600 * 1000);
|
||||
|
||||
//~ const minutes = Math.floor(remainingMilliseconds1 / (60 * 1000));
|
||||
//~ const remainingMilliseconds2 = remainingMilliseconds1 % (60 * 1000);
|
||||
|
||||
//~ const seconds = Math.floor(remainingMilliseconds2 / 1000);
|
||||
|
||||
//~ let formattedTime = `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
|
||||
|
||||
//~ if (ms) {
|
||||
//~ const milliseconds = Math.floor(remainingMilliseconds2 % 1000);
|
||||
//~ const tenthsOfSecond = Math.floor((milliseconds % 1000) / 100);
|
||||
//~ formattedTime += `.${tenthsOfSecond.toString()}`;
|
||||
//~ }
|
||||
|
||||
//~ return formattedTime;
|
||||
//~ }
|
||||
|
||||
//~ export default timer;
|
||||
12
Source Code/src/main.js
Normal file
12
Source Code/src/main.js
Normal file
@ -0,0 +1,12 @@
|
||||
import { createApp } from 'vue';
|
||||
import App from './App.vue';
|
||||
import directives from "./directives.js"
|
||||
|
||||
const app = createApp(App);
|
||||
|
||||
for (let name in directives) {
|
||||
// Creation of the directive name within the application
|
||||
app.directive(name, directives[name]);
|
||||
}
|
||||
|
||||
app.mount('#app');
|
||||
Loading…
x
Reference in New Issue
Block a user