diff options
author | Bastien Wirtz <bastien.wirtz@gmail.com> | 2021-10-06 22:55:09 +0200 |
---|---|---|
committer | Bastien Wirtz <bastien.wirtz@gmail.com> | 2021-10-06 22:55:09 +0200 |
commit | 2ca4faad9cb336ac8904bbc775fdcc2a12731b90 (patch) | |
tree | 76f26d0503c5f86e6240e3d84fa4e793cc4c44ed | |
parent | c7dc6bfd0d73f803914092593d440d8b27e2c851 (diff) | |
download | homer-2ca4faad9cb336ac8904bbc775fdcc2a12731b90.tar.gz homer-2ca4faad9cb336ac8904bbc775fdcc2a12731b90.tar.zst homer-2ca4faad9cb336ac8904bbc775fdcc2a12731b90.zip |
Extendable base service for easier development.
-rw-r--r-- | CONTRIBUTING.md | 11 | ||||
-rw-r--r-- | docs/development.md | 45 | ||||
-rw-r--r-- | src/components/Service.vue | 5 | ||||
-rw-r--r-- | src/components/services/AdGuardHome.vue | 60 | ||||
-rw-r--r-- | src/components/services/Generic.vue | 33 | ||||
-rw-r--r-- | src/components/services/Ping.vue | 49 |
6 files changed, 104 insertions, 99 deletions
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f29b7d8..de893b6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md | |||
@@ -12,10 +12,6 @@ UX and usability. If you are looking for a full featured dashboard, there is ton | |||
12 | - Configuration is stored in a simple config file, avoiding the need for a backend/database while making possible to use versioning or [config template](https://docs.ansible.com/ansible/latest/user_guide/playbooks_templating.html). | 12 | - Configuration is stored in a simple config file, avoiding the need for a backend/database while making possible to use versioning or [config template](https://docs.ansible.com/ansible/latest/user_guide/playbooks_templating.html). |
13 | - Only modern browsers are supported, feel free to use any JS features without any polyfill as soon as the latest version of the major browsers supports them. | 13 | - Only modern browsers are supported, feel free to use any JS features without any polyfill as soon as the latest version of the major browsers supports them. |
14 | 14 | ||
15 | ### Roadmap | ||
16 | |||
17 | If you want to know more about the project direction or looking for something to work on, checkout the [roadmap](https://github.com/bastienwirtz/homer#Roadmap)! | ||
18 | Feel free to open an issue if you have any question. | ||
19 | 15 | ||
20 | # Ground Rules | 16 | # Ground Rules |
21 | 17 | ||
@@ -40,8 +36,9 @@ feel free to open an issue to present your idea. | |||
40 | ### How to submit a contribution | 36 | ### How to submit a contribution |
41 | 37 | ||
42 | The general process to submit a contribution is as follow: | 38 | The general process to submit a contribution is as follow: |
43 | 1. Create your own fork of the code | 39 | 1. Take a look to the [development guideline](https://github.com/bastienwirtz/homer/blob/main/docs/development.md). |
44 | 2. Do the changes in your fork | 40 | 2. Create your own fork of the code |
45 | 3. Make sure to fill the [pull request description](https://github.com/bastienwirtz/homer/blob/main/.github/PULL_REQUEST_TEMPLATE.md) properly. | 41 | 3. Do the changes in your fork |
42 | 4. Make sure to fill the [pull request description](https://github.com/bastienwirtz/homer/blob/main/.github/PULL_REQUEST_TEMPLATE.md) properly. | ||
46 | 43 | ||
47 | ### Happy coding :metal: | 44 | ### Happy coding :metal: |
diff --git a/docs/development.md b/docs/development.md index 5e432f1..a22ae0b 100644 --- a/docs/development.md +++ b/docs/development.md | |||
@@ -1,5 +1,7 @@ | |||
1 | # Development | 1 | # Development |
2 | 2 | ||
3 | If you want to contribute to Homer, please read the [contributing guidelines](https://github.com/bastienwirtz/homer/blob/main/CONTRIBUTING.md) first. | ||
4 | |||
3 | ```sh | 5 | ```sh |
4 | # Using yarn (recommended) | 6 | # Using yarn (recommended) |
5 | yarn install | 7 | yarn install |
@@ -10,6 +12,49 @@ npm install | |||
10 | npm run serve | 12 | npm run serve |
11 | ``` | 13 | ``` |
12 | 14 | ||
15 | ## Custom services | ||
16 | |||
17 | Custom services are small VueJs component (see `src/components/services/`) that add little features to a classic, "static", dashboard item. It should be very simple. | ||
18 | A dashboard can contain a lot of items, so performance is very important. | ||
19 | |||
20 | The [`Generic`](https://github.com/bastienwirtz/homer/blob/main/src/components/services/Generic.vue) service provides a typical card layout which | ||
21 | you can extend to add specific features. Unless you want a completely different design, extended the generic service is the recommended way. It gives you 3 [slots](https://vuejs.org/v2/guide/components-slots.html#Named-Slots) to extend: `icon`, `content` and `indicator`. | ||
22 | Each one is **optional**, and will display the usual information if omitted. | ||
23 | |||
24 | Each service must implement the `item` [property](https://vuejs.org/v2/guide/components-props.html) and bind it the Generic component if used. | ||
25 | |||
26 | ### Skeleton | ||
27 | ```Vue | ||
28 | <template> | ||
29 | <Generic :item="item"> | ||
30 | <template #icon> | ||
31 | <!-- left area containing the icon --> | ||
32 | </template> | ||
33 | <template #content> | ||
34 | <!-- main area containing the title, subtitle, ... --> | ||
35 | </template> | ||
36 | <template #indicator> | ||
37 | <!-- top right area, empty by default --> | ||
38 | </template> | ||
39 | </Generic> | ||
40 | </template> | ||
41 | |||
42 | <script> | ||
43 | import Generic from "./Generic.vue"; | ||
44 | |||
45 | export default { | ||
46 | name: "MyNewService", | ||
47 | props: { | ||
48 | item: Object, | ||
49 | }, | ||
50 | components: { | ||
51 | Generic, | ||
52 | } | ||
53 | }; | ||
54 | </script> | ||
55 | ``` | ||
56 | |||
57 | |||
13 | ## Themes | 58 | ## Themes |
14 | 59 | ||
15 | Themes are meant to be simple customization (written in [scss](https://sass-lang.com/documentation/syntax)). | 60 | Themes are meant to be simple customization (written in [scss](https://sass-lang.com/documentation/syntax)). |
diff --git a/src/components/Service.vue b/src/components/Service.vue index 8686759..39a9ac4 100644 --- a/src/components/Service.vue +++ b/src/components/Service.vue | |||
@@ -7,16 +7,13 @@ import Generic from "./services/Generic.vue"; | |||
7 | 7 | ||
8 | export default { | 8 | export default { |
9 | name: "Service", | 9 | name: "Service", |
10 | components: { | ||
11 | Generic, | ||
12 | }, | ||
13 | props: { | 10 | props: { |
14 | item: Object, | 11 | item: Object, |
15 | }, | 12 | }, |
16 | computed: { | 13 | computed: { |
17 | component() { | 14 | component() { |
18 | const type = this.item.type || "Generic"; | 15 | const type = this.item.type || "Generic"; |
19 | if (type == "Generic") { | 16 | if (type === "Generic") { |
20 | return Generic; | 17 | return Generic; |
21 | } | 18 | } |
22 | return () => import(`./services/${type}.vue`); | 19 | return () => import(`./services/${type}.vue`); |
diff --git a/src/components/services/AdGuardHome.vue b/src/components/services/AdGuardHome.vue index 61d4bed..16881fa 100644 --- a/src/components/services/AdGuardHome.vue +++ b/src/components/services/AdGuardHome.vue | |||
@@ -1,49 +1,35 @@ | |||
1 | <template> | 1 | <template> |
2 | <div> | 2 | <Generic :item="item"> |
3 | <div class="card" :class="item.class"> | 3 | <template #content> |
4 | <a :href="item.url" :target="item.target" rel="noreferrer"> | 4 | <p class="title is-4">{{ item.name }}</p> |
5 | <div class="card-content"> | 5 | <p class="subtitle is-6"> |
6 | <div class="media"> | 6 | <template v-if="item.subtitle"> |
7 | <div v-if="item.logo" class="media-left"> | 7 | {{ item.subtitle }} |
8 | <figure class="image is-48x48"> | 8 | </template> |
9 | <img :src="item.logo" :alt="`${item.name} logo`" /> | 9 | <template v-else-if="stats"> |
10 | </figure> | 10 | {{ percentage }}% blocked |
11 | </div> | 11 | </template> |
12 | <div v-if="item.icon" class="media-left"> | 12 | </p> |
13 | <figure class="image is-48x48"> | 13 | </template> |
14 | <i style="font-size: 35px" :class="['fa-fw', item.icon]"></i> | 14 | <template #indicator> |
15 | </figure> | 15 | <div class="status" :class="protection"> |
16 | </div> | 16 | {{ protection }} |
17 | <div class="media-content"> | 17 | </div> |
18 | <p class="title is-4">{{ item.name }}</p> | 18 | </template> |
19 | <p class="subtitle is-6"> | 19 | </Generic> |
20 | <template v-if="item.subtitle"> | ||
21 | {{ item.subtitle }} | ||
22 | </template> | ||
23 | <template v-else-if="stats"> | ||
24 | {{ percentage }}% blocked | ||
25 | </template> | ||
26 | </p> | ||
27 | </div> | ||
28 | <div class="status" :class="protection"> | ||
29 | {{ protection }} | ||
30 | </div> | ||
31 | </div> | ||
32 | <div class="tag" :class="item.tagstyle" v-if="item.tag"> | ||
33 | <strong class="tag-text">#{{ item.tag }}</strong> | ||
34 | </div> | ||
35 | </div> | ||
36 | </a> | ||
37 | </div> | ||
38 | </div> | ||
39 | </template> | 20 | </template> |
40 | 21 | ||
41 | <script> | 22 | <script> |
23 | import Generic from "./Generic.vue"; | ||
24 | |||
42 | export default { | 25 | export default { |
43 | name: "AdGuardHome", | 26 | name: "AdGuardHome", |
44 | props: { | 27 | props: { |
45 | item: Object, | 28 | item: Object, |
46 | }, | 29 | }, |
30 | components: { | ||
31 | Generic, | ||
32 | }, | ||
47 | data: () => { | 33 | data: () => { |
48 | return { | 34 | return { |
49 | status: null, | 35 | status: null, |
diff --git a/src/components/services/Generic.vue b/src/components/services/Generic.vue index 08bd3f6..af65a8c 100644 --- a/src/components/services/Generic.vue +++ b/src/components/services/Generic.vue | |||
@@ -8,22 +8,27 @@ | |||
8 | <a :href="item.url" :target="item.target" rel="noreferrer"> | 8 | <a :href="item.url" :target="item.target" rel="noreferrer"> |
9 | <div class="card-content"> | 9 | <div class="card-content"> |
10 | <div :class="mediaClass"> | 10 | <div :class="mediaClass"> |
11 | <div v-if="item.logo" class="media-left"> | 11 | <slot name="icon"> |
12 | <figure class="image is-48x48"> | 12 | <div v-if="item.logo" class="media-left"> |
13 | <img :src="item.logo" :alt="`${item.name} logo`" /> | 13 | <figure class="image is-48x48"> |
14 | </figure> | 14 | <img :src="item.logo" :alt="`${item.name} logo`" /> |
15 | </div> | 15 | </figure> |
16 | <div v-if="item.icon" class="media-left"> | 16 | </div> |
17 | <figure class="image is-48x48"> | 17 | <div v-if="item.icon" class="media-left"> |
18 | <i style="font-size: 35px" :class="['fa-fw', item.icon]"></i> | 18 | <figure class="image is-48x48"> |
19 | </figure> | 19 | <i style="font-size: 35px" :class="['fa-fw', item.icon]"></i> |
20 | </div> | 20 | </figure> |
21 | </div> | ||
22 | </slot> | ||
21 | <div class="media-content"> | 23 | <div class="media-content"> |
22 | <p class="title is-4">{{ item.name }}</p> | 24 | <slot name="content"> |
23 | <p class="subtitle is-6" v-if="item.subtitle"> | 25 | <p class="title is-4">{{ item.name }}</p> |
24 | {{ item.subtitle }} | 26 | <p class="subtitle is-6" v-if="item.subtitle"> |
25 | </p> | 27 | {{ item.subtitle }} |
28 | </p> | ||
29 | </slot> | ||
26 | </div> | 30 | </div> |
31 | <slot name="indicator" class="indicator"></slot> | ||
27 | </div> | 32 | </div> |
28 | <div class="tag" :class="item.tagstyle" v-if="item.tag"> | 33 | <div class="tag" :class="item.tagstyle" v-if="item.tag"> |
29 | <strong class="tag-text">#{{ item.tag }}</strong> | 34 | <strong class="tag-text">#{{ item.tag }}</strong> |
diff --git a/src/components/services/Ping.vue b/src/components/services/Ping.vue index e693af4..6fd3ec5 100644 --- a/src/components/services/Ping.vue +++ b/src/components/services/Ping.vue | |||
@@ -1,46 +1,24 @@ | |||
1 | <template> | 1 | <template> |
2 | <div> | 2 | <Generic :item="item"> |
3 | <div class="card" :class="item.class"> | 3 | <template #indicator> |
4 | <a :href="item.url" :target="item.target" rel="noreferrer"> | 4 | <div v-if="status" class="status" :class="status"> |
5 | <div class="card-content"> | 5 | {{ status }} |
6 | <div class="media"> | 6 | </div> |
7 | <div v-if="item.logo" class="media-left"> | 7 | </template> |
8 | <figure class="image is-48x48"> | 8 | </Generic> |
9 | <img :src="item.logo" :alt="`${item.name} logo`" /> | ||
10 | </figure> | ||
11 | </div> | ||
12 | <div v-if="item.icon" class="media-left"> | ||
13 | <figure class="image is-48x48"> | ||
14 | <i style="font-size: 35px" :class="['fa-fw', item.icon]"></i> | ||
15 | </figure> | ||
16 | </div> | ||
17 | <div class="media-content"> | ||
18 | <p class="title is-4">{{ item.name }}</p> | ||
19 | <p class="subtitle is-6"> | ||
20 | <template v-if="item.subtitle"> | ||
21 | {{ item.subtitle }} | ||
22 | </template> | ||
23 | </p> | ||
24 | </div> | ||
25 | <div v-if="status" class="status" :class="status"> | ||
26 | {{ status }} | ||
27 | </div> | ||
28 | </div> | ||
29 | <div class="tag" :class="item.tagstyle" v-if="item.tag"> | ||
30 | <strong class="tag-text">#{{ item.tag }}</strong> | ||
31 | </div> | ||
32 | </div> | ||
33 | </a> | ||
34 | </div> | ||
35 | </div> | ||
36 | </template> | 9 | </template> |
37 | 10 | ||
38 | <script> | 11 | <script> |
12 | import Generic from "./Generic.vue"; | ||
13 | |||
39 | export default { | 14 | export default { |
40 | name: "Ping", | 15 | name: "Ping", |
41 | props: { | 16 | props: { |
42 | item: Object, | 17 | item: Object, |
43 | }, | 18 | }, |
19 | components: { | ||
20 | Generic, | ||
21 | }, | ||
44 | data: () => ({ | 22 | data: () => ({ |
45 | status: null, | 23 | status: null, |
46 | }), | 24 | }), |
@@ -70,9 +48,6 @@ export default { | |||
70 | </script> | 48 | </script> |
71 | 49 | ||
72 | <style scoped lang="scss"> | 50 | <style scoped lang="scss"> |
73 | .media-left img { | ||
74 | max-height: 100%; | ||
75 | } | ||
76 | .status { | 51 | .status { |
77 | font-size: 0.8rem; | 52 | font-size: 0.8rem; |
78 | color: var(--text-title); | 53 | color: var(--text-title); |