Initial commit
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
# Auto detect text files and perform LF normalization
|
||||
* text=auto
|
||||
@@ -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
@@ -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.
|
||||
|
||||
|
||||
@@ -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.
|
||||
@@ -0,0 +1,11 @@
|
||||
<script setup>
|
||||
|
||||
import MyCounter from "./components/MyCounter.vue"
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
<MyCounter />
|
||||
|
||||
</template>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 6.7 KiB |
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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');
|
||||
Reference in New Issue
Block a user