spoko-design-system 0.5.6 → 0.5.8

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.
@@ -1 +1 @@
1
- [["Map",1,2],"meta::meta",["Map",3,4,5,6],"astro-version","5.3.0","astro-config-digest","{\"root\":{},\"srcDir\":{},\"publicDir\":{},\"outDir\":{},\"cacheDir\":{},\"site\":\"https://sds.spoko.space/\",\"compressHTML\":true,\"base\":\"/\",\"trailingSlash\":\"ignore\",\"output\":\"static\",\"scopedStyleStrategy\":\"attribute\",\"build\":{\"format\":\"directory\",\"client\":{},\"server\":{},\"assets\":\"_astro\",\"serverEntry\":\"entry.mjs\",\"redirects\":true,\"inlineStylesheets\":\"auto\",\"concurrency\":1},\"server\":{\"open\":false,\"host\":false,\"port\":1234,\"streaming\":true},\"redirects\":{},\"image\":{\"endpoint\":{\"route\":\"/_image\"},\"service\":{\"entrypoint\":\"astro/assets/services/sharp\",\"config\":{}},\"domains\":[\"placehold.co\",\"api.polo.blue\",\"polo.blue\",\"media.istockphoto.com\",\"freepik.com\",\"img.freepik.com\",\"polo6r.pl\"],\"remotePatterns\":[]},\"devToolbar\":{\"enabled\":true},\"markdown\":{\"syntaxHighlight\":\"shiki\",\"shikiConfig\":{\"langs\":[],\"langAlias\":{},\"theme\":\"github-dark\",\"themes\":{},\"wrap\":false,\"transformers\":[]},\"remarkPlugins\":[],\"rehypePlugins\":[],\"remarkRehype\":{},\"gfm\":true,\"smartypants\":true},\"security\":{\"checkOrigin\":true},\"env\":{\"schema\":{},\"validateSecrets\":false},\"experimental\":{\"clientPrerender\":false,\"contentIntellisense\":false,\"responsiveImages\":false,\"serializeConfig\":false},\"legacy\":{\"collections\":false}}"]
1
+ [["Map",1,2],"meta::meta",["Map",3,4,5,6],"astro-version","5.3.1","astro-config-digest","{\"root\":{},\"srcDir\":{},\"publicDir\":{},\"outDir\":{},\"cacheDir\":{},\"site\":\"https://sds.spoko.space/\",\"compressHTML\":true,\"base\":\"/\",\"trailingSlash\":\"ignore\",\"output\":\"static\",\"scopedStyleStrategy\":\"attribute\",\"build\":{\"format\":\"directory\",\"client\":{},\"server\":{},\"assets\":\"_astro\",\"serverEntry\":\"entry.mjs\",\"redirects\":true,\"inlineStylesheets\":\"auto\",\"concurrency\":1},\"server\":{\"open\":false,\"host\":false,\"port\":1234,\"streaming\":true},\"redirects\":{},\"image\":{\"endpoint\":{\"route\":\"/_image\"},\"service\":{\"entrypoint\":\"astro/assets/services/sharp\",\"config\":{}},\"domains\":[\"placehold.co\",\"api.polo.blue\",\"polo.blue\",\"media.istockphoto.com\",\"freepik.com\",\"img.freepik.com\",\"polo6r.pl\"],\"remotePatterns\":[]},\"devToolbar\":{\"enabled\":true},\"markdown\":{\"syntaxHighlight\":\"shiki\",\"shikiConfig\":{\"langs\":[],\"langAlias\":{},\"theme\":\"github-dark\",\"themes\":{},\"wrap\":false,\"transformers\":[]},\"remarkPlugins\":[],\"rehypePlugins\":[],\"remarkRehype\":{},\"gfm\":true,\"smartypants\":true},\"security\":{\"checkOrigin\":true},\"env\":{\"schema\":{},\"validateSecrets\":false},\"experimental\":{\"clientPrerender\":false,\"contentIntellisense\":false,\"responsiveImages\":false,\"serializeConfig\":false},\"legacy\":{\"collections\":false}}"]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spoko-design-system",
3
- "version": "0.5.6",
3
+ "version": "0.5.8",
4
4
  "private": false,
5
5
  "main": "./index.ts",
6
6
  "module": "./index.ts",
@@ -74,20 +74,20 @@
74
74
  "@iconify/vue": "^4.3.0",
75
75
  "@playform/compress": "^0.1.7",
76
76
  "@playform/inline": "^0.1.1",
77
- "@unocss/astro": "^66.0.0",
78
- "@unocss/preset-attributify": "^66.0.0",
79
- "@unocss/preset-typography": "^66.0.0",
80
- "@unocss/preset-uno": "^66.0.0",
81
- "@unocss/preset-web-fonts": "^66.0.0",
82
- "@unocss/preset-wind": "^66.0.0",
83
- "@unocss/reset": "^66.0.0",
77
+ "@unocss/astro": "66.1.0-beta.2",
78
+ "@unocss/preset-attributify": "66.1.0-beta.2",
79
+ "@unocss/preset-typography": "66.1.0-beta.2",
80
+ "@unocss/preset-uno": "66.1.0-beta.2",
81
+ "@unocss/preset-web-fonts": "66.1.0-beta.2",
82
+ "@unocss/preset-wind": "66.1.0-beta.2",
83
+ "@unocss/reset": "66.1.0-beta.2",
84
84
  "@vite-pwa/astro": "^0.5.0",
85
85
  "@vueuse/core": "^12.7.0",
86
86
  "astro-i18next": "1.0.0-beta.21",
87
87
  "astro-icon": "^1.1.5",
88
88
  "astro-meta-tags": "^0.3.1",
89
89
  "astro-navbar": "^2.3.9",
90
- "astro-pagefind": "^1.8.0",
90
+ "astro-pagefind": "^1.8.1",
91
91
  "astro-remote": "^0.3.3",
92
92
  "dotenv": "^16.4.7",
93
93
  "i18next": "^24.2.2",
@@ -96,13 +96,13 @@
96
96
  "i18next-http-backend": "^3.0.2",
97
97
  "i18next-vue": "^5.2.0",
98
98
  "swiper": "^11.2.4",
99
- "unocss": "^66.0.0",
99
+ "unocss": "66.1.0-beta.2",
100
100
  "vue": "^3.5.13"
101
101
  },
102
102
  "devDependencies": {
103
103
  "@types/gtag.js": "^0.0.20",
104
104
  "@types/node": "^22.13.5",
105
- "@unocss/transformer-variant-group": "^66.0.0",
105
+ "@unocss/transformer-variant-group": "66.1.0-beta.2",
106
106
  "@vitejs/plugin-vue": "^5.2.1",
107
107
  "@vue/compiler-sfc": "^3.5.13",
108
108
  "astro": "^5.3.1",
@@ -0,0 +1,171 @@
1
+ <script setup lang="ts">
2
+ import { computed, useAttrs } from 'vue';
3
+
4
+ interface InputProps {
5
+ id?: string;
6
+ name?: string;
7
+ label: string;
8
+ variant?: 'filled' | 'standard';
9
+ type?: string; // HTMLInputElement['type'] | 'textarea'
10
+ modelValue?: string | number; // For v-model compatibility
11
+ required?: boolean;
12
+ rows?: number; // rows for textarea
13
+ placeholder?: string;
14
+ error?: string | boolean;
15
+ success?: string | boolean;
16
+ size?: 'sm' | 'md' | 'lg'; // Size prop
17
+ class?: string; // additional classes
18
+ [key: string]: any; // To allow additional props
19
+ }
20
+
21
+ const props = withDefaults(defineProps<InputProps>(), {
22
+ id: () => `input-${Math.random().toString(36).substring(2, 9)}`,
23
+ name: undefined,
24
+ variant: 'standard',
25
+ type: 'text',
26
+ modelValue: '',
27
+ required: false,
28
+ rows: 3,
29
+ placeholder: ' ', // space is important for "floating label"
30
+ error: false,
31
+ success: false,
32
+ size: 'md',
33
+ class: ''
34
+ });
35
+
36
+ const emit = defineEmits(['update:modelValue', 'input', 'focus', 'blur']);
37
+
38
+ // Handle external attrs
39
+ const attrs = useAttrs();
40
+
41
+ // Compute wrapper class
42
+ const wrapperClass = computed(() => `input-wrapper-${props.variant}`);
43
+
44
+ // Compute input classes going back to direct arbitrary selectors
45
+ const inputClass = computed(() => {
46
+ // Base classes
47
+ const classes = ['input-base', `input-${props.variant}`];
48
+
49
+ // Focus and placeholder behavior - using direct arbitrary selectors
50
+ classes.push('[&:focus~label]:text-blue-light');
51
+ classes.push('[&:focus~label]:dark:text-blue-lightest');
52
+ classes.push('[&:focus~label]:scale-75');
53
+ classes.push('[&:placeholder-shown~label]:scale-100');
54
+ classes.push('[&:placeholder-shown~label]:translate-y-0');
55
+ classes.push('[&:not(:placeholder-shown)~label]:scale-75');
56
+
57
+ // Variant-specific behaviors
58
+ if (props.variant === 'standard') {
59
+ classes.push('[&:focus~label]:-translate-y-6');
60
+ classes.push('[&:focus~label]:start-0');
61
+ classes.push('[&:not(:placeholder-shown)~label]:-translate-y-6');
62
+ } else if (props.variant === 'filled') {
63
+ classes.push('[&:focus~label]:-translate-y-4');
64
+ classes.push('[&:not(:placeholder-shown)~label]:-translate-y-4');
65
+ }
66
+
67
+ // Additional classes
68
+ if (props.size) classes.push(`input-${props.size}`);
69
+ if (props.type === 'textarea') classes.push('input-textarea');
70
+ if (props.error) classes.push('input-error');
71
+ else if (props.success) classes.push('input-success');
72
+ if (props.class) classes.push(props.class);
73
+
74
+ return classes.join(' ');
75
+ });
76
+
77
+ // Compute label classes - important: add -translate-y for initial state explicitly
78
+ const labelClass = computed(() => {
79
+ // Base classes
80
+ const classes = ['input-label-base', `input-label-${props.variant}`];
81
+
82
+ // Explicitly add transform for initial state to ensure consistency
83
+ if (props.variant === 'standard') {
84
+ // Start in position and let focus/content move it
85
+ classes.push('translate-y-0');
86
+ } else if (props.variant === 'filled') {
87
+ // Start in position and let focus/content move it
88
+ classes.push('translate-y-0');
89
+ }
90
+
91
+ // Additional classes
92
+ if (props.size) classes.push(`input-label-${props.size}`);
93
+ if (props.error) classes.push('input-label-error');
94
+ else if (props.success) classes.push('input-label-success');
95
+
96
+ return classes.join(' ');
97
+ });
98
+
99
+ // Emit modelValue on input change
100
+ const handleInput = (event: Event) => {
101
+ const target = event.target as HTMLInputElement | HTMLTextAreaElement;
102
+ emit('update:modelValue', target.value);
103
+ emit('input', event);
104
+ };
105
+
106
+ // Forward focus and blur events
107
+ const handleFocus = (event: FocusEvent) => emit('focus', event);
108
+ const handleBlur = (event: FocusEvent) => emit('blur', event);
109
+ </script>
110
+
111
+ <template>
112
+ <div :class="wrapperClass">
113
+ <!-- Textarea field -->
114
+ <textarea
115
+ v-if="type === 'textarea'"
116
+ :id="id"
117
+ :name="name || id"
118
+ :rows="rows"
119
+ :required="required"
120
+ :class="inputClass"
121
+ :placeholder="placeholder"
122
+ :value="modelValue"
123
+ @input="handleInput"
124
+ @focus="handleFocus"
125
+ @blur="handleBlur"
126
+ v-bind="attrs"
127
+ ></textarea>
128
+
129
+ <!-- Input field -->
130
+ <input
131
+ v-else
132
+ :type="type"
133
+ :id="id"
134
+ :name="name || id"
135
+ :required="required"
136
+ :class="inputClass"
137
+ :placeholder="placeholder"
138
+ :value="modelValue"
139
+ @input="handleInput"
140
+ @focus="handleFocus"
141
+ @blur="handleBlur"
142
+ v-bind="attrs"
143
+ />
144
+
145
+ <!-- Label with guaranteed correct transform origin -->
146
+ <label
147
+ :for="id"
148
+ :class="labelClass"
149
+ style="transform-origin: top left;"
150
+ >
151
+ {{ label }}
152
+ <span v-if="required" class="text-red-500 ml-1">*</span>
153
+ </label>
154
+
155
+ <!-- Error message -->
156
+ <div
157
+ v-if="error && typeof error === 'string'"
158
+ class="input-error-message"
159
+ >
160
+ {{ error }}
161
+ </div>
162
+
163
+ <!-- Success message -->
164
+ <div
165
+ v-if="success && typeof success === 'string'"
166
+ class="input-success-message"
167
+ >
168
+ {{ success }}
169
+ </div>
170
+ </div>
171
+ </template>
@@ -3,7 +3,7 @@ title: Input
3
3
  layout: ../../layouts/MainLayout.astro
4
4
  ---
5
5
  import MainInput from '../../components/MainInput.vue'
6
- import Input from '../../components/Input.astro';
6
+ import Input from '../../components/Input.vue';
7
7
 
8
8
  # Basic Input text
9
9
 
@@ -45,4 +45,41 @@ import Input from '../../components/Input.astro';
45
45
  label="Floating standard"
46
46
  variant="standard"
47
47
  />
48
+ ```
49
+
50
+
51
+ # Floating Textarea
52
+ <div class="component-preview">
53
+ <div class="bg-white grid items-end w-full gap-6 md:grid-cols-3 px-4 py-6">
54
+ <Input
55
+ id="name-filled"
56
+ label="Floating filled"
57
+ variant="filled"
58
+ type="textarea"
59
+ />
60
+ <Input
61
+ id="email-standard"
62
+ label="Floating standard"
63
+ variant="standard"
64
+ type="textarea"
65
+ />
66
+ </div>
67
+ </div>
68
+
69
+ ```js
70
+ // Filled variant
71
+ <Input
72
+ id="name-filled"
73
+ label="Floating filled"
74
+ variant="filled"
75
+ type="textarea"
76
+ />
77
+
78
+ // Standard variant
79
+ <Input
80
+ id="email-standard"
81
+ label="Floating standard"
82
+ variant="standard"
83
+ type="textarea"
84
+ />
48
85
  ```
@@ -33,8 +33,61 @@ export function createSdsConfig(customConfig: CustomConfig = {}) {
33
33
  ...theme,
34
34
  ...(customConfig.theme || {})
35
35
  },
36
+ // safelist section with transform origin classes
36
37
  safelist: [
37
- 'md:grid-cols-product'
38
+ // Existing safelist items
39
+ 'md:grid-cols-product',
40
+
41
+ // Base classes
42
+ 'input-base',
43
+ 'input-label-base',
44
+
45
+ // Variants
46
+ 'input-standard',
47
+ 'input-filled',
48
+ 'input-label-standard',
49
+ 'input-label-filled',
50
+ 'input-wrapper-standard',
51
+ 'input-wrapper-filled',
52
+
53
+ // Sizes
54
+ 'input-sm',
55
+ 'input-md',
56
+ 'input-lg',
57
+ 'input-label-sm',
58
+ 'input-label-md',
59
+ 'input-label-lg',
60
+
61
+ // Special types
62
+ 'input-textarea',
63
+
64
+ // Status classes
65
+ 'input-error',
66
+ 'input-label-error',
67
+ 'input-error-message',
68
+ 'input-success',
69
+ 'input-label-success',
70
+ 'input-success-message',
71
+
72
+ // Essential transform classes
73
+ 'origin-top-left',
74
+ 'transform-gpu',
75
+ 'translate-y-0',
76
+ '-translate-y-4',
77
+ '-translate-y-6',
78
+
79
+ // Critical arbitrary selectors (all needed ones)
80
+ '[&:focus~label]:text-blue-light',
81
+ '[&:focus~label]:dark:text-blue-lightest',
82
+ '[&:focus~label]:scale-75',
83
+ '[&:focus~label]:-translate-y-6',
84
+ '[&:focus~label]:-translate-y-4',
85
+ '[&:focus~label]:start-0',
86
+ '[&:placeholder-shown~label]:scale-100',
87
+ '[&:placeholder-shown~label]:translate-y-0',
88
+ '[&:not(:placeholder-shown)~label]:scale-75',
89
+ '[&:not(:placeholder-shown)~label]:-translate-y-6',
90
+ '[&:not(:placeholder-shown)~label]:-translate-y-4',
38
91
  ],
39
92
  presets: [
40
93
  presetUno(),
@@ -5,6 +5,7 @@ import { layoutShortcuts } from './layout';
5
5
  import { componentShortcuts } from './components';
6
6
  import { productShortcuts } from './product';
7
7
  import { jumbotronShortcuts } from './jumbotron';
8
+ import { inputShortcuts } from './inputs';
8
9
 
9
10
  const convertToShortcuts = (shortcuts: string[][]): UserShortcuts => {
10
11
  return Object.fromEntries(shortcuts.map(([name, value]) => [name, value]));
@@ -16,4 +17,5 @@ export const shortcuts: UserShortcuts = {
16
17
  ...convertToShortcuts(componentShortcuts),
17
18
  ...convertToShortcuts(productShortcuts),
18
19
  ...convertToShortcuts(jumbotronShortcuts),
20
+ ...convertToShortcuts(inputShortcuts),
19
21
  };
@@ -0,0 +1,44 @@
1
+ // shortcuts/inputs.ts
2
+
3
+ export const inputShortcuts = [
4
+ // Base input styles
5
+ ['input-base', 'block w-full text-4.5 text-blue-medium border-0 border-b-1 border-neutral-light appearance-none dark:text-white dark:border-gray-600 dark:focus:border-blue-lightest focus:outline-none focus:ring-0 focus:border-blue-medium'],
6
+
7
+ // Base label styles with optimized transform settings
8
+ ['input-label-base', 'absolute text-sm text-slate-light dark:text-neutral-default origin-top-left transform-gpu transition-all duration-300 ease-in-out'],
9
+
10
+ // We're going back to direct arbitrary selectors in the input component
11
+ // since the grouped selectors seem to be causing issues with the focus behavior
12
+
13
+ // Variant styles for labels - position only, no transforms
14
+ ['input-label-standard', 'top-3 -z-10'],
15
+ ['input-label-filled', 'top-4 z-10 start-2.5'],
16
+
17
+ // Container styles
18
+ ['input-wrapper-standard', 'relative z-0'],
19
+ ['input-wrapper-filled', 'relative'],
20
+
21
+ // Input variant styles
22
+ ['input-standard', 'py-2.5 px-0 bg-transparent'],
23
+ ['input-filled', 'rounded-t-lg px-2.5 pb-2.5 pt-5 bg-gray-50 dark:bg-gray-700'],
24
+
25
+ // Special input types
26
+ ['input-textarea', 'resize-none'],
27
+
28
+ // Size variants
29
+ ['input-sm', 'text-sm'],
30
+ ['input-md', 'text-base'],
31
+ ['input-lg', 'text-lg py-3'],
32
+ ['input-label-sm', 'text-xs'],
33
+ ['input-label-md', 'text-sm'],
34
+ ['input-label-lg', 'text-base'],
35
+
36
+ // Status styles - grouped logically
37
+ ['input-error', 'border-red-500 focus:border-red-500 dark:border-red-400 dark:focus:border-red-400'],
38
+ ['input-label-error', 'text-red-500 dark:text-red-400'],
39
+ ['input-error-message', 'mt-1 text-xs text-red-500 dark:text-red-400'],
40
+
41
+ ['input-success', 'border-green-500 focus:border-green-500 dark:border-green-400 dark:focus:border-green-400'],
42
+ ['input-label-success', 'text-green-500 dark:text-green-400'],
43
+ ['input-success-message', 'mt-1 text-xs text-green-500 dark:text-green-400'],
44
+ ];
@@ -1,86 +0,0 @@
1
- ---
2
- // Input.astro
3
- interface Props {
4
- id: string;
5
- name?: string;
6
- label: string;
7
- variant?: 'filled' | 'standard';
8
- type?: HTMLInputElement['type'] | 'textarea'; // support textarea
9
- value?: string;
10
- required?: boolean;
11
- rows?: number; // rows for textarea
12
- placeholder?: string;
13
- class?: string; // additional classes
14
- }
15
-
16
- const {
17
- id,
18
- name,
19
- label,
20
- variant = 'standard',
21
- type = 'text',
22
- value = '',
23
- required = false,
24
- rows = 3,
25
- placeholder = " ", //space for "floating label")
26
- class: additionalClasses = "",
27
- ...restProps
28
- } = Astro.props;
29
-
30
- // Common classes for both variants
31
- const baseInputClasses = "block w-full text-4.5 text-blue-medium border-0 border-b-1 border-neutral-light appearance-none dark:text-white dark:border-gray-600 dark:focus:border-blue-lightest focus:outline-none focus:ring-0 focus:border-blue-medium peer";
32
-
33
- const baseLabelClasses = "absolute text-sm text-slate-medium dark:text-neutral-default transform scale-75 origin-[0] peer-focus:text-blue-medium peer-focus:dark:text-blue-lightest peer-placeholder-shown:scale-100 peer-focus:scale-75 rtl:peer-focus:translate-x-1/4 rtl:peer-focus:left-auto transition-all duration-300 ease-in-out";
34
-
35
- // Variant specific classes
36
- const variantClasses = {
37
- filled: {
38
- wrapper: "relative",
39
- input: `${baseInputClasses} rounded-t-lg px-2.5 pb-2.5 pt-5 bg-gray-50 dark:bg-gray-700 ${additionalClasses}`,
40
- label: `${baseLabelClasses} -translate-y-4 top-4 z-10 start-2.5 peer-placeholder-shown:translate-y-0 peer-focus:-translate-y-4`
41
- },
42
- standard: {
43
- wrapper: "relative z-0",
44
- input: `${baseInputClasses} py-2.5 px-0 bg-transparent ${additionalClasses}`,
45
- label: `${baseLabelClasses} -translate-y-6 top-3 -z-10 peer-focus:start-0 peer-placeholder-shown:translate-y-0 peer-focus:-translate-y-6`
46
- }
47
- };
48
-
49
- const classes = variantClasses[variant];
50
-
51
- // Dla textarea modyfikujemy nieco style
52
- const textareaClasses = type === 'textarea'
53
- ? classes.input.replace('border-b-1', 'border-1') + ' resize-none'
54
- : classes.input;
55
- ---
56
-
57
- <div class={classes.wrapper}>
58
- {type === 'textarea' ? (
59
- <textarea
60
- name={name}
61
- id={id}
62
- rows={rows}
63
- required={required}
64
- class={textareaClasses}
65
- placeholder={placeholder}
66
- {...restProps}
67
- >{value}</textarea>
68
- ) : (
69
- <input
70
- type={type}
71
- name={name}
72
- id={id}
73
- value={value}
74
- required={required}
75
- class={classes.input}
76
- placeholder={placeholder}
77
- {...restProps}
78
- />
79
- )}
80
- <label
81
- for={id}
82
- class={classes.label}
83
- >
84
- {label}
85
- </label>
86
- </div>