@codesuma/baseline 1.0.8 → 1.0.10

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
@@ -1,4 +1,4 @@
1
- # Base
1
+ # Baseline
2
2
 
3
3
  A minimal, imperative UI framework for building fast web apps. No virtual DOM, no magic, no dependencies.
4
4
 
@@ -10,7 +10,7 @@ A minimal, imperative UI framework for building fast web apps. No virtual DOM, n
10
10
  npm install @codesuma/baseline
11
11
  ```
12
12
 
13
- ## Why Base?
13
+ ## Why Baseline?
14
14
 
15
15
  - **Predictable** - What you write is what happens. No hidden lifecycle, no reactivity magic.
16
16
  - **Fast** - Direct DOM manipulation. No diffing algorithms.
@@ -66,6 +66,16 @@ import { Div, Button, Input, Span, A, Img } from '@codesuma/baseline'
66
66
  const container = Div('Hello')
67
67
  const btn = Button('Click')
68
68
  const input = Input('Enter name...', 'text')
69
+
70
+ // Support for different input types (First arg is label for check/radio)
71
+ const checkbox = Input('Stay signed in', 'checkbox', { checked: true })
72
+ const number = Input('Age', 'number', { min: 0, max: 100 })
73
+ const date = Input('Date', 'date')
74
+
75
+ // Checkbox helpers
76
+ checkbox.isChecked() // true
77
+ checkbox.toggle()
78
+ checkbox.on('change', (checked) => console.log(checked))
69
79
  ```
70
80
 
71
81
  ### Events
@@ -8,7 +8,6 @@
8
8
  bottom: 0;
9
9
  width: 100%;
10
10
  height: 100%;
11
- padding-left: 60px;
12
11
  padding-top: env(safe-area-inset-top);
13
12
  padding-bottom: env(safe-area-inset-bottom);
14
13
  background-color: #fff;
@@ -1,29 +1,156 @@
1
- import { Base } from '../base'
2
-
3
- export const Input = (placeholder = '', type = 'text', options: { value?: string; accept?: string } = {}) => {
4
- const base = type === 'textarea' ? Base('textarea') : Base('input')
5
-
6
- base.el.setAttribute('type', type)
7
- base.el.setAttribute('placeholder', placeholder)
8
- if (options.accept) base.el.setAttribute('accept', options.accept)
9
- if (options.value) base.el.value = options.value
10
-
11
- // Events
12
- base.el.onblur = () => base.emit('blur')
13
- base.el.onfocus = () => base.emit('focus')
14
- base.el.oninput = () => base.emit('input', base.el.value)
15
- base.el.onkeydown = (e: KeyboardEvent) => {
16
- if (e.key === 'Enter') base.emit('enter', base.el.value)
1
+ import { Base, IBaseComponent } from '../base'
2
+
3
+ export type InputType = 'text' | 'password' | 'email' | 'number' | 'search' | 'tel' | 'url' | 'checkbox' | 'radio' | 'date' | 'time' | 'datetime-local' | 'month' | 'week' | 'color' | 'file' | 'range' | 'hidden' | 'textarea'
4
+
5
+ export interface InputOptions {
6
+ value?: string | number
7
+ accept?: string
8
+ checked?: boolean
9
+ name?: string
10
+ id?: string
11
+ min?: string | number
12
+ max?: string | number
13
+ step?: string | number
14
+ autocomplete?: string
15
+ autofocus?: boolean
16
+ disabled?: boolean
17
+ readonly?: boolean
18
+ required?: boolean
19
+ multiple?: boolean
20
+ pattern?: string
21
+ }
22
+
23
+ export interface IInputComponent extends IBaseComponent<'input'> {
24
+ value(): string
25
+ setValue(v: string): void
26
+ clear(): void
27
+ select(): void
28
+ focus(): void
29
+ blur(): void
30
+ }
31
+
32
+ export interface ITextAreaComponent extends IBaseComponent<'textarea'> {
33
+ value(): string
34
+ setValue(v: string): void
35
+ clear(): void
36
+ select(): void
37
+ focus(): void
38
+ blur(): void
39
+ }
40
+
41
+ export interface ICheckboxComponent extends IBaseComponent<any> {
42
+ isChecked(): boolean
43
+ setChecked(v: boolean): void
44
+ toggle(): void
45
+ focus(): void
46
+ blur(): void
47
+ }
48
+
49
+ export function Input(placeholder: string, type: 'checkbox' | 'radio', options?: InputOptions): ICheckboxComponent
50
+ export function Input(placeholder: string, type: 'textarea', options?: InputOptions): ITextAreaComponent
51
+ export function Input(placeholder?: string, type?: InputType, options?: InputOptions): IInputComponent
52
+ export function Input(placeholder = '', type: InputType = 'text', options: InputOptions = {}) {
53
+ const isCheckOrRadio = type === 'checkbox' || type === 'radio'
54
+ const hasLabel = isCheckOrRadio && placeholder.length > 0
55
+
56
+ // If it's a checkbox/radio with a label, the main component is a Label wrapper
57
+ // Otherwise, it's the Input element itself
58
+ const base = hasLabel
59
+ ? Base('label')
60
+ : (type === 'textarea' ? Base('textarea') : Base('input'))
61
+
62
+ // The actual input element to interact with
63
+ let inputEl: HTMLInputElement | HTMLTextAreaElement
64
+
65
+ if (hasLabel) {
66
+ // Create internal input for the wrapper
67
+ const inputComp = Base('input')
68
+ inputEl = inputComp.el as HTMLInputElement
69
+
70
+ // Setup wrapper styles
71
+ base.style({ display: 'inline-flex', alignItems: 'center', gap: '8px', cursor: 'pointer', userSelect: 'none' })
72
+
73
+ // Assemble: Input + Text
74
+ const text = Base('span')
75
+ text.el.textContent = placeholder
76
+ base.append(inputComp, text)
77
+ } else {
78
+ inputEl = base.el as HTMLInputElement | HTMLTextAreaElement
79
+ }
80
+
81
+ // --- Common Setup Logic (applied to inputEl) ---
82
+
83
+ if (type !== 'textarea') {
84
+ inputEl.setAttribute('type', type)
85
+ // Fix for global appearance: none which hides checkboxes
86
+ if (isCheckOrRadio) {
87
+ inputEl.style.setProperty('-webkit-appearance', 'auto')
88
+ inputEl.style.setProperty('appearance', 'auto')
89
+ }
90
+ }
91
+
92
+ // For non-checkboxes, placeholder goes on input. For checkboxes, it's already used as label text.
93
+ if (!isCheckOrRadio && placeholder) {
94
+ inputEl.setAttribute('placeholder', placeholder)
95
+ }
96
+
97
+ // Attributes
98
+ if (options.value !== undefined) inputEl.value = String(options.value)
99
+ if (options.accept) inputEl.setAttribute('accept', options.accept)
100
+ if (options.name) inputEl.setAttribute('name', options.name)
101
+ // ID goes on the input element normally, but if wrapped, user might expect ID on wrapper?
102
+ // Standard practice: ID on input, wrapper usually wraps it.
103
+ if (options.id) inputEl.setAttribute('id', options.id)
104
+
105
+ if (options.min !== undefined) inputEl.setAttribute('min', String(options.min))
106
+ if (options.max !== undefined) inputEl.setAttribute('max', String(options.max))
107
+ if (options.step !== undefined) inputEl.setAttribute('step', String(options.step))
108
+ if (options.autocomplete) inputEl.setAttribute('autocomplete', options.autocomplete)
109
+ if (options.pattern) inputEl.setAttribute('pattern', options.pattern)
110
+ if (options.multiple) inputEl.setAttribute('multiple', '')
111
+
112
+ // Boolean properties
113
+ if (options.checked !== undefined && 'checked' in inputEl) (inputEl as HTMLInputElement).checked = options.checked
114
+ if (options.disabled) inputEl.disabled = true
115
+ if (options.readonly) inputEl.readOnly = true
116
+ if (options.required) inputEl.required = true
117
+ if (options.autofocus) setTimeout(() => inputEl.focus(), 0)
118
+
119
+ // Events - Proxy from inputEl to base emitter
120
+ inputEl.onblur = () => base.emit('blur')
121
+ inputEl.onfocus = () => base.emit('focus')
122
+
123
+ const emitValue = () => {
124
+ const val = isCheckOrRadio && 'checked' in inputEl
125
+ ? (inputEl as HTMLInputElement).checked
126
+ : inputEl.value
127
+ return val
128
+ }
129
+
130
+ inputEl.oninput = () => base.emit('input', emitValue())
131
+ inputEl.onchange = () => base.emit('change', emitValue())
132
+
133
+ inputEl.onkeydown = (e: KeyboardEvent) => {
134
+ if (e.key === 'Enter') base.emit('enter', inputEl.value)
17
135
  if (e.key === 'Escape') base.emit('escape')
18
- base.emit('keydown', { key: e.key, value: base.el.value })
136
+ base.emit('keydown', { key: e.key, value: inputEl.value })
19
137
  }
20
138
 
21
139
  return Object.assign(base, {
22
- focus() { base.el.focus() },
23
- blur() { base.el.blur() },
24
- select() { base.el.select() },
25
- value() { return base.el.value },
26
- setValue(v: string) { base.el.value = v },
27
- clear() { base.el.value = '' }
28
- })
140
+ focus() { inputEl.focus() },
141
+ blur() { inputEl.blur() },
142
+ select() { inputEl.select() },
143
+ value() { return inputEl.value },
144
+ setValue(v: string) { inputEl.value = v },
145
+ clear() { inputEl.value = '' },
146
+ // Checkbox/Radio specific
147
+ isChecked() { return 'checked' in inputEl ? (inputEl as HTMLInputElement).checked : false },
148
+ setChecked(v: boolean) { if ('checked' in inputEl) (inputEl as HTMLInputElement).checked = v },
149
+ toggle() {
150
+ if ('checked' in inputEl) {
151
+ (inputEl as HTMLInputElement).checked = !(inputEl as HTMLInputElement).checked
152
+ base.emit('change', (inputEl as HTMLInputElement).checked)
153
+ }
154
+ }
155
+ }) as any
29
156
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codesuma/baseline",
3
- "version": "1.0.8",
3
+ "version": "1.0.10",
4
4
  "description": "A minimal, imperative UI framework for building fast web apps. No virtual DOM, no magic, no dependencies.",
5
5
  "main": "index.ts",
6
6
  "types": "index.ts",