@luna-park/design 1.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 (130) hide show
  1. package/eslint.config.js +9 -0
  2. package/histoire.config.ts +60 -0
  3. package/package.json +71 -0
  4. package/public/favicon_rc.png +0 -0
  5. package/src/app.ts +9 -0
  6. package/src/assets/controls/mouse.svg +54 -0
  7. package/src/assets/logo_neon.svg +18 -0
  8. package/src/assets/logo_rc_color.svg +54 -0
  9. package/src/assets/logo_rc_square_blue.svg +16 -0
  10. package/src/assets/logo_square_blue.svg +17 -0
  11. package/src/assets/logo_square_white.svg +17 -0
  12. package/src/assets/logo_text_white.svg +32 -0
  13. package/src/assets/stars.svg +66 -0
  14. package/src/components/breadcrumb/LBreadLink.vue +40 -0
  15. package/src/components/breadcrumb/LBreadcrumb.story.vue +29 -0
  16. package/src/components/breadcrumb/LBreadcrumb.vue +54 -0
  17. package/src/components/breadcrumb/type.ts +6 -0
  18. package/src/components/context/LContextMenu.story.vue +73 -0
  19. package/src/components/context/LContextMenu.vue +24 -0
  20. package/src/components/context/LContextMenuElement.vue +54 -0
  21. package/src/components/context/LContextMenuWrapper.vue +55 -0
  22. package/src/components/context/LContextOption.story.vue +18 -0
  23. package/src/components/context/LContextOption.vue +160 -0
  24. package/src/components/context/LContextWrapper.story.vue +11 -0
  25. package/src/components/context/LContextWrapper.vue +60 -0
  26. package/src/components/context/store.ts +62 -0
  27. package/src/components/context/type.ts +27 -0
  28. package/src/components/dialog/LDialogAlert.vue +38 -0
  29. package/src/components/dialog/LDialogConfirm.vue +45 -0
  30. package/src/components/dialog/LDialogInjector.story.vue +41 -0
  31. package/src/components/dialog/LDialogInjector.vue +40 -0
  32. package/src/components/dialog/LDialogPrompt.vue +67 -0
  33. package/src/components/dialog/LDialogWrapper.vue +66 -0
  34. package/src/components/dialog/lib.ts +50 -0
  35. package/src/components/dialog/store.ts +32 -0
  36. package/src/components/floating/LFloating.story.vue +35 -0
  37. package/src/components/floating/LFloating.vue +362 -0
  38. package/src/components/form/LAutoComplete.vue +13 -0
  39. package/src/components/form/LAutoInput.story.vue +43 -0
  40. package/src/components/form/LAutoInput.vue +101 -0
  41. package/src/components/form/LButton.story.vue +147 -0
  42. package/src/components/form/LButton.vue +227 -0
  43. package/src/components/form/LCheckbox.story.vue +13 -0
  44. package/src/components/form/LCheckbox.vue +70 -0
  45. package/src/components/form/LColorInput.story.vue +28 -0
  46. package/src/components/form/LColorInput.vue +101 -0
  47. package/src/components/form/LImageInput.story.vue +28 -0
  48. package/src/components/form/LImageInput.vue +75 -0
  49. package/src/components/form/LInfo.story.vue +22 -0
  50. package/src/components/form/LInfo.vue +44 -0
  51. package/src/components/form/LInput.story.vue +150 -0
  52. package/src/components/form/LInput.vue +493 -0
  53. package/src/components/form/LInputDateFloating.vue +61 -0
  54. package/src/components/form/LInputNumber.story.vue +58 -0
  55. package/src/components/form/LProgress.story.vue +49 -0
  56. package/src/components/form/LProgress.vue +77 -0
  57. package/src/components/form/LSelect.story.vue +67 -0
  58. package/src/components/form/LSelect.vue +142 -0
  59. package/src/components/form/LSwitch.story.vue +15 -0
  60. package/src/components/form/LSwitch.vue +79 -0
  61. package/src/components/form/LTextarea.story.vue +29 -0
  62. package/src/components/form/LTextarea.vue +151 -0
  63. package/src/components/form/color-picker/LColorAlpha.vue +129 -0
  64. package/src/components/form/color-picker/LColorHue.vue +109 -0
  65. package/src/components/form/color-picker/LColorModels.vue +223 -0
  66. package/src/components/form/color-picker/LColorPicker.story.vue +44 -0
  67. package/src/components/form/color-picker/LColorPicker.vue +105 -0
  68. package/src/components/form/color-picker/LColorShade.vue +114 -0
  69. package/src/components/form/color-picker/LImagePicker.vue +477 -0
  70. package/src/components/form/dropdown/LDropdown.story.vue +123 -0
  71. package/src/components/form/dropdown/LDropdown.vue +483 -0
  72. package/src/components/form/dropdown/LDropdownOption.vue +224 -0
  73. package/src/components/form/dropdown/LDropdownSelection.vue +76 -0
  74. package/src/components/form/dropdown/types.ts +15 -0
  75. package/src/components/form/emoji-picker/LEmojiList.vue +54 -0
  76. package/src/components/form/emoji-picker/LEmojiListCategory.vue +92 -0
  77. package/src/components/form/emoji-picker/LEmojiPicker.story.vue +32 -0
  78. package/src/components/form/emoji-picker/LEmojiPicker.vue +55 -0
  79. package/src/components/form/emoji-picker/LEmojiSelect.story.vue +22 -0
  80. package/src/components/form/emoji-picker/LEmojiSelect.vue +51 -0
  81. package/src/components/form/icon-picker/LIconList.vue +100 -0
  82. package/src/components/form/icon-picker/LIconMaterial.vue +43 -0
  83. package/src/components/form/icon-picker/LIconPicker.story.vue +39 -0
  84. package/src/components/form/icon-picker/LIconPicker.vue +92 -0
  85. package/src/components/form/icon-picker/LIconSelect.story.vue +25 -0
  86. package/src/components/form/icon-picker/LIconSelect.vue +91 -0
  87. package/src/components/icons/LControls.story.vue +92 -0
  88. package/src/components/icons/LKeyIcon.vue +66 -0
  89. package/src/components/icons/LMouseIcon.vue +85 -0
  90. package/src/components/icons/LShortcut.story.vue +12 -0
  91. package/src/components/icons/LShortcut.vue +45 -0
  92. package/src/components/layout/LResizer.story.vue +89 -0
  93. package/src/components/layout/LResizer.vue +138 -0
  94. package/src/components/misc/LIcon.vue +34 -0
  95. package/src/components/misc/LLineLoader.story.vue +18 -0
  96. package/src/components/misc/LLineLoader.vue +52 -0
  97. package/src/components/misc/LLoading.story.vue +14 -0
  98. package/src/components/misc/LLoading.vue +28 -0
  99. package/src/components/misc/LStarsBackground.story.vue +16 -0
  100. package/src/components/misc/LStarsBackground.vue +121 -0
  101. package/src/components/navigation/LElementsPagination.vue +75 -0
  102. package/src/components/navigation/LPagination.story.vue +57 -0
  103. package/src/components/navigation/LPagination.vue +125 -0
  104. package/src/components/table/LCell.vue +37 -0
  105. package/src/components/table/LLine.vue +24 -0
  106. package/src/components/table/LTable.story.vue +35 -0
  107. package/src/components/table/LTable.vue +21 -0
  108. package/src/components/toasts/LContainer.story.vue +47 -0
  109. package/src/components/toasts/LContainer.vue +140 -0
  110. package/src/components/toasts/LToast.story.vue +47 -0
  111. package/src/components/toasts/LToast.vue +30 -0
  112. package/src/components/toasts/LToastInjector.vue +54 -0
  113. package/src/components/toasts/requests.ts +46 -0
  114. package/src/components/toasts/store.ts +45 -0
  115. package/src/components/utils/LVirtualElement.vue +36 -0
  116. package/src/components/utils/LVirtualScroller.story.vue +62 -0
  117. package/src/components/utils/LVirtualScroller.vue +105 -0
  118. package/src/components/utils/virtual.ts +6 -0
  119. package/src/env.d.ts +9 -0
  120. package/src/histoire.setup.ts +8 -0
  121. package/src/icons.ts +8 -0
  122. package/src/index.ts +58 -0
  123. package/src/style/colors.scss +152 -0
  124. package/src/style/fonts.scss +45 -0
  125. package/src/style/index.scss +64 -0
  126. package/src/style/layout.scss +3 -0
  127. package/src/style/lengths.scss +21 -0
  128. package/src/style/scrollbar.scss +27 -0
  129. package/tsconfig.json +34 -0
  130. package/vite.config.ts +68 -0
@@ -0,0 +1,101 @@
1
+ <template>
2
+ <component
3
+ :is="textInputComponent"
4
+ ref="input"
5
+ v-model="value"
6
+ class="input"
7
+ :type="inputType"
8
+ v-bind="$attrs"
9
+ @keydown="processKeyDown"
10
+ />
11
+ </template>
12
+
13
+ <script setup lang="ts">
14
+ import { computed, nextTick, ref, watch } from "vue";
15
+
16
+ import LColorInput from "@/components/form/LColorInput.vue";
17
+ import LInput from "@/components/form/LInput.vue";
18
+ import LTextarea from "@/components/form/LTextarea.vue";
19
+
20
+ const props = withDefaults(defineProps<{
21
+ cast?: "text" | "number";
22
+ forceInput?: boolean;
23
+ modelValue: string | number;
24
+ type?: "text" | "number" | "color" | "auto";
25
+ }>(), {
26
+ type: "auto"
27
+ });
28
+
29
+ const emits = defineEmits<(e: "update:modelValue", value: string | number) => void>();
30
+
31
+ const value = computed({
32
+ get: () => props.modelValue,
33
+ set: (newValue: string | number) => {
34
+ if (props.cast === "number") {
35
+ newValue = Number(newValue);
36
+ }
37
+
38
+ if (props.cast === "text") {
39
+ newValue = String(newValue);
40
+ }
41
+
42
+ emits("update:modelValue", newValue);
43
+ }
44
+ });
45
+
46
+ const useTextarea = ref(false);
47
+
48
+ function processKeyDown(event: KeyboardEvent) {
49
+ useTextarea.value = (event.key === "Enter" && event.shiftKey);
50
+ }
51
+
52
+ const input = ref();
53
+ const inputType = computed(() => {
54
+ if (isColor.value) {
55
+ return "text";
56
+ }
57
+ return props.type;
58
+ });
59
+
60
+ const isColor = computed(() => {
61
+ if (props.type === "color") {
62
+ return true;
63
+ }
64
+
65
+ if (["auto", "text"].includes(props.type)) {
66
+ const stringValue = value.value as string || "";
67
+ return /^#[0-9a-f]{3,8}$/i.test(stringValue);
68
+ }
69
+
70
+ return false;
71
+ });
72
+
73
+ const textInputComponent = computed(() => {
74
+ if (isColor.value) {
75
+ return LColorInput;
76
+ }
77
+
78
+ if (!props.forceInput && (useTextarea.value || value.value?.includes?.("\n"))) {
79
+ return LTextarea;
80
+ }
81
+
82
+ return LInput;
83
+ });
84
+
85
+ function getCurrentInput() {
86
+ return input.value.$el.querySelector("input") ?? input.value.$el.querySelector("textarea");
87
+ }
88
+
89
+ watch(textInputComponent, async () => {
90
+ if (document.activeElement === getCurrentInput()) {
91
+ await nextTick();
92
+ getCurrentInput()?.focus();
93
+ }
94
+ });
95
+ </script>
96
+
97
+ <style scoped>
98
+ .auto-input {
99
+
100
+ }
101
+ </style>
@@ -0,0 +1,147 @@
1
+ <template>
2
+ <Story
3
+ :layout="{
4
+ type: 'grid',
5
+ width: '20%',
6
+ }"
7
+ >
8
+ <template #controls>
9
+ <HstText
10
+ v-model="state.name"
11
+ title="Name"
12
+ />
13
+ </template>
14
+ <Variant title="standard + icon">
15
+ <LButton
16
+ class="button"
17
+ :icon="faRocket"
18
+ >
19
+ {{ state.name }}
20
+ </LButton>
21
+ </Variant>
22
+ <Variant title="standard + icon + loading">
23
+ <LButton
24
+ class="button"
25
+ :icon="faRocket"
26
+ loading
27
+ >
28
+ {{ state.name }}
29
+ </LButton>
30
+ </Variant>
31
+ <Variant title="big">
32
+ <LButton big>
33
+ {{ state.name }}
34
+ </LButton>
35
+ </Variant>
36
+ <Variant title="borderless">
37
+ <LButton borderless>
38
+ {{ state.name }}
39
+ </LButton>
40
+ </Variant>
41
+ <Variant title="big borderless">
42
+ <LButton
43
+ big
44
+ borderless
45
+ >
46
+ {{ state.name }}
47
+ </LButton>
48
+ </Variant>
49
+ <Variant title="transparent">
50
+ <LButton transparent>
51
+ {{ state.name }}
52
+ </LButton>
53
+ </Variant>
54
+ <Variant title="big transparent">
55
+ <LButton
56
+ big
57
+ transparent
58
+ >
59
+ {{ state.name }}
60
+ </LButton>
61
+ </Variant>
62
+ <Variant title="borderless primary">
63
+ <LButton
64
+ borderless
65
+ primary
66
+ >
67
+ {{ state.name }}
68
+ </LButton>
69
+ </Variant>
70
+ <Variant title="big borderless primary">
71
+ <LButton
72
+ big
73
+ borderless
74
+ primary
75
+ >
76
+ {{ state.name }}
77
+ </LButton>
78
+ </Variant>
79
+ <Variant title="primary">
80
+ <LButton
81
+ primary
82
+ >
83
+ {{ state.name }}
84
+ </LButton>
85
+ </Variant>
86
+ <Variant title="big primary">
87
+ <LButton
88
+ big
89
+ primary
90
+ >
91
+ {{ state.name }}
92
+ </LButton>
93
+ </Variant>
94
+ <Variant title="big error">
95
+ <LButton
96
+ big
97
+ error
98
+ >
99
+ {{ state.name }}
100
+ </LButton>
101
+ </Variant>
102
+ <Variant title="big warning">
103
+ <LButton
104
+ big
105
+ warning
106
+ >
107
+ {{ state.name }}
108
+ </LButton>
109
+ </Variant>
110
+ <Variant title="big info">
111
+ <LButton
112
+ big
113
+ info
114
+ >
115
+ {{ state.name }}
116
+ </LButton>
117
+ </Variant>
118
+ <Variant title="big success">
119
+ <LButton
120
+ big
121
+ success
122
+ >
123
+ {{ state.name }}
124
+ </LButton>
125
+ </Variant>
126
+ </Story>
127
+ </template>
128
+
129
+ <script setup lang="ts">
130
+ import { reactive } from "vue";
131
+ import LButton from "./LButton.vue";
132
+ import { faRocket } from "@fortawesome/pro-solid-svg-icons";
133
+
134
+ const state = reactive({
135
+ name: "Test"
136
+ });
137
+
138
+ defineExpose({
139
+ state
140
+ });
141
+ </script>
142
+
143
+ <style scoped>
144
+ .button {
145
+ width: 100%;
146
+ }
147
+ </style>
@@ -0,0 +1,227 @@
1
+ <template>
2
+ <component
3
+ :is="tagType"
4
+ ref="button"
5
+ class="button"
6
+ :class="{big, transparent, borderless, primary, disabled: disabled || loading, square, lite, error, warning, info, success, inline, small, focus, cell}"
7
+ :disabled="disabled"
8
+ :href="href"
9
+ :target="target"
10
+ >
11
+ <LIcon
12
+ v-if="loading"
13
+ class="icon"
14
+ :icon="faCircleNotch"
15
+ spin
16
+ />
17
+ <LIcon
18
+ v-else-if="icon"
19
+ class="icon"
20
+ :icon="icon"
21
+ />
22
+ <slot />
23
+ </component>
24
+ </template>
25
+
26
+ <script setup lang="ts">
27
+ import { IconDefinition } from "@fortawesome/fontawesome-svg-core";
28
+ import { faCircleNotch } from "@fortawesome/pro-solid-svg-icons";
29
+ import { computed, onMounted, useTemplateRef } from "vue";
30
+
31
+ import LIcon from "@/components/misc/LIcon.vue";
32
+
33
+ const props = defineProps<{
34
+ big?: boolean;
35
+ borderless?: boolean;
36
+ cell?: boolean;
37
+ disabled?: boolean;
38
+ error?: boolean;
39
+ focus?: boolean;
40
+ href?: string;
41
+ icon?: IconDefinition;
42
+ info?: boolean;
43
+ inline?: boolean;
44
+ lite?: boolean;
45
+ loading?: boolean;
46
+ primary?: boolean;
47
+ small?: boolean;
48
+ square?: boolean;
49
+ success?: boolean;
50
+ target?: string;
51
+ transparent?: boolean;
52
+ warning?: boolean;
53
+ }>();
54
+
55
+ const tagType = computed(() => props.href ? "a" : "button");
56
+ const buttonTemplate = useTemplateRef<HTMLElement>("button");
57
+
58
+ onMounted(() => {
59
+ if (props.focus && buttonTemplate.value) {
60
+ buttonTemplate.value.focus({ focusVisible: true });
61
+ }
62
+ });
63
+ </script>
64
+
65
+ <style scoped>
66
+ .button {
67
+ --button-background: var(--color-background-1);
68
+ --button-background-hover: var(--color-background-2);
69
+ --button-content: var(--color-content-lite);
70
+ --button-border: var(--color-soft);
71
+ --button-radius: var(--length-radius-s);
72
+ padding: 0 var(--length-xxs);
73
+
74
+ .icon {
75
+ color: var(--color-content-liter);
76
+ transition: var(--transition-fast);
77
+ }
78
+
79
+ &.error {
80
+ --button-background: var(--color-error-background);
81
+ --button-content: var(--color-error-content);
82
+ --button-border: var(--color-error);
83
+ }
84
+
85
+ &.warning {
86
+ --button-background: var(--color-warning-background);
87
+ --button-content: var(--color-warning-content);
88
+ --button-border: var(--color-warning);
89
+ }
90
+
91
+ &.info {
92
+ --button-background: var(--color-info-background);
93
+ --button-content: var(--color-info-content);
94
+ --button-border: var(--color-info);
95
+ }
96
+
97
+ &.success {
98
+ --button-background: var(--color-success-background);
99
+ --button-content: var(--color-success-content);
100
+ --button-border: var(--color-success);
101
+ }
102
+
103
+ &.primary:not(.disabled) {
104
+ --button-background: var(--color-primary-dark);
105
+ --button-background-hover: var(--color-primary-darker);
106
+ --button-border: var(--color-primary-liter);
107
+ }
108
+
109
+ &.error, &.warning, &.info, &.success {
110
+ --button-background-hover: var(--button-border);
111
+ }
112
+
113
+ &.transparent {
114
+ --button-background: transparent;
115
+ --button-background-hover: var(--color-background-liter);
116
+ }
117
+
118
+ box-sizing: border-box;
119
+ height: 24px;
120
+ font-size: 0.8rem;
121
+ border: 1px solid var(--button-border);
122
+ background-clip: border-box;
123
+ color: var(--button-content);
124
+ transition: var(--transition-fast);
125
+ cursor: pointer;
126
+ display: flex;
127
+ justify-content: center;
128
+ align-items: center;
129
+
130
+ &:has(.icon) {
131
+ gap: var(--length-xs);
132
+ }
133
+
134
+ &:not(.cell) {
135
+ border-radius: var(--button-radius);
136
+ }
137
+
138
+ &.lite {
139
+ color: var(--color-content-litest);
140
+
141
+ .icon {
142
+ color: var(--color-content-litest);
143
+ }
144
+ }
145
+
146
+ &:not(.transparent) {
147
+ background: var(--button-background);
148
+ }
149
+
150
+ /* .focus:focus is a hack to make focus-visible on chrome */
151
+ /* Remove when this is available: https://caniuse.com/mdn-api_htmlelement_focus_options_focusvisible_parameter */
152
+ &:focus-visible, &.focus:focus {
153
+ outline: 2px dotted var(--color-primary);
154
+ outline-offset: var(--length-xxxs);
155
+ }
156
+
157
+ &.square {
158
+ width: 24px;
159
+ }
160
+
161
+ &.big {
162
+ font-size: 0.9rem;
163
+ height: 36px;
164
+ padding: 0 var(--length-xs);
165
+
166
+ &.square {
167
+ width: 36px;
168
+ }
169
+ }
170
+
171
+ &.small {
172
+ font-size: 0.7rem;
173
+ height: 20px;
174
+ --button-radius: var(--length-radius-xs);
175
+
176
+ &.square {
177
+ width: 20px;
178
+ }
179
+
180
+ &:has(.icon) {
181
+ gap: var(--length-xxs);
182
+ }
183
+ }
184
+
185
+ &.borderless {
186
+ border-color: transparent;
187
+ }
188
+
189
+ &.disabled {
190
+ --button-content: var(--color-content-litest);
191
+ cursor: default;
192
+
193
+ .icon {
194
+ color: var(--button-content);
195
+ }
196
+ }
197
+
198
+ &:hover:not(.disabled) {
199
+ color: var(--color-content);
200
+ background: var(--button-background-hover);
201
+
202
+ .icon {
203
+ color: var(--color-content);
204
+ }
205
+
206
+ &:not(.borderless).transparent {
207
+ border-color: var(--color-content-litest);
208
+ }
209
+ }
210
+
211
+ &.transparent.borderless {
212
+ &.disabled, &.disabled .icon {
213
+ color: var(--color-content-litest);
214
+ }
215
+
216
+ &.lite {
217
+ &.disabled, &.disabled .icon {
218
+ color: var(--color-soft);
219
+ }
220
+ }
221
+
222
+ &:hover:not(.disabled) {
223
+ background: var(--button-background-hover);
224
+ }
225
+ }
226
+ }
227
+ </style>
@@ -0,0 +1,13 @@
1
+ <template>
2
+ <Story>
3
+ <LCheckbox />
4
+ </Story>
5
+ </template>
6
+
7
+ <script setup lang="ts">
8
+
9
+ import LCheckbox from "./LCheckbox.vue";
10
+ </script>
11
+
12
+ <style scoped>
13
+ </style>
@@ -0,0 +1,70 @@
1
+ <template>
2
+ <div
3
+ class="checkbox"
4
+ :class="{colored, small, borderless}"
5
+ @click="toggleValue"
6
+ >
7
+ <LIcon
8
+ v-if="props.modelValue"
9
+ :icon="faCheck"
10
+ />
11
+ </div>
12
+ </template>
13
+
14
+
15
+ <script setup lang="ts">
16
+ import { faCheck } from "@fortawesome/pro-solid-svg-icons";
17
+ import LIcon from "@/components/misc/LIcon.vue";
18
+
19
+ const props = defineProps<{
20
+ borderless?: boolean;
21
+ colored?: boolean;
22
+ modelValue?: boolean;
23
+ small?: boolean;
24
+ }>();
25
+
26
+ const emits = defineEmits<(e: "update:modelValue", value: boolean) => void>();
27
+
28
+ function toggleValue() {
29
+ emits("update:modelValue", !props.modelValue);
30
+ }
31
+ </script>
32
+
33
+ <style scoped>
34
+ .checkbox {
35
+ color: var(--color-content-lite);
36
+ border: 1px solid var(--color-soft);
37
+ border-radius: var(--length-radius-s);
38
+ display: flex;
39
+ height: 24px;
40
+ width: 24px;
41
+ box-sizing: border-box;
42
+ flex-shrink: 0;
43
+ font-size: 0.7rem;
44
+ align-items: center;
45
+ justify-content: center;
46
+ cursor: pointer;
47
+ background: var(--color-background-1);
48
+ transition: var(--transition-fast);
49
+
50
+ &.colored {
51
+ border: 1px solid var(--color-type-boolean);
52
+ }
53
+
54
+ &:hover {
55
+ background: var(--color-background-2);
56
+ color: var(--color-content);
57
+ border-color: var(--color-primary);
58
+ }
59
+
60
+ &.small {
61
+ height: 20px;
62
+ width: 20px;
63
+ border-radius: var(--length-radius-xs);
64
+ }
65
+
66
+ &.borderless {
67
+ border-color: transparent;
68
+ }
69
+ }
70
+ </style>
@@ -0,0 +1,28 @@
1
+ <template>
2
+ <Story
3
+ :layout="{
4
+ type: 'grid',
5
+ width: '200px'
6
+ }"
7
+ >
8
+ <LColorInput v-model="state.color" />
9
+ <template #controls>
10
+ <HstText
11
+ v-model="state.color"
12
+ title="Color"
13
+ />
14
+ </template>
15
+ </Story>
16
+ </template>
17
+
18
+ <script setup lang="ts">
19
+ import { reactive } from "vue";
20
+ import LColorInput from "@/components/form/LColorInput.vue";
21
+
22
+ const state = reactive({
23
+ color: "#ff6600"
24
+ });
25
+ </script>
26
+
27
+ <style scoped>
28
+ </style>
@@ -0,0 +1,101 @@
1
+ <template>
2
+ <LInput
3
+ v-model="value"
4
+ v-bind="$attrs"
5
+ :big="big"
6
+ class="color-input"
7
+ :small="small"
8
+ >
9
+ <template #prefix>
10
+ <LFloating :offset="distance">
11
+ <div
12
+ class="color-preview-wrapper"
13
+ @click.middle="eyeDropper"
14
+ >
15
+ <div class="color-preview" />
16
+ </div>
17
+ <template #popper>
18
+ <div class="color-wrapper">
19
+ <LColorPicker v-model="value" />
20
+ </div>
21
+ </template>
22
+ </LFloating>
23
+ </template>
24
+ </LInput>
25
+ </template>
26
+
27
+ <script setup lang="ts">
28
+ import { computed } from "vue";
29
+ import LInput from "@/components/form/LInput.vue";
30
+ import LColorPicker from "@/components/form/color-picker/LColorPicker.vue";
31
+ import LFloating from "@/components/floating/LFloating.vue";
32
+ import { useEyeDropper } from "@vueuse/core";
33
+
34
+ const props = defineProps<{
35
+ big?: boolean;
36
+ modelValue: string;
37
+ small?: boolean;
38
+ }>();
39
+
40
+ const emits = defineEmits<(e: "update:modelValue", value: string) => void>();
41
+
42
+ const { isSupported, open: openEyeDropper } = useEyeDropper();
43
+
44
+ const distance = computed(() => {
45
+ if (props.big) {
46
+ return 4;
47
+ }
48
+ if (props.small) {
49
+ return 6;
50
+ }
51
+ return 9;
52
+ });
53
+
54
+ const value = computed({
55
+ get: () => props.modelValue,
56
+ set: (newValue: string) => emits("update:modelValue", newValue)
57
+ });
58
+
59
+ async function eyeDropper() {
60
+ if (!isSupported.value) {
61
+ return;
62
+ }
63
+
64
+ const result = await openEyeDropper();
65
+
66
+ if (result?.sRGBHex) {
67
+ value.value = result.sRGBHex;
68
+ }
69
+ }
70
+ </script>
71
+
72
+ <style scoped>
73
+ .color-input {
74
+ padding-left: var(--length-xxs);
75
+ gap: var(--length-xxs);
76
+
77
+ .color-preview-wrapper {
78
+ width: 14px;
79
+ height: 14px;
80
+ border-radius: var(--length-radius-xs);
81
+ cursor: pointer;
82
+ box-sizing: border-box;
83
+ box-shadow: 0 0 4px var(--color-soft);
84
+ overflow: hidden;
85
+ background: repeating-conic-gradient(var(--color-background-lite) 0% 25%, var(--color-background-3) 0% 50%) 50% / 16px 16px;
86
+
87
+ .color-preview {
88
+ background: v-bind(modelValue);
89
+ height: 100%;
90
+ width: 100%;
91
+ }
92
+ }
93
+ }
94
+
95
+ .color-wrapper {
96
+ padding: var(--length-s);
97
+ background: var(--color-background-0-backdrop);
98
+ backdrop-filter: blur(16px);
99
+ }
100
+ </style>
101
+