vue3-float-label 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.
- package/README.md +82 -0
- package/dist/float-label.svg +13 -0
- package/dist/style.css +86 -0
- package/dist/vue3-float-label.es.js +73 -0
- package/dist/vue3-float-label.umd.js +1 -0
- package/package.json +44 -0
package/README.md
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Vue 3 Float Label
|
|
2
|
+
|
|
3
|
+
Float Label like package for Vue 3. Written in Composition API and easily customizable with CSS.
|
|
4
|
+
|
|
5
|
+

|
|
6
|
+
|
|
7
|
+
```html
|
|
8
|
+
<float-label>
|
|
9
|
+
<input type="text" placeholder="Label">
|
|
10
|
+
</float-label>
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
Install the package via npm:
|
|
16
|
+
```sh
|
|
17
|
+
$ npm install vue3-float-label
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Usage
|
|
21
|
+
|
|
22
|
+
To use the component locally within a single component:
|
|
23
|
+
|
|
24
|
+
```html
|
|
25
|
+
<template>
|
|
26
|
+
<FloatLabel label="Your Name">
|
|
27
|
+
<input type="text" placeholder="Enter your name" />
|
|
28
|
+
</FloatLabel>
|
|
29
|
+
</template>
|
|
30
|
+
|
|
31
|
+
<script setup>
|
|
32
|
+
import { FloatLabel } from 'vue3-float-label';
|
|
33
|
+
import 'vue3-float-label/dist/style.css'; // Import the stylesheet
|
|
34
|
+
</script>
|
|
35
|
+
```
|
|
36
|
+
### Props
|
|
37
|
+
<hr>
|
|
38
|
+
|
|
39
|
+
#### `label`
|
|
40
|
+
|
|
41
|
+
**String** - If you want a different label than the placeholder text. You can also pass it dynamic text based on another value (i.e. `'Start time on ' + beginningDate` )
|
|
42
|
+
|
|
43
|
+
Example:
|
|
44
|
+
```html
|
|
45
|
+
<float-label :label="'Your Name'">
|
|
46
|
+
<input type="text" placeholder="Enter your name">
|
|
47
|
+
</float-label>
|
|
48
|
+
```
|
|
49
|
+
<hr>
|
|
50
|
+
|
|
51
|
+
#### `float`
|
|
52
|
+
|
|
53
|
+
**Boolean** - Use this to manually control when the label is floated. This disables all other detection and `on-focus` prop.
|
|
54
|
+
|
|
55
|
+
```html
|
|
56
|
+
<float-label :float="name ? true : false">
|
|
57
|
+
<input v-model="name" type="text" placeholder="Enter your name">
|
|
58
|
+
</float-label>
|
|
59
|
+
```
|
|
60
|
+
<hr>
|
|
61
|
+
#### `on-focus`
|
|
62
|
+
|
|
63
|
+
**Boolean** - Optional behavior that triggers the float when the field is in focus instead of when content is typed
|
|
64
|
+
|
|
65
|
+
## Customizing Styles
|
|
66
|
+
|
|
67
|
+
The `vue3-float-label` component uses CSS variables to allow easy customization of its appearance. You can either copy the styles and modify them yourself, or there are some variables you can override. Below are the default CSS variables defined in the component that you can override in your stylesheet:
|
|
68
|
+
|
|
69
|
+
```css
|
|
70
|
+
:root {
|
|
71
|
+
--vfl-padding-y: 17px;
|
|
72
|
+
--vfl-padding-x: 16px;
|
|
73
|
+
--vfl-label-color: #6c6c6c;
|
|
74
|
+
--border-color: lightgray;
|
|
75
|
+
--border-width: 1px;
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
## License
|
|
81
|
+
|
|
82
|
+
This project is licensed under the MIT License.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<svg width="128px" height="128px" viewBox="0 0 128 128" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
3
|
+
<title>float-label</title>
|
|
4
|
+
<g id="float-label" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
|
5
|
+
<rect id="Rectangle" stroke="#979797" stroke-width="4" fill="#FFFFFF" x="2" y="2" width="124" height="124" rx="8"></rect>
|
|
6
|
+
<text id="label" font-family="OpenSans-Semibold, Open Sans" font-size="33.8253887" font-weight="500" fill="#4885FF">
|
|
7
|
+
<tspan x="12" y="39">label</tspan>
|
|
8
|
+
</text>
|
|
9
|
+
<text id="Text" font-family="OpenSans-Semibold, Open Sans" font-size="50.738083" font-weight="500" fill="#000000">
|
|
10
|
+
<tspan x="12" y="93">Text</tspan>
|
|
11
|
+
</text>
|
|
12
|
+
</g>
|
|
13
|
+
</svg>
|
package/dist/style.css
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
--vfl-padding-y: 17px;
|
|
3
|
+
--vfl-padding-x: 16px;
|
|
4
|
+
--vfl-label-color: #6c6c6c;
|
|
5
|
+
--border-color: lightgray;
|
|
6
|
+
--border-width: 1px;
|
|
7
|
+
}
|
|
8
|
+
.float-label {
|
|
9
|
+
display: flex;
|
|
10
|
+
flex-direction: column;
|
|
11
|
+
position: relative;
|
|
12
|
+
width: 100%;
|
|
13
|
+
}
|
|
14
|
+
.float-label__label__bg {
|
|
15
|
+
background-image: linear-gradient(180deg, #FFF 55%, #ffffff00 100%);
|
|
16
|
+
display: block;
|
|
17
|
+
pointer-events: none;
|
|
18
|
+
position: absolute;
|
|
19
|
+
top: 2px;
|
|
20
|
+
right: calc(var(--vfl-padding-x) - 2px);
|
|
21
|
+
left: calc(var(--vfl-padding-x) - 2px);
|
|
22
|
+
height: 26px;
|
|
23
|
+
opacity: 0;
|
|
24
|
+
transition: opacity 0.3s;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.float-label__label__text {
|
|
28
|
+
transition: all .2s;
|
|
29
|
+
opacity: 1;
|
|
30
|
+
left: calc(var(--vfl-padding-x) -2px);
|
|
31
|
+
display: table;
|
|
32
|
+
}
|
|
33
|
+
.float-label--fade-anim .float-label__label__text {
|
|
34
|
+
opacity: 0;
|
|
35
|
+
}
|
|
36
|
+
.float-label input, .float-label textarea, .float-label select {
|
|
37
|
+
padding: var(--vfl-padding-y) var(--vfl-padding-x);
|
|
38
|
+
width: 100%;
|
|
39
|
+
font-size: 16px;
|
|
40
|
+
line-height: 20px;
|
|
41
|
+
border: var(--border-width) solid var(--border-color);
|
|
42
|
+
margin-bottom: 6px;
|
|
43
|
+
-webkit-appearance: none;
|
|
44
|
+
-moz-appearance: none;
|
|
45
|
+
}
|
|
46
|
+
.float-label select {
|
|
47
|
+
background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath d='M7 10l5 5 5-5z'/%3E%3C/svg%3E") right 16px top 22px no-repeat;
|
|
48
|
+
}
|
|
49
|
+
.float-label input:focus, .float-label textarea:focus, .float-label select:focus {
|
|
50
|
+
outline: 2px solid #80b4d3;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.float-label select {
|
|
54
|
+
background-position: top 7px right -3px;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.float-label input::placeholder, .float-label > textarea::placeholder, .float-label select::placeholder, .float-label__label__text {
|
|
58
|
+
position: absolute;
|
|
59
|
+
top: var(--vfl-padding-y);
|
|
60
|
+
left: var(--vfl-padding-x);
|
|
61
|
+
display: block;
|
|
62
|
+
line-height: 24px;
|
|
63
|
+
color: var(--vfl-label-color);
|
|
64
|
+
}
|
|
65
|
+
.float-label input::placeholder, .float-label textarea::placeholder {
|
|
66
|
+
color: transparent;
|
|
67
|
+
transition: opacity 0.2s;
|
|
68
|
+
}
|
|
69
|
+
.float-label--fade-anim input::placeholder, .float-label--fade-anim textarea::placeholder {
|
|
70
|
+
color: gray;
|
|
71
|
+
}
|
|
72
|
+
.float-label--fixed .float-label__label__text, .float-label--on-focus input:focus ~ .float-label__label .float-label__label__text {
|
|
73
|
+
transform-origin: top left;
|
|
74
|
+
transform: scale(.85) translateY(-18px);
|
|
75
|
+
opacity: 1;
|
|
76
|
+
pointer-events: none;
|
|
77
|
+
}
|
|
78
|
+
.float-label--fixed input::placeholder, .float-label--fixed textarea::placeholder, .float-label--on-focus input:focus::placeholder {
|
|
79
|
+
opacity: 0;
|
|
80
|
+
}
|
|
81
|
+
.float-label--fixed .float-label__label__bg {
|
|
82
|
+
opacity: 1;
|
|
83
|
+
}
|
|
84
|
+
.float-label--no-click {
|
|
85
|
+
pointer-events: none;
|
|
86
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { ref as t, computed as m, watch as I, onMounted as N, onBeforeUnmount as M, openBlock as v, createElementBlock as d, normalizeClass as g, renderSlot as W, unref as u, createElementVNode as x, toDisplayString as E, createCommentVNode as q, createTextVNode as A } from "vue";
|
|
2
|
+
const H = {
|
|
3
|
+
key: 0,
|
|
4
|
+
class: "float-label__label float-label--no-click"
|
|
5
|
+
}, R = { class: "float-label__label__text" }, z = ["for"], D = {
|
|
6
|
+
key: 0,
|
|
7
|
+
class: "float-label__label__bg"
|
|
8
|
+
}, O = { class: "float-label__label__text" }, P = "[type=date], [type=datetime-local], [type=datetime], [type=email], [type=month], [type=number], [type=password], [type=search], [type=tel], [type=text], [type=time], [type=url], [type=week], textarea, select", $ = {
|
|
9
|
+
__name: "FloatLabel",
|
|
10
|
+
props: {
|
|
11
|
+
label: { type: String, default: "" },
|
|
12
|
+
float: { type: Boolean, default: null },
|
|
13
|
+
onFocus: { type: Boolean, default: !1 }
|
|
14
|
+
},
|
|
15
|
+
setup(F) {
|
|
16
|
+
const s = F;
|
|
17
|
+
let b = t(null), c = t(""), r = t(""), k = t(null), e = t({}), n = t(""), a = t(""), i = t(!1), p = t(!1), _ = t(!1);
|
|
18
|
+
const y = m(() => s.label), L = m(() => a.value === "select" ? !0 : r.value && c.value !== r.value), w = m(() => {
|
|
19
|
+
let l = !1;
|
|
20
|
+
return s.float ? l = !0 : l = i.value && i.value !== "0", l;
|
|
21
|
+
}), h = () => {
|
|
22
|
+
c.value = y.value ? s.label : r.value;
|
|
23
|
+
}, C = () => e.value.getAttribute("id"), T = () => ("10000000-1000-4000-8000" + -1e11).replace(/[018]/g, (o) => (o ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> o / 4).toString(16)), V = () => {
|
|
24
|
+
switch (a.value) {
|
|
25
|
+
case "input":
|
|
26
|
+
case "textarea":
|
|
27
|
+
return e.value.placeholder;
|
|
28
|
+
case "select":
|
|
29
|
+
let l = e.value.querySelector("option:disabled");
|
|
30
|
+
return l ? l.innerHTML : "";
|
|
31
|
+
default:
|
|
32
|
+
return "";
|
|
33
|
+
}
|
|
34
|
+
}, S = () => {
|
|
35
|
+
n.value || (n.value = T(), e.value.id = n.value);
|
|
36
|
+
}, B = () => {
|
|
37
|
+
a.value === "select" ? e.value.addEventListener("change", f) : e.value.addEventListener("input", f);
|
|
38
|
+
}, U = () => {
|
|
39
|
+
a.value === "select" ? e.value.removeEventListener("change", f) : e.value.removeEventListener("input", f);
|
|
40
|
+
}, f = (l) => {
|
|
41
|
+
i.value = l.target.value.length > 0;
|
|
42
|
+
};
|
|
43
|
+
return I(y, (l, o) => {
|
|
44
|
+
h();
|
|
45
|
+
}), N(() => {
|
|
46
|
+
k.value = s.label, e.value = b.value.querySelector(P), p.value = !!e.value, _.value = s.float === null, p.value ? (n.value = C(), a.value = e.value ? e.value.tagName.toLowerCase() : "", i.value = !!e.value.value, r.value = V(), S(), _.value === !0 && setTimeout(() => {
|
|
47
|
+
B();
|
|
48
|
+
}, 200)) : r.value = "", h();
|
|
49
|
+
}), M(() => {
|
|
50
|
+
p.value && U();
|
|
51
|
+
}), (l, o) => (v(), d("div", {
|
|
52
|
+
class: g(["float-label", { "float-label--fade-anim": L.value, "float-label--on-focus": s.onFocus, "float-label--fixed": w.value }]),
|
|
53
|
+
ref_key: "root",
|
|
54
|
+
ref: b
|
|
55
|
+
}, [
|
|
56
|
+
W(l.$slots, "default"),
|
|
57
|
+
u(a) === "" ? (v(), d("div", H, [
|
|
58
|
+
x("span", R, E(u(c)), 1)
|
|
59
|
+
])) : (v(), d("label", {
|
|
60
|
+
key: 1,
|
|
61
|
+
class: g(["float-label__label", { "float-label--no-click": u(a) === "select" }]),
|
|
62
|
+
for: u(n)
|
|
63
|
+
}, [
|
|
64
|
+
u(a) === "textarea" ? (v(), d("span", D)) : q("", !0),
|
|
65
|
+
o[0] || (o[0] = A()),
|
|
66
|
+
x("span", O, E(u(c)), 1)
|
|
67
|
+
], 10, z))
|
|
68
|
+
], 2));
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
export {
|
|
72
|
+
$ as FloatLabel
|
|
73
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
(function(o,e){typeof exports=="object"&&typeof module<"u"?e(exports,require("vue")):typeof define=="function"&&define.amd?define(["exports","vue"],e):(o=typeof globalThis<"u"?globalThis:o||self,e(o.Vue3FloatLabel={},o.Vue))})(this,function(o,e){"use strict";const _={key:0,class:"float-label__label float-label--no-click"},h={class:"float-label__label__text"},g=["for"],v={key:0,class:"float-label__label__bg"},E={class:"float-label__label__text"},k="[type=date], [type=datetime-local], [type=datetime], [type=email], [type=month], [type=number], [type=password], [type=search], [type=tel], [type=text], [type=time], [type=url], [type=week], textarea, select",x={__name:"FloatLabel",props:{label:{type:String,default:""},float:{type:Boolean,default:null},onFocus:{type:Boolean,default:!1}},setup(F){const r=F;let p=e.ref(null),u=e.ref(""),s=e.ref(""),B=e.ref(null),l=e.ref({}),c=e.ref(""),a=e.ref(""),f=e.ref(!1),d=e.ref(!1),m=e.ref(!1);const b=e.computed(()=>r.label),L=e.computed(()=>a.value==="select"?!0:s.value&&u.value!==s.value),T=e.computed(()=>{let t=!1;return r.float?t=!0:t=f.value&&f.value!=="0",t}),y=()=>{u.value=b.value?r.label:s.value},V=()=>l.value.getAttribute("id"),S=()=>("10000000-1000-4000-8000"+-1e11).replace(/[018]/g,n=>(n^crypto.getRandomValues(new Uint8Array(1))[0]&15>>n/4).toString(16)),C=()=>{switch(a.value){case"input":case"textarea":return l.value.placeholder;case"select":let t=l.value.querySelector("option:disabled");return t?t.innerHTML:"";default:return""}},w=()=>{c.value||(c.value=S(),l.value.id=c.value)},N=()=>{a.value==="select"?l.value.addEventListener("change",i):l.value.addEventListener("input",i)},U=()=>{a.value==="select"?l.value.removeEventListener("change",i):l.value.removeEventListener("input",i)},i=t=>{f.value=t.target.value.length>0};return e.watch(b,(t,n)=>{y()}),e.onMounted(()=>{B.value=r.label,l.value=p.value.querySelector(k),d.value=!!l.value,m.value=r.float===null,d.value?(c.value=V(),a.value=l.value?l.value.tagName.toLowerCase():"",f.value=!!l.value.value,s.value=C(),w(),m.value===!0&&setTimeout(()=>{N()},200)):s.value="",y()}),e.onBeforeUnmount(()=>{d.value&&U()}),(t,n)=>(e.openBlock(),e.createElementBlock("div",{class:e.normalizeClass(["float-label",{"float-label--fade-anim":L.value,"float-label--on-focus":r.onFocus,"float-label--fixed":T.value}]),ref_key:"root",ref:p},[e.renderSlot(t.$slots,"default"),e.unref(a)===""?(e.openBlock(),e.createElementBlock("div",_,[e.createElementVNode("span",h,e.toDisplayString(e.unref(u)),1)])):(e.openBlock(),e.createElementBlock("label",{key:1,class:e.normalizeClass(["float-label__label",{"float-label--no-click":e.unref(a)==="select"}]),for:e.unref(c)},[e.unref(a)==="textarea"?(e.openBlock(),e.createElementBlock("span",v)):e.createCommentVNode("",!0),n[0]||(n[0]=e.createTextVNode()),e.createElementVNode("span",E,e.toDisplayString(e.unref(u)),1)],10,g))],2))}};o.FloatLabel=x,Object.defineProperty(o,Symbol.toStringTag,{value:"Module"})});
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "vue3-float-label",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A Vue 3 component for float labels.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"author": "Justin Moreland",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"vue",
|
|
9
|
+
"vue 3",
|
|
10
|
+
"float-label",
|
|
11
|
+
"component"
|
|
12
|
+
],
|
|
13
|
+
"license": "MIT",
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "https://github.com/jwmoreland/vue3-float-label.git"
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"dist",
|
|
20
|
+
"dist/style.css"
|
|
21
|
+
],
|
|
22
|
+
"main": "dist/vue3-float-label.umd.js",
|
|
23
|
+
"module": "dist/vue3-float-label.es.js",
|
|
24
|
+
"exports": {
|
|
25
|
+
".": {
|
|
26
|
+
"import": "./dist/vue3-float-label.es.js",
|
|
27
|
+
"require": "./dist/vue3-float-label.umd.js"
|
|
28
|
+
},
|
|
29
|
+
"./style.css": "./dist/style.css"
|
|
30
|
+
},
|
|
31
|
+
"scripts": {
|
|
32
|
+
"dev": "vite",
|
|
33
|
+
"watch": "vite",
|
|
34
|
+
"build": "vite build && cp src/style.css dist/style.css",
|
|
35
|
+
"preview": "vite preview"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"vue": "^3.5.13"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@vitejs/plugin-vue": "^5.2.1",
|
|
42
|
+
"vite": "^6.0.7"
|
|
43
|
+
}
|
|
44
|
+
}
|