@m11g/library 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/.editorconfig +8 -0
  2. package/.gitattributes +1 -0
  3. package/.oxfmtrc.json +5 -0
  4. package/.oxlintrc.json +10 -0
  5. package/.stylelintrc +33 -0
  6. package/.vscode/extensions.json +8 -0
  7. package/README.md +48 -0
  8. package/env.d.ts +1 -0
  9. package/eslint.config.ts +35 -0
  10. package/index.html +13 -0
  11. package/package.json +61 -0
  12. package/postcss.config.ts +23 -0
  13. package/public/favicon.ico +0 -0
  14. package/src/App.vue +28 -0
  15. package/src/assets/css/base.css +2 -0
  16. package/src/assets/css/elements/_base.css +5 -0
  17. package/src/assets/css/elements/buttons.css +23 -0
  18. package/src/assets/css/elements/form.css +75 -0
  19. package/src/assets/css/elements/scaffolding.css +62 -0
  20. package/src/assets/css/elements/transition.css +4 -0
  21. package/src/assets/css/elements/typography.css +80 -0
  22. package/src/assets/css/media-queries/media-queries.css +3 -0
  23. package/src/assets/css/mixins/list-reset.css +5 -0
  24. package/src/assets/css/variables/_base.css +7 -0
  25. package/src/assets/css/variables/chakra.css +160 -0
  26. package/src/assets/css/variables/colors.css +205 -0
  27. package/src/assets/css/variables/grid.css +6 -0
  28. package/src/assets/css/variables/spacings.css +10 -0
  29. package/src/assets/css/variables/transitions.css +10 -0
  30. package/src/assets/css/variables/typography.css +17 -0
  31. package/src/components/AppBreadcrumb.vue +63 -0
  32. package/src/components/AppButton.vue +105 -0
  33. package/src/components/AppCarousel.vue +145 -0
  34. package/src/components/AppDialog.vue +126 -0
  35. package/src/components/AppNotification.vue +68 -0
  36. package/src/components/AppPagination.vue +127 -0
  37. package/src/components/AppPopover.vue +57 -0
  38. package/src/components/AppTabs.vue +176 -0
  39. package/src/components/CenterWrapper.vue +50 -0
  40. package/src/components/ClickableWrapper.vue +48 -0
  41. package/src/components/Forms/AppForm.vue +61 -0
  42. package/src/components/Forms/FormErrorMessage.vue +17 -0
  43. package/src/components/Forms/FormField.vue +30 -0
  44. package/src/components/Forms/FormFieldset.vue +28 -0
  45. package/src/components/Forms/RadioGroup.vue +85 -0
  46. package/src/components/Forms/SearchField.vue +38 -0
  47. package/src/components/Forms/SelectField.vue +125 -0
  48. package/src/components/Forms/TextField.vue +41 -0
  49. package/src/components/Forms/TextareaField.vue +32 -0
  50. package/src/components/ScrollSpy.vue +111 -0
  51. package/src/components/SummaryDetails.vue +83 -0
  52. package/src/composables/useTheme.ts +20 -0
  53. package/src/index.ts +48 -0
  54. package/src/main.ts +10 -0
  55. package/src/router/index.ts +26 -0
  56. package/src/types/Theme.ts +11 -0
  57. package/src/views/CarouselView.vue +27 -0
  58. package/src/views/FormView.vue +111 -0
  59. package/src/views/HomeView.vue +100 -0
  60. package/src/views/ScrollSpyView.vue +58 -0
  61. package/theme-dark +117 -0
  62. package/theme-light +191 -0
  63. package/tsconfig.app.json +18 -0
  64. package/tsconfig.json +11 -0
  65. package/tsconfig.node.json +28 -0
  66. package/vite.config.ts +26 -0
@@ -0,0 +1,145 @@
1
+ <script lang="ts" setup>
2
+ import useTheme from "@/composables/useTheme";
3
+ import { computed } from "vue";
4
+
5
+ const props = defineProps<{
6
+ items: string[];
7
+ }>();
8
+
9
+ const total = computed(() => {
10
+ return props.items.length;
11
+ });
12
+
13
+ const { colors } = useTheme()
14
+ </script>
15
+
16
+ <template>
17
+ <div class="wrapper">
18
+ <ul>
19
+ <li v-for="item in items" :key="item">
20
+ <slot :name="item" />
21
+ </li>
22
+ </ul>
23
+ </div>
24
+ </template>
25
+
26
+ <style lang="css" scoped>
27
+ .wrapper {
28
+ position: relative;
29
+
30
+ @supports (scroll-marker-group: after) {
31
+ margin-block-end: var(--spacing-12);
32
+ }
33
+ }
34
+
35
+ ul {
36
+ display: grid;
37
+ grid-template-columns: repeat(v-bind(total), 100%);
38
+ gap: var(--spacing-2);
39
+ padding-inline-start: 0;
40
+ margin-block-end: var(--spacing-4);
41
+ list-style: none;
42
+ scroll-snap-type: x mandatory;
43
+ scrollbar-color: var(--chakra-colors-gray-400) transparent;
44
+ scrollbar-width: thin;
45
+
46
+ @supports (scroll-marker-group: after) {
47
+ margin-block-end: var(--spacing-2);
48
+ }
49
+
50
+ &::scroll-button(*) {
51
+ position: absolute;
52
+ inset-block-start: 50%;
53
+ display: none;
54
+ inline-size: 1.5em;
55
+ aspect-ratio: 0.571;
56
+ margin-block-start: calc(var(--spacing-8) * -1);
57
+ cursor: pointer;
58
+ content: "";
59
+ background-color: var(--chakra-colors-gray-900);
60
+ border: 1px solid var(--chakra-colors-gray-300);
61
+ opacity: 0.7;
62
+ clip-path:
63
+ shape(
64
+ from 96.34% 44.96%,
65
+ curve by 0% 10.11% with 4.88% 2.79%/4.88% 7.32%,
66
+ line by -74.99% 42.84%,
67
+ curve by -17.69% 0% with -4.88% 2.79%/-12.81% 2.79%,
68
+ smooth by 0% -10.11% with -4.88% -7.32%,
69
+ line to 69.82% 50%,
70
+ line to 3.7% 12.2%,
71
+ curve by 0% -10.11% with -4.88% -2.79%/-4.88% -7.32%,
72
+ smooth by 17.69% 0% with 12.81% -2.79%,
73
+ line by 74.99% 42.84%,
74
+ close
75
+ );
76
+ transition: opacity var(--transition);
77
+ }
78
+
79
+ &::scroll-button(*):hover,
80
+ &::scroll-button(*):focus {
81
+ opacity: 1;
82
+ }
83
+
84
+ &::scroll-button(*):active {
85
+ translate: 0 1px;
86
+ }
87
+
88
+ &::scroll-button(*):disabled {
89
+ opacity: 0.2;
90
+ }
91
+
92
+ &::scroll-button(left) {
93
+ inset-inline-start: 1em;
94
+ rotate: 180deg;
95
+ }
96
+
97
+ &::scroll-button(right) {
98
+ inset-inline-end: 1em;
99
+ }
100
+
101
+ &:has(> li:nth-child(2)) {
102
+ overflow-x: scroll;
103
+ scroll-behavior: smooth;
104
+ scroll-marker-group: after;
105
+
106
+ &::scroll-button(left),
107
+ &::scroll-button(right) {
108
+ display: block;
109
+ }
110
+ }
111
+
112
+ &::scroll-marker-group {
113
+ display: flex;
114
+ gap: var(--spacing-2);
115
+ place-content: center;
116
+ }
117
+ }
118
+
119
+ li {
120
+ scroll-snap-align: center;
121
+
122
+ &::scroll-marker {
123
+ display: block;
124
+ inline-size: 0.5em;
125
+ block-size: 0.5em;
126
+ aspect-ratio: 1;
127
+ content: "";
128
+ background-color: var(--color-white);
129
+ border: 3px solid var(--color-white);
130
+ border-radius: 50%;
131
+ transition:
132
+ border var(--transition),
133
+ background var(--transition);
134
+ }
135
+
136
+ &::scroll-marker:target-current,
137
+ &::scroll-marker:hover {
138
+ background-color: v-bind(colors.solid);
139
+ }
140
+
141
+ &::scroll-marker:target-current {
142
+ border-color: v-bind(colors.solid);
143
+ }
144
+ }
145
+ </style>
@@ -0,0 +1,126 @@
1
+ <script lang="ts" setup>
2
+ import { Icon } from "@iconify/vue";
3
+
4
+ defineProps<{
5
+ id: string;
6
+ title: string;
7
+ }>();
8
+ </script>
9
+
10
+ <template>
11
+ <dialog :id="id" closedby="any">
12
+ <header>
13
+ <h2>{{ title }}</h2>
14
+ <button :commandfor="id" command="close" class="btn-close">
15
+ <icon icon="solar:close-circle-bold" />
16
+ </button>
17
+ </header>
18
+ <div class="content">
19
+ <slot />
20
+ </div>
21
+ </dialog>
22
+ </template>
23
+
24
+ <style lang="css">
25
+ dialog {
26
+ inline-size: calc(100vw - (var(--spacing-4) * 2));
27
+ max-inline-size: var(--container-size-md);
28
+ padding: 0;
29
+ overscroll-behavior: contain;
30
+ color: var(--color-fg);
31
+ background-color: var(--color-bg-panel);
32
+ border: 0;
33
+ border-radius: 0.25em;
34
+ box-shadow: var(--shadow);
35
+ transition:
36
+ display var(--transition) allow-discrete,
37
+ overlay var(--transition) allow-discrete;
38
+ animation: dialog-hide var(--transition);
39
+
40
+ &[open] {
41
+ animation: dialog-show var(--transition);
42
+
43
+ &::backdrop {
44
+ animation: backdrop-show var(--transition);
45
+ }
46
+ }
47
+ }
48
+
49
+ ::backdrop {
50
+ overflow: hidden;
51
+ overscroll-behavior: contain;
52
+ background-color: rgb(0 0 0 / 50%);
53
+ backdrop-filter: blur(0.25em);
54
+ animation: backdrop-hide var(--transition);
55
+ }
56
+
57
+ @scope (dialog) {
58
+ svg {
59
+ font-size: 2em;
60
+ cursor: pointer;
61
+ }
62
+
63
+ header {
64
+ position: sticky;
65
+ inset-block-start: 0;
66
+ display: flex;
67
+ gap: var(--spacing-2);
68
+ align-items: start;
69
+ justify-content: space-between;
70
+ padding: var(--spacing-2) var(--spacing-4) 0;
71
+ background-color: var(--color-panel-bg);
72
+ }
73
+
74
+ .content {
75
+ padding: var(--spacing-4);
76
+ }
77
+ }
78
+
79
+ @keyframes dialog-hide {
80
+ from {
81
+ opacity: 1;
82
+ translate: 0 0;
83
+ }
84
+
85
+ to {
86
+ opacity: 0;
87
+ translate: 0 -1em;
88
+ }
89
+ }
90
+
91
+ @keyframes dialog-show {
92
+ from {
93
+ opacity: 0;
94
+ translate: 0 -1em;
95
+ }
96
+
97
+ to {
98
+ opacity: 1;
99
+ translate: 0 0;
100
+ }
101
+ }
102
+
103
+ @keyframes backdrop-show {
104
+ from {
105
+ opacity: 0;
106
+ backdrop-filter: blur(0);
107
+ }
108
+
109
+ to {
110
+ opacity: 1;
111
+ backdrop-filter: blur(0.25em);
112
+ }
113
+ }
114
+
115
+ @keyframes backdrop-hide {
116
+ from {
117
+ opacity: 1;
118
+ backdrop-filter: blur(0.25em);
119
+ }
120
+
121
+ to {
122
+ opacity: 0;
123
+ backdrop-filter: blur(0);
124
+ }
125
+ }
126
+ </style>
@@ -0,0 +1,68 @@
1
+ <script lang="ts" setup>
2
+ import { Icon } from "@iconify/vue";
3
+ import { computed, ref } from "vue";
4
+
5
+ const props = defineProps<{
6
+ title?: string;
7
+ variant: "error" | "info" | "success" | "warning";
8
+ }>();
9
+
10
+ const variantIcon = computed(() => {
11
+ switch (props.variant) {
12
+ case "error":
13
+ return "solar:danger-triangle-bold";
14
+ case "info":
15
+ return "solar:info-circle-bold";
16
+ case "warning":
17
+ return "solar:danger-circle-bold";
18
+ case "success":
19
+ default:
20
+ return "solar:check-circle-bold";
21
+ }
22
+ });
23
+
24
+ const colors = ref({
25
+ bg: `var(--color-bg-${props.variant})`,
26
+ fg: `var(--color-fg-${props.variant})`,
27
+ border: `var(--color-border-${props.variant})`,
28
+ })
29
+
30
+ </script>
31
+
32
+ <template>
33
+ <div class="notification" :class="variant">
34
+ <icon :icon="variantIcon" />
35
+ <div>
36
+ <div v-if="title" class="title">
37
+ {{ title }}
38
+ </div>
39
+ <slot />
40
+ </div>
41
+ </div>
42
+ </template>
43
+
44
+ <style lang="css" scoped>
45
+ .notification {
46
+ display: flex;
47
+ gap: var(--spacing-1);
48
+ padding: var(--spacing-2);
49
+ margin-block-end: var(--spacing-4);
50
+ color: v-bind(colors.fg);
51
+ background-color: v-bind(colors.bg);
52
+ border: 1px solid v-bind(colors.border);
53
+ border-inline-start-width: 0.25em;
54
+ }
55
+
56
+ .title {
57
+ font-weight: var(--font-weight-bold);
58
+ }
59
+
60
+ svg {
61
+ --size: 1.25em;
62
+
63
+ flex: 0 0 auto;
64
+ inline-size: var(--size);
65
+ block-size: var(--size);
66
+ margin-block-start: 0.1em;
67
+ }
68
+ </style>
@@ -0,0 +1,127 @@
1
+ <script lang="ts" setup>
2
+ import { computed } from "vue";
3
+ import { useRoute } from "vue-router";
4
+
5
+ defineProps<{
6
+ totalPages: number;
7
+ }>();
8
+
9
+ const route = useRoute();
10
+
11
+ const currentPage = computed(() => (route.query.page ? Number(route.query.page) : 1));
12
+ </script>
13
+
14
+ <template>
15
+ <div v-if="totalPages > 1" class="paging">
16
+ <router-link
17
+ v-if="currentPage > 1"
18
+ :to="{
19
+ query: {
20
+ ...route.query,
21
+ page: currentPage - 1,
22
+ },
23
+ }"
24
+ >
25
+ Vorige pagina
26
+ </router-link>
27
+ <span v-else>Vorige pagina</span>
28
+ <ul>
29
+ <li v-for="page in totalPages" :key="page" :class="{ current: page === currentPage }">
30
+ <router-link
31
+ :to="{
32
+ query: {
33
+ ...route.query,
34
+ page,
35
+ },
36
+ }"
37
+ >
38
+ {{ page }}
39
+ </router-link>
40
+ </li>
41
+ </ul>
42
+ <router-link
43
+ v-if="currentPage < totalPages"
44
+ :to="{
45
+ query: {
46
+ ...route.query,
47
+ page: currentPage + 1,
48
+ },
49
+ }"
50
+ >
51
+ Volgende pagina
52
+ </router-link>
53
+ <span v-else>Volgende pagina</span>
54
+ </div>
55
+ </template>
56
+
57
+ <style lang="css" scoped>
58
+ .paging {
59
+ display: flex;
60
+ flex-wrap: wrap;
61
+ gap: var(--spacing-2);
62
+ align-items: start;
63
+ justify-content: space-between;
64
+ font-family: var(--font-family-heading);
65
+ font-weight: var(--font-weight-medium);
66
+ line-height: var(--line-height-heading);
67
+ }
68
+
69
+ ul {
70
+ @mixin list-reset;
71
+
72
+ display: flex;
73
+ gap: var(--spacing-2);
74
+ align-items: center;
75
+ justify-content: center;
76
+ }
77
+
78
+ li {
79
+ display: none;
80
+
81
+ &:first-child,
82
+ &:last-child {
83
+ display: block;
84
+ }
85
+
86
+ &:has(+ .current),
87
+ &:has(+ li + .current),
88
+ &.current {
89
+ &,
90
+ + li,
91
+ + li + li {
92
+ display: block;
93
+ }
94
+ }
95
+ }
96
+
97
+ span,
98
+ a {
99
+ display: block;
100
+ padding: var(--spacing-1) var(--spacing-3);
101
+ text-decoration: none;
102
+ border: 1px solid currentcolor;
103
+
104
+ &:hover {
105
+ color: var(--color-black);
106
+ background-color: var(--color-white);
107
+ }
108
+ }
109
+
110
+ span {
111
+ display: none;
112
+ visibility: hidden;
113
+
114
+ @media (--md) {
115
+ display: block;
116
+ }
117
+ }
118
+
119
+ a {
120
+ display: block;
121
+ }
122
+
123
+ .current a {
124
+ color: var(--color-black);
125
+ background-color: var(--color-white);
126
+ }
127
+ </style>
@@ -0,0 +1,57 @@
1
+ <script lang="ts" setup>
2
+ import { computed, useId } from "vue";
3
+
4
+ const id = useId();
5
+
6
+ const anchor = computed(() => {
7
+ return `--${id}`;
8
+ });
9
+
10
+ defineOptions({
11
+ inheritAttrs: false,
12
+ });
13
+ </script>
14
+
15
+ <template>
16
+ <button :interestfor="id" :popovertarget="id" v-bind="$attrs"><slot name="anchor" /></button>
17
+
18
+ <div :id popover="hint"><slot /></div>
19
+ </template>
20
+
21
+ <style lang="css" scoped>
22
+ button {
23
+ anchor-name: v-bind(anchor);
24
+ interest-delay: 0s var(--transition-duration);
25
+ }
26
+
27
+ [popover] {
28
+ position: absolute;
29
+ padding: var(--spacing-1) var(--spacing-2);
30
+ margin: var(--spacing-2);
31
+ position-area: top;
32
+ position-anchor: v-bind(anchor);
33
+ position-try-fallbacks: flip-block;
34
+ color: rgb(250 250 250);
35
+ zoom: 0.9;
36
+ background-color: rgb(17 17 17);
37
+ border: 1px solid rgb(39 39 42);
38
+ opacity: 0;
39
+ transition:
40
+ opacity var(--transition),
41
+ zoom var(--transition),
42
+ overlay allow-discrete,
43
+ display allow-discrete;
44
+
45
+ &:popover-open {
46
+ zoom: 1;
47
+ opacity: 1;
48
+ }
49
+ }
50
+
51
+ @starting-style {
52
+ [popover]:popover-open {
53
+ zoom: 0.9;
54
+ opacity: 0;
55
+ }
56
+ }
57
+ </style>
@@ -0,0 +1,176 @@
1
+ <script lang="ts" setup>
2
+ import useTheme from "@/composables/useTheme";
3
+ import { computed, ref, useId, useTemplateRef } from "vue";
4
+
5
+ const props = defineProps<{
6
+ tabs: {
7
+ id: string;
8
+ title: string;
9
+ }[];
10
+ }>();
11
+
12
+ const selectedTab = ref(0);
13
+
14
+ const id = useId();
15
+
16
+ const anchor = computed(() => `--${id}`);
17
+
18
+ const nextTab = () => {
19
+ if (selectedTab.value === props.tabs.length - 1) {
20
+ goToTab(0);
21
+ } else {
22
+ goToTab(selectedTab.value + 1);
23
+ }
24
+ };
25
+
26
+ const previousTab = () => {
27
+ if (selectedTab.value === 0) {
28
+ goToTab(props.tabs.length - 1);
29
+ } else {
30
+ goToTab(selectedTab.value - 1);
31
+ }
32
+ };
33
+
34
+ const firstTab = () => {
35
+ goToTab(0);
36
+ };
37
+
38
+ const lastTab = () => {
39
+ goToTab(props.tabs.length - 1);
40
+ };
41
+
42
+ const tabButtons = useTemplateRef("button");
43
+
44
+ const goToTab = (tab: number) => {
45
+ if (tabButtons.value?.length && tabButtons.value[tab]) {
46
+ tabButtons.value[tab].focus();
47
+ }
48
+ selectedTab.value = tab;
49
+ };
50
+
51
+ const total = computed(() => {
52
+ return props.tabs.length;
53
+ });
54
+
55
+ const {colors} = useTheme()
56
+ </script>
57
+
58
+ <template>
59
+ <div class="tabs">
60
+ <ul role="tablist">
61
+ <li v-for="(tab, index) in tabs" :key="tab.id" role="presentation">
62
+ <button
63
+ :id="`tab-${tab.id}`"
64
+ ref="button"
65
+ type="button"
66
+ role="tab"
67
+ :aria-selected="selectedTab === index"
68
+ :aria-controls="`panel-${tab.id}`"
69
+ :tabindex="selectedTab === index ? undefined : '-1'"
70
+ @keydown.right="nextTab"
71
+ @keydown.left="previousTab"
72
+ @keydown.home="firstTab"
73
+ @keydown.end="lastTab"
74
+ @click="selectedTab = index"
75
+ >
76
+ {{ tab.title }}
77
+ </button>
78
+ </li>
79
+ </ul>
80
+ <div class="panel">
81
+ <div
82
+ v-for="(tab, index) in tabs"
83
+ :id="`panel-${tab.id}`"
84
+ :key="tab.id"
85
+ :aria-labelledby="`tab-${tab.id}`"
86
+ role="tabpanel"
87
+ tabindex="0"
88
+ :class="{
89
+ active: selectedTab === index,
90
+ }"
91
+ >
92
+ <slot :name="tab.id" />
93
+ </div>
94
+ </div>
95
+ </div>
96
+ </template>
97
+
98
+ <style lang="css">
99
+ @scope (.tabs) {
100
+ :scope {
101
+ margin-block-end: var(--spacing-4);
102
+ container-type: inline-size;
103
+ transition:
104
+ height var(--transition),
105
+ content-visibility var(--transition) allow-discrete;
106
+ }
107
+
108
+ ul {
109
+ display: grid;
110
+ grid-template-columns: repeat(v-bind(total), 1fr);
111
+ padding: 0;
112
+ margin: 0 0 var(--spacing-4);
113
+ list-style: none outside;
114
+ border-block-end: 1px solid v-bind(colors.subtle);
115
+
116
+ &::before {
117
+ position: absolute;
118
+ inset-block-end: anchor(bottom);
119
+ inset-inline-start: anchor(left);
120
+ display: block;
121
+ /* stylelint-disable-next-line declaration-property-value-no-unknown */
122
+ inline-size: anchor-size(inline);
123
+ /* stylelint-disable-next-line declaration-property-value-no-unknown */
124
+ block-size: anchor-size(block);
125
+ position-anchor: v-bind(anchor);
126
+ content: "";
127
+ background: v-bind(colors.emphasized);
128
+
129
+ /* background: v-bind(colors.muted); */
130
+ transition:
131
+ left var(--transition),
132
+ width var(--transition);
133
+ }
134
+ }
135
+
136
+ [role="tab"] {
137
+ position: relative;
138
+ inline-size: 100%;
139
+ padding: var(--spacing-3) var(--spacing-2);
140
+ text-align: center;
141
+ transition: background var(--transition);
142
+
143
+ &:hover:not([aria-selected="true"]) {
144
+ color: v-bind(colors.fg);
145
+ background-color: v-bind(colors.subtle);
146
+ }
147
+
148
+ &[aria-selected="true"] {
149
+ anchor-name: v-bind(anchor);
150
+ color: v-bind(colors.fg);
151
+ }
152
+ }
153
+
154
+ [role="tabpanel"] {
155
+ display: none;
156
+ opacity: 0;
157
+ transition:
158
+ opacity var(--transition),
159
+ overlay allow-discrete,
160
+ display allow-discrete;
161
+
162
+ &.active {
163
+ display: block;
164
+ opacity: 1;
165
+ transition-delay: 0.1s;
166
+ }
167
+ }
168
+
169
+ @starting-style {
170
+ [role="tabpanel"].active {
171
+ opacity: 0;
172
+ }
173
+ }
174
+ }
175
+
176
+ </style>