@grayscale-dev/dragon 0.1.1 → 0.1.3

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 CHANGED
@@ -24,7 +24,8 @@ Covers value/property sync, native `input`/`change` events, form association, fo
24
24
  import '@grayscale-dev/dragon';
25
25
  </script>
26
26
 
27
- <dui-input id="name" placeholder="Your name"></dui-input>
27
+ <dui-input id="name" label="Name" label-position="above"></dui-input>
28
+ <dui-input id="email" label="Email" label-position="floating"></dui-input>
28
29
 
29
30
  <script type="module">
30
31
  const el = document.querySelector('#name');
@@ -42,7 +43,8 @@ import '@grayscale-dev/dragon';
42
43
  export function Demo() {
43
44
  return (
44
45
  <dui-input
45
- placeholder="Email"
46
+ label="Email"
47
+ label-position="floating"
46
48
  onInput={(e) => {
47
49
  const el = e.currentTarget as HTMLInputElement & { value: string };
48
50
  console.log(el.value);
@@ -65,7 +67,7 @@ function handleInput(e: Event) {
65
67
  </script>
66
68
 
67
69
  <template>
68
- <dui-input placeholder="Email" @input="handleInput" />
70
+ <dui-input label="Email" label-position="floating" @input="handleInput" />
69
71
  </template>
70
72
  ```
71
73
 
@@ -81,15 +83,19 @@ CSS custom properties:
81
83
  - `--ui-input-color`
82
84
  - `--ui-input-placeholder-color`
83
85
  - `--ui-input-focus-ring`
86
+ - `--ui-input-label-font-size`
87
+
88
+ Default size values in `<dui-input>` are pixel-based.
84
89
 
85
90
  Example:
86
91
 
87
92
  ```css
88
93
  dui-input {
89
- --ui-input-padding: 0.75rem 1rem;
94
+ --ui-input-padding: 12px 16px;
90
95
  --ui-input-border: 1px solid #94a3b8;
91
96
  --ui-input-radius: 999px;
92
97
  --ui-input-focus-ring: 0 0 0 3px rgba(59, 130, 246, 0.35);
98
+ --ui-input-label-font-size: 14px;
93
99
  }
94
100
 
95
101
  dui-input::part(input) {
@@ -97,6 +103,14 @@ dui-input::part(input) {
97
103
  }
98
104
  ```
99
105
 
106
+ **Labels**
107
+
108
+ `label` and `label-position` control the built-in label:
109
+
110
+ - `label-position="above"` (default): label sits above the field.
111
+ - `label-position="floating"`: label sits like a placeholder and floats to the top-left on focus or when the input has a value.
112
+ - Floating mode hides placeholder text to avoid overlap with the label.
113
+
100
114
  **Form Behavior**
101
115
 
102
116
  `<dui-input>` dispatches native `input` and `change` events (bubbling + composed) and exposes a `value` property on the custom element. It also integrates with forms via the Form-Associated Custom Elements API where supported.
@@ -9,6 +9,8 @@ export declare class DuiInput extends LitElement {
9
9
  required: boolean;
10
10
  type: string;
11
11
  autocomplete?: string;
12
+ label: string;
13
+ labelPosition: 'floating' | 'above';
12
14
  private inputEl?;
13
15
  private internals?;
14
16
  private defaultValue?;
package/dist/index.js CHANGED
@@ -1,23 +1,23 @@
1
- import { css as d, LitElement as h, html as c } from "lit";
2
- import { property as s, query as f, customElement as b } from "lit/decorators.js";
3
- import { ifDefined as p } from "lit/directives/if-defined.js";
4
- var m = Object.defineProperty, v = Object.getOwnPropertyDescriptor, i = (e, r, l, o) => {
5
- for (var a = o > 1 ? void 0 : o ? v(r, l) : r, n = e.length - 1, u; n >= 0; n--)
6
- (u = e[n]) && (a = (o ? u(r, l, a) : u(a)) || a);
7
- return o && a && m(r, l, a), a;
1
+ import { css as h, LitElement as c, html as d } from "lit";
2
+ import { property as l, query as f, customElement as b } from "lit/decorators.js";
3
+ import { ifDefined as u } from "lit/directives/if-defined.js";
4
+ var g = Object.defineProperty, v = Object.getOwnPropertyDescriptor, a = (t, i, n, r) => {
5
+ for (var o = r > 1 ? void 0 : r ? v(i, n) : i, s = t.length - 1, p; s >= 0; s--)
6
+ (p = t[s]) && (o = (r ? p(i, n, o) : p(o)) || o);
7
+ return r && o && g(i, n, o), o;
8
8
  };
9
- let t = class extends h {
9
+ let e = class extends c {
10
10
  constructor() {
11
- super(), this.value = "", this.placeholder = "", this.name = "", this.disabled = !1, this.required = !1, this.type = "text", "attachInternals" in this && (this.internals = this.attachInternals());
11
+ super(), this.value = "", this.placeholder = "", this.name = "", this.disabled = !1, this.required = !1, this.type = "text", this.label = "", this.labelPosition = "above", "attachInternals" in this && (this.internals = this.attachInternals());
12
12
  }
13
13
  connectedCallback() {
14
14
  super.connectedCallback(), this.defaultValue === void 0 && (this.defaultValue = this.getAttribute("value") ?? ""), this.syncFormValue();
15
15
  }
16
- updated(e) {
17
- (e.has("value") || e.has("disabled")) && this.syncFormValue();
16
+ updated(t) {
17
+ (t.has("value") || t.has("disabled")) && this.syncFormValue();
18
18
  }
19
- focus(e) {
20
- this.inputEl?.focus(e);
19
+ focus(t) {
20
+ this.inputEl?.focus(t);
21
21
  }
22
22
  blur() {
23
23
  this.inputEl?.blur();
@@ -25,11 +25,11 @@ let t = class extends h {
25
25
  formResetCallback() {
26
26
  this.value = this.defaultValue ?? "";
27
27
  }
28
- formStateRestoreCallback(e) {
29
- typeof e == "string" && (this.value = e);
28
+ formStateRestoreCallback(t) {
29
+ typeof t == "string" && (this.value = t);
30
30
  }
31
- formDisabledCallback(e) {
32
- this.disabled = e;
31
+ formDisabledCallback(t) {
32
+ this.disabled = t;
33
33
  }
34
34
  syncFormValue() {
35
35
  if (this.internals) {
@@ -40,53 +40,93 @@ let t = class extends h {
40
40
  this.internals.setFormValue(this.value);
41
41
  }
42
42
  }
43
- handleInput(e) {
44
- e.stopPropagation();
45
- const r = e.target;
46
- this.value = r.value, this.dispatchEvent(new Event("input", { bubbles: !0, composed: !0 }));
43
+ handleInput(t) {
44
+ t.stopPropagation();
45
+ const i = t.target;
46
+ this.value = i.value, this.dispatchEvent(new Event("input", { bubbles: !0, composed: !0 }));
47
47
  }
48
- handleChange(e) {
49
- e.stopPropagation();
50
- const r = e.target;
51
- this.value = r.value, this.dispatchEvent(new Event("change", { bubbles: !0, composed: !0 }));
48
+ handleChange(t) {
49
+ t.stopPropagation();
50
+ const i = t.target;
51
+ this.value = i.value, this.dispatchEvent(new Event("change", { bubbles: !0, composed: !0 }));
52
52
  }
53
- handleKeydown(e) {
54
- if (e.key !== "Enter") return;
55
- const r = e.target;
56
- this.value = r.value, this.dispatchEvent(new Event("change", { bubbles: !0, composed: !0 }));
53
+ handleKeydown(t) {
54
+ if (t.key !== "Enter") return;
55
+ const i = t.target;
56
+ this.value = i.value, this.dispatchEvent(new Event("change", { bubbles: !0, composed: !0 }));
57
57
  }
58
58
  render() {
59
- return c`
60
- <input
61
- part="input"
62
- .value=${this.value}
63
- .type=${this.type}
64
- .name=${this.name}
65
- ?disabled=${this.disabled}
66
- ?required=${this.required}
67
- placeholder=${p(this.placeholder || void 0)}
68
- autocomplete=${p(this.autocomplete)}
69
- @input=${this.handleInput}
70
- @change=${this.handleChange}
71
- @keydown=${this.handleKeydown}
72
- />
59
+ const t = this.label.length > 0, i = t && this.labelPosition === "floating", n = this.value.length > 0, r = i ? void 0 : this.placeholder || void 0;
60
+ return d`
61
+ <div class="field ${i ? "floating" : ""}" data-has-value=${n}>
62
+ ${t ? d`<label for="input">${this.label}</label>` : null}
63
+ <input
64
+ id="input"
65
+ part="input"
66
+ .value=${this.value}
67
+ .type=${this.type}
68
+ .name=${this.name}
69
+ ?disabled=${this.disabled}
70
+ ?required=${this.required}
71
+ placeholder=${u(r)}
72
+ autocomplete=${u(this.autocomplete)}
73
+ aria-label=${u(this.label || this.placeholder || void 0)}
74
+ @input=${this.handleInput}
75
+ @change=${this.handleChange}
76
+ @keydown=${this.handleKeydown}
77
+ />
78
+ </div>
73
79
  `;
74
80
  }
75
81
  };
76
- t.formAssociated = !0;
77
- t.styles = d`
82
+ e.formAssociated = !0;
83
+ e.styles = h`
78
84
  :host {
79
85
  display: inline-block;
80
86
  width: 100%;
81
87
  }
82
88
 
89
+ .field {
90
+ position: relative;
91
+ display: block;
92
+ width: 100%;
93
+ }
94
+
95
+ label {
96
+ display: block;
97
+ font-size: var(--ui-input-label-font-size, 14px);
98
+ color: var(--ui-input-label-color, #475569);
99
+ margin-bottom: 6px;
100
+ line-height: 1.2;
101
+ }
102
+
103
+ .field.floating label {
104
+ position: absolute;
105
+ left: var(--ui-input-floating-label-left, 12px);
106
+ top: 50%;
107
+ transform: translateY(-50%);
108
+ margin: 0;
109
+ padding: 0 4px;
110
+ background: var(--ui-input-bg, #ffffff);
111
+ color: var(--ui-input-placeholder-color, #9aa4b2);
112
+ pointer-events: none;
113
+ transition: transform 120ms ease, top 120ms ease, color 120ms ease;
114
+ }
115
+
116
+ .field.floating[data-has-value='true'] label,
117
+ :host(:focus-within) .field.floating label {
118
+ top: 6px;
119
+ transform: translateY(0) scale(0.85);
120
+ color: var(--ui-input-label-color, #475569);
121
+ }
122
+
83
123
  input {
84
124
  box-sizing: border-box;
85
125
  width: 100%;
86
- padding: var(--ui-input-padding, 0.5rem 0.75rem);
87
- font-size: var(--ui-input-font-size, 1rem);
126
+ padding: var(--ui-input-padding, 8px 12px);
127
+ font-size: var(--ui-input-font-size, 16px);
88
128
  border: var(--ui-input-border, 1px solid #c6ccd5);
89
- border-radius: var(--ui-input-radius, 0.5rem);
129
+ border-radius: var(--ui-input-radius, 8px);
90
130
  background: var(--ui-input-bg, #ffffff);
91
131
  color: var(--ui-input-color, #1f2937);
92
132
  outline: none;
@@ -97,6 +137,10 @@ t.styles = d`
97
137
  color: var(--ui-input-placeholder-color, #9aa4b2);
98
138
  }
99
139
 
140
+ .field.floating input::placeholder {
141
+ color: transparent;
142
+ }
143
+
100
144
  input:focus {
101
145
  box-shadow: var(--ui-input-focus-ring, 0 0 0 3px rgba(24, 98, 255, 0.25));
102
146
  }
@@ -105,34 +149,47 @@ t.styles = d`
105
149
  opacity: 0.6;
106
150
  cursor: not-allowed;
107
151
  }
152
+
153
+ .field.floating input {
154
+ padding-top: var(--ui-input-floating-padding-top, 15px);
155
+ padding-right: var(--ui-input-floating-padding-right, 12px);
156
+ padding-bottom: var(--ui-input-floating-padding-bottom, 7px);
157
+ padding-left: var(--ui-input-floating-padding-left, 12px);
158
+ }
108
159
  `;
109
- i([
110
- s({ type: String })
111
- ], t.prototype, "value", 2);
112
- i([
113
- s({ type: String })
114
- ], t.prototype, "placeholder", 2);
115
- i([
116
- s({ type: String, reflect: !0 })
117
- ], t.prototype, "name", 2);
118
- i([
119
- s({ type: Boolean, reflect: !0 })
120
- ], t.prototype, "disabled", 2);
121
- i([
122
- s({ type: Boolean, reflect: !0 })
123
- ], t.prototype, "required", 2);
124
- i([
125
- s({ type: String, reflect: !0 })
126
- ], t.prototype, "type", 2);
127
- i([
128
- s({ type: String, reflect: !0 })
129
- ], t.prototype, "autocomplete", 2);
130
- i([
160
+ a([
161
+ l({ type: String })
162
+ ], e.prototype, "value", 2);
163
+ a([
164
+ l({ type: String })
165
+ ], e.prototype, "placeholder", 2);
166
+ a([
167
+ l({ type: String, reflect: !0 })
168
+ ], e.prototype, "name", 2);
169
+ a([
170
+ l({ type: Boolean, reflect: !0 })
171
+ ], e.prototype, "disabled", 2);
172
+ a([
173
+ l({ type: Boolean, reflect: !0 })
174
+ ], e.prototype, "required", 2);
175
+ a([
176
+ l({ type: String, reflect: !0 })
177
+ ], e.prototype, "type", 2);
178
+ a([
179
+ l({ type: String, reflect: !0 })
180
+ ], e.prototype, "autocomplete", 2);
181
+ a([
182
+ l({ type: String })
183
+ ], e.prototype, "label", 2);
184
+ a([
185
+ l({ type: String, attribute: "label-position" })
186
+ ], e.prototype, "labelPosition", 2);
187
+ a([
131
188
  f("input")
132
- ], t.prototype, "inputEl", 2);
133
- t = i([
189
+ ], e.prototype, "inputEl", 2);
190
+ e = a([
134
191
  b("dui-input")
135
- ], t);
192
+ ], e);
136
193
  export {
137
- t as DuiInput
194
+ e as DuiInput
138
195
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@grayscale-dev/dragon",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Framework-agnostic Web Components built with Lit.",
5
5
  "type": "module",
6
6
  "files": [