Replace font awesome font with svg

This commit is contained in:
2026-04-26 17:01:50 +02:00
parent dc411cc532
commit f2af936e60
73 changed files with 497 additions and 12976 deletions
+40 -40
View File
@@ -9,20 +9,20 @@ export default {
SpotButton,
AdminInput
},
inject: ['api', 'lang'],
data() {
return {
elems: {},
feedbacks: [],
saveTimer: null
};
},
beforeUnmount() {
if(this.saveTimer) clearTimeout(this.saveTimer);
},
mounted() {
this.setProjects();
},
inject: ['api', 'lang'],
data() {
return {
elems: {},
feedbacks: [],
saveTimer: null
};
},
beforeUnmount() {
if(this.saveTimer) clearTimeout(this.saveTimer);
},
mounted() {
this.setProjects();
},
methods: {
l(id) {
return this.lang.get(id);
@@ -39,7 +39,7 @@ export default {
this.feedbacks.push({type:sType, msg:sMsg});
},
async setProjects() {
let aoElemTypes = await this.api.get('admin_get');
let aoElemTypes = await this.api.get('admin_get');
for(const [sType, aoElems] of Object.entries(aoElemTypes)) {
this.elems[sType] = {};
@@ -50,7 +50,7 @@ export default {
}
},
createElem(sType) {
this.api.get('admin_create', {type: sType})
this.api.get('admin_create', {type: sType})
.then((aoNewElemTypes) => {
for(const [sType, aoNewElems] of Object.entries(aoNewElemTypes)) {
for(const [iKey, oNewElem] of Object.entries(aoNewElems)) {
@@ -68,20 +68,20 @@ export default {
id: oElem.id
};
this.api.get('admin_delete', asInputs)
.then((asData) => {
delete this.elems[asInputs.type][asInputs.id];
this.addFeedback('success', this.lang.get('admin_delete_success'), asInputs);
})
.catch((sError) => {
this.addFeedback('error', sError, asInputs);
});
},
updateElem(oElem, oEvent) {
if(this.saveTimer) clearTimeout(this.saveTimer);
let sOldVal = this.elems[oElem.type][oElem.id][oEvent.target.name];
let sNewVal = oEvent.target.value;
this.api.get('admin_delete', asInputs)
.then((asData) => {
delete this.elems[asInputs.type][asInputs.id];
this.addFeedback('success', this.lang.get('admin_delete_success'), asInputs);
})
.catch((sError) => {
this.addFeedback('error', sError, asInputs);
});
},
updateElem(oElem, oEvent) {
if(this.saveTimer) clearTimeout(this.saveTimer);
let sOldVal = this.elems[oElem.type][oElem.id][oEvent.target.name];
let sNewVal = oEvent.target.value;
if(sOldVal != sNewVal) {
let asInputs = {
type: oElem.type,
@@ -90,7 +90,7 @@ export default {
value: sNewVal
};
this.api.get('admin_set', asInputs)
this.api.get('admin_set', asInputs)
.then((asData) => {
this.elems[oElem.type][oElem.id][oEvent.target.name] = sNewVal;
this.addFeedback('success', this.lang.get('admin_save_success'), asInputs);
@@ -100,13 +100,13 @@ export default {
this.addFeedback('error', sError, asInputs);
});
}
},
queue(oElem, oEvent) {
if(this.saveTimer) clearTimeout(this.saveTimer);
this.saveTimer = setTimeout(() => {this.updateElem(oElem, oEvent);}, 2000);
},
},
queue(oElem, oEvent) {
if(this.saveTimer) clearTimeout(this.saveTimer);
this.saveTimer = setTimeout(() => {this.updateElem(oElem, oEvent);}, 2000);
},
updateProject() {
this.api.get('update_project')
this.api.get('update_project')
.then((asData, sMsg) => {this.addFeedback('success', sMsg, {'update':'project'});})
.catch((sMsg) => {this.addFeedback('error', sMsg, {'update':'project'});});
}
@@ -138,7 +138,7 @@ export default {
<td><AdminInput :type="'text'" :name="'codename'" :elem="project" /></td>
<td><AdminInput :type="'date'" :name="'active_from'" :elem="project" /></td>
<td><AdminInput :type="'date'" :name="'active_to'" :elem="project" /></td>
<td><SpotButton :icon="'close fa-lg'" @click="deleteElem(project)" /></td>
<td><SpotButton :icon="'close'" iconSize="lg" @click="deleteElem(project)" /></td>
</tr>
</tbody>
</table>
@@ -168,7 +168,7 @@ export default {
<td>{{ feed.name }}</td>
<td>{{ feed.status }}</td>
<td>{{ feed.last_update }}</td>
<td><SpotButton :icon="'close fa-lg'" @click="deleteElem(feed)" /></td>
<td><SpotButton :icon="'close'" iconSize="lg" @click="deleteElem(feed)" /></td>
</tr>
</tbody>
</table>
@@ -215,7 +215,7 @@ export default {
<td>{{ user.language }}</td>
<td>{{ user.timezone }}</td>
<td><AdminInput :type="'number'" :name="'clearance'" :elem="user" /></td>
<td><SpotButton :icon="'close fa-lg'" @click="deleteElem(user)" /></td>
<td><SpotButton :icon="'close'" iconSize="lg" @click="deleteElem(user)" /></td>
</tr>
</tbody>
</table>
+17 -11
View File
@@ -158,7 +158,7 @@ export default {
initLightbox() {
lightbox.option({
alwaysShowNavOnTouchDevices: true,
albumLabel: '<i class="fa fa-fw fa-lg fa-media push"></i> %1 / %2',
albumLabel: 'Media %1 / %2',
fadeDuration: 300,
imageFadeDuration: 400,
positionFromTop: 0,
@@ -303,7 +303,7 @@ export default {
this.markers.messages.forEach(msg => {
const el = document.createElement('div');
const app = createApp(SpotIconStack, {iconMain: 'message', iconSub:'message-in', iconSubClasses:'fa-rotate-270'});
const app = createApp(SpotIconStack, {iconMain: 'message', iconSub:'message-in', iconSubRotation: 270});
app.mount(el);
new Marker({element: el, anchor: 'bottom'})
@@ -646,7 +646,9 @@ export default {
<div id="projects" :class="projectClasses">
<div id="background"></div>
<div id="submap">
<div class="loader fa fa-fw fa-map flicker"></div>
<div class="loader">
<SpotIcon :icon="'map'" :classes="'flicker'" fixed-width />
</div>
</div>
<div id="map"></div>
<div id="settings" class="map-container map-container-left" ref="settings">
@@ -660,7 +662,7 @@ export default {
<div class="settings-sections">
<Simplebar id="settings-sections-scrollbox">
<div class="settings-section">
<h1><SpotIcon :icon="'project'" :classes="'fa-fw'" :text="lang.get('hikes')" /></h1>
<h1><SpotIcon :icon="'project'" fixed-width :text="lang.get('hikes')" /></h1>
<div class="settings-section-body">
<div class="radio" v-for="project in projects" :key="'project-'+project.id">
<input type="radio" :id="'project-'+project.id" :value="project.codename" v-model="$parent.hash.items[0]" />
@@ -674,7 +676,7 @@ export default {
</div>
</div>
<div class="settings-section">
<h1><SpotIcon :icon="'map'" :classes="'fa-fw'" :text="lang.get('maps')" /></h1>
<h1><SpotIcon :icon="'map'" fixed-width :text="lang.get('maps')" /></h1>
<div class="settings-section-body">
<div class="radio" v-for="bm in baseMaps" :key="'map-'+bm.id_map">
<input type="radio" :id="'map-'+bm.id_map" :value="bm.codename" v-model="baseMap" />
@@ -683,9 +685,11 @@ export default {
</div>
</div>
<div class="settings-section newsletter">
<h1><SpotIcon :icon="'newsletter'" :classes="'fa-fw'" :text="lang.get('newsletter')" /></h1>
<input type="email" name="email" id="email" :placeholder="lang.get('nl_email_placeholder')" v-model="user.email" :disabled="nlLoading || subscribed" />
<SpotButton id="nl_btn" :classes="nlClasses" :title="lang.get('nl_'+nlAction)" @click="manageSubs" />
<h1><SpotIcon :icon="'newsletter'" fixed-width :text="lang.get('newsletter')" /></h1>
<div class="newsletter-form">
<input type="email" name="email" id="email" :placeholder="lang.get('nl_email_placeholder')" v-model="user.email" :disabled="nlLoading || subscribed" />
<SpotButton id="nl_btn" :classes="nlClasses" :title="lang.get('nl_'+nlAction)" :icon="nlAction" @click="manageSubs" />
</div>
<div id="settings-feedback" class="feedback">
<p v-for="feedback in nlFeedbacks" :class="feedback.type">
<SpotIcon :icon="feedback.type" :text="feedback.msg" />
@@ -694,9 +698,11 @@ export default {
{{ lang.get(subscribed?'nl_subscribed_desc':'nl_unsubscribed_desc') }}
</div>
<div class="settings-section admin" v-if="user.hasClearance(consts.clearances.admin)">
<h1><SpotIcon :icon="'admin fa-fw'" :text="lang.get('admin')" /></h1>
<a class="button" href="#admin"><SpotIcon :icon="'config'" :text="lang.get('admin_config')" /></a>
<a class="button" href="#upload"><SpotIcon :icon="'upload'" :text="lang.get('admin_upload')" /></a>
<h1><SpotIcon :icon="'admin'" fixed-width :text="lang.get('admin')" /></h1>
<div class="admin-actions">
<a class="button" href="#admin"><SpotIcon :icon="'config'" :text="lang.get('admin_config')" /></a>
<a class="button" href="#upload"><SpotIcon :icon="'upload'" :text="lang.get('admin_upload')" /></a>
</div>
</div>
</Simplebar>
</div>
+1 -1
View File
@@ -55,7 +55,7 @@ export default {
</a>
<div style="display:none">
<span v-if="options.comment" ref="comment" class="lb-caption-line comment desktop">
<spotIcon :icon="'post'" :classes="'fa-lg fa-fw push'" :text-classes="'comment-text'" :text="options.comment" />
<spotIcon :icon="'post'" fixed-width size="lg" :text-classes="'comment-text'" :text="options.comment" />
</span>
<span ref="postedon" class="lb-caption-line">
<projectRelTime :icon="'upload'" :localTime="options.posted_on_formatted_time_local" :siteTime="options.posted_on_formatted_time" :offset="options.posted_on_day_offset" />
+4 -4
View File
@@ -31,24 +31,24 @@ export default {
<div :class="'info-window '+type">
<div class="header" v-if="type=='message'">
<h1>
<spotIcon :icon="'message'" :classes="'fa-lg'" :text="lang.get('post_message')+' '+lang.get('counter', options.displayed_id)" />
<spotIcon :icon="'message'" size="lg" :text="lang.get('post_message')+' '+lang.get('counter', options.displayed_id)" />
<span class="message-type">({{ options.type }})</span>
</h1>
<div class="separator"></div>
</div>
<p class="coordinates">
<spotIcon :icon="'coords'" :classes="'fa-fw fa-lg'" :margin="true" />
<spotIcon :icon="'coords'" fixed-width size="lg" :margin="true" />
<projectMapLink :options="options" />
</p>
<p class="altitude" v-if="options.altitude">
<spotIcon :icon="'altitude'" :classes="'fa-fw fa-lg'" :text="options.altitude+'m'" />
<spotIcon :icon="'altitude'" fixed-width size="lg" :text="options.altitude+'m'" />
</p>
<p class="time">
<projectRelTime :icon="timeIcon" :localTime="options.formatted_time_local" :siteTime="options.formatted_time" :offset="options.day_offset" />
<span v-if="project.mode==consts.modes.blog"> ({{ options.relative_time }})</span>
</p>
<p class="weather" v-if="options.weather_icon && options.weather_icon!='unknown'" :title="options.weather_cond==''?'':lang.get(options.weather_cond)">
<spotIcon :icon="options.weather_icon" :classes="'fa-fw fa-lg'" :text="options.weather_temp+'°C'" />
<spotIcon :icon="options.weather_icon" fixed-width size="lg" :text="options.weather_temp+'°C'" />
</p>
<div v-if="medias.length > 0">
<div class="separator"></div>
+15 -10
View File
@@ -32,6 +32,7 @@
anchorTitle: this.lang.get('copy_to_clipboard'),
anchorIcon: 'link',
popupRequested: false,
mouseOverDrill: false,
postMessage: '',
sending: false,
focusZoomLevel: 15
@@ -51,6 +52,9 @@
displayedId() {
return this.options.displayed_id?(this.lang.get('counter', this.options.displayed_id)):'';
},
drillMainIcon() {
return this.mouseOverDrill?'drill-message':'marker';
},
anchorLink() {
return '#'+[this.hash.page, this.hash.items[0], this.options.type, this.options.id].join(this.consts.hash_sep);
},
@@ -139,11 +143,11 @@
<template>
<div :class="postClass" :id="postId">
<div class="header">
<div class="header" v-if="!options.headerless">
<div class="index">
<spotIcon :icon="subType" :text="displayedId" />
<a v-if="anchorVisible" class="link desktop" @click="copyAnchor" ref="anchor" :href="anchorLink" :title="anchorTitle">
<spotIcon :icon="anchorIcon" />
<spotIcon :icon="anchorIcon" :classes="'push-left'" />
</a>
</div>
<div class="time" @mouseleave="mouseOverHeader = false" @mouseover="mouseOverHeader = true" :title="timeDiff?lang.get('local_time', absTimeLocal):''">
@@ -156,19 +160,18 @@
<div class="body">
<div v-if="options.type == 'message'" class="body-box" @mouseenter="openMarkerPopup" @mouseleave="closeMarkerPopup">
<p>
<spotIcon :icon="'coords'" :classes="'push'" />
<spotIcon :icon="'coords'" :classes="'push'" size="lg" />
<projectMapLink :options="options" />
</p>
<p v-if="timeDiff">
<projectRelTime :icon="'time'" :iconClasses="'push'" :localTime="absTimeLocal" :siteTime="options.formatted_time" :offset="options.day_offset" />
</p>
<a class="drill" @click.prevent="executeMainAction">
<a class="drill" @click.prevent="executeMainAction" @mouseenter="mouseOverDrill = true" @mouseleave="mouseOverDrill = false">
<span v-if="options.weather_icon && options.weather_icon!='unknown'" class="weather clickable" :title="lang.get(options.weather_cond)">
<spotIcon :icon="options.weather_icon" />
<span class="temperature">{{ options.weather_temp+'°C' }}</span>
<spotIcon :icon="options.weather_icon" :text="Math.round(options.weather_temp)+'°C'" text-classes="temperature" />
</span>
<img class="staticmap clickable" :title="lang.get('click_zoom')" :src="options.static_img_url" />
<spotIconStack :mainClasses="'message drill-icon'" :iconMain="'marker'" :iconSub="'footprint'" :iconSubClasses="'fa-rotate-270'" />
<spotIconStack :mainClasses="'message drill-icon'" :iconMain="drillMainIcon" :iconSub="'footprint'" :icon-sub-transform="'rotate-270'" />
</a>
</div>
<div v-else-if="options.type == 'media'" class="body-box">
@@ -182,10 +185,12 @@
<span>{{ options.formatted_name }}</span>
</p>
</div>
<div v-else-if="options.type == 'poster'">
<div v-else-if="options.type == 'poster'" class="poster-form">
<textarea ref="post" name="post" :placeholder="lang.get('post_message')" class="autoExpand" rows="1" v-model="postMessage"></textarea>
<input type="text" name="name" :placeholder="lang.get('post_name')" v-model="user.name" />
<spotButton name="submit" :aria-label="lang.get('send')" :title="lang.get('send')" :icon="'send'" @click="send()" :iconClasses="sending?'flicker':''" />
<div class="poster-actions">
<input type="text" name="name" :placeholder="lang.get('post_name')" v-model="user.name" />
<spotButton name="submit" :aria-label="lang.get('send')" :title="lang.get('send')" :icon="'send'" @click="send()" :iconClasses="sending?'flicker':''" />
</div>
</div>
<div v-else-if="options.type == 'archived'">
<p><spotIcon :icon="'success'" /></p>
+1 -4
View File
@@ -17,15 +17,12 @@ export default {
computed: {
title() {
if(this.localTime != this.siteTime) return this.lang.get('your_time', this.siteTime.slice(-5)) + ((this.offset != '0')?' ('+this.lang.get('unit_d')+this.offset+')':'');
},
spotIconClasses() {
return this.iconClasses || 'fa-fw fa-lg';
}
}
}
</script>
<template>
<spotIcon v-if="icon" :icon="icon" :classes="spotIconClasses" :title="title" :text="localTime" />
<spotIcon v-if="icon" :icon="icon" :classes="iconClasses" :title="title" :text="localTime" fixed-width size="lg" />
<span v-else :class="classes" :title="title">{{ localTime }}</span>
</template>
+5 -4
View File
@@ -6,13 +6,14 @@ export default {
SpotIcon
},
props: {
classes: String,
classes: String,
text: String,
icon: String,
iconClasses: String
iconClasses: String,
iconSize: String
}
}
</script>
<template>
<button :class="classes"><SpotIcon :icon="icon" :text="text" :classes="iconClasses" /></button>
</template>
<button :class="classes"><SpotIcon :icon="icon" :text="text" :classes="iconClasses" :size="iconSize" /></button>
</template>
+39 -5
View File
@@ -1,24 +1,58 @@
<script>
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
import { getIcon } from '../scripts/icons.js';
export default {
components: {
FontAwesomeIcon
},
props: {
icon: String,
fixedWidth: Boolean,
rotation: [Number, String],
size: String,
classes: String,
title: String,
text: String,
margin: Boolean,
classes: String,
textClasses: String
textClasses: String,
transform: String
},
computed: {
classNames() {
return 'fa fa-'+this.icon+((this.margin || this.hasText)?' push':'')+(this.classes?' '+this.classes:'')
return [
'spot-icon',
this.icon,
...(this.classes || '').split(/\s+/).filter(Boolean),
...(this.margin || this.hasText ? ['push'] : [])
].filter(Boolean).join(' ');
},
resolvedFixedWidth() {
return this.fixedWidth || null;
},
hasText() {
return this.text && this.text != '';
},
iconDefinition() {
return getIcon(this.icon);
},
resolvedRotation() {
return this.rotation || null;
},
resolvedSize() {
return this.size || null;
},
resolvedTransform() {
return this.transform || null;
}
}
}
</script>
<template>
<span :title="title"><i :class="classNames"></i><span v-if="hasText" :class="textClasses">{{ text }}</span></span>
</template>
<span :title="title" v-if="title || hasText">
<FontAwesomeIcon :icon="iconDefinition" :class="classNames" :fixed-width="resolvedFixedWidth" :rotation="resolvedRotation" :size="resolvedSize" :transform="resolvedTransform" />
<span v-if="hasText" :class="textClasses">{{ text }}</span>
</span>
<FontAwesomeIcon v-else :icon="iconDefinition" :class="classNames" :fixed-width="resolvedFixedWidth" :rotation="resolvedRotation" :size="resolvedSize" :transform="resolvedTransform" />
</template>
+27 -4
View File
@@ -1,20 +1,41 @@
<script>
import { FontAwesomeLayers } from '@fortawesome/vue-fontawesome';
import spotIcon from './spotIcon.vue';
export default {
components: {
FontAwesomeLayers,
spotIcon
},
props: {
mainClasses: String,
iconMain: String,
iconMainClasses: String,
iconMainTransform: String,
iconSub: String,
iconSubClasses: String,
iconSubTransform: String
},
computed: {
mainClass() {
return 'icon-stack fa-stack clickable'+(this.mainClasses?' '+this.mainClasses:'');
return 'spot-icon-stack clickable'+(this.mainClasses?' '+this.mainClasses:'');
},
iconMainClass() {
return this.mergeClasses('main clickable', this.iconMainClasses);
},
iconSubClass() {
return this.mergeClasses('sub', this.iconSubClasses);
},
iconSubTransformValue() {
return this.mergeClasses('shrink-8 up-2', this.iconSubTransform);
}
},
methods: {
mergeClasses(sClassesA, sClassesB) {
return [
...(sClassesA || '').split(/\s+/),
...(sClassesB || '').split(/\s+/)
].filter(Boolean).join(' ');
}
}
}
@@ -22,7 +43,9 @@ export default {
<template>
<span :class="mainClass">
<spotIcon :icon="iconMain" :classes="'fa-stack-2x clickable'+(iconMainClasses?' '+iconMainClasses:'')" />
<spotIcon :icon="iconSub" :classes="'fa-stack-1x'+(iconSubClasses?' '+iconSubClasses:'')" />
<FontAwesomeLayers>
<spotIcon :icon="iconMain" :classes="iconMainClass" :transform="iconMainTransform" />
<spotIcon :icon="iconSub" :classes="iconSubClass" :transform="iconSubTransformValue" />
</FontAwesomeLayers>
</span>
</template>
</template>