@mhmo91/schmancy 0.7.1 → 0.7.2
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/mixins/baseElement.ts +73 -0
- package/mixins/constructor.ts +3 -0
- package/mixins/discovery.service.ts +60 -0
- package/mixins/formField.mixin.ts +223 -0
- package/mixins/index.ts +5 -0
- package/mixins/litElement.mixin.ts +15 -0
- package/mixins/scss.d.ts +21 -0
- package/mixins/tailwind.css +201 -0
- package/mixins/tailwind.mixin.ts +30 -0
- package/package.json +3 -2
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { Constructor } from './constructor'
|
|
2
|
+
import { LitElement } from 'lit'
|
|
3
|
+
import { Subject, fromEvent, Observable } from 'rxjs'
|
|
4
|
+
import { takeUntil } from 'rxjs/operators'
|
|
5
|
+
import { classMap } from 'lit/directives/class-map.js'
|
|
6
|
+
import { styleMap } from 'lit/directives/style-map.js'
|
|
7
|
+
import { discoverComponent } from './discovery.service'
|
|
8
|
+
|
|
9
|
+
export declare class IBaseMixin {
|
|
10
|
+
disconnecting: Subject<boolean>
|
|
11
|
+
classMap: typeof classMap
|
|
12
|
+
styleMap: typeof styleMap
|
|
13
|
+
discover<T extends HTMLElement>(tag: string): Observable<T | null>
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const BaseElement = <T extends Constructor<LitElement>>(superClass: T) => {
|
|
17
|
+
class BaseElement extends superClass {
|
|
18
|
+
disconnecting = new Subject<boolean>()
|
|
19
|
+
|
|
20
|
+
classMap(classes: Record<string, boolean>) {
|
|
21
|
+
const newClasses: Record<string, boolean> = {}
|
|
22
|
+
Object.keys(classes).forEach(key => {
|
|
23
|
+
key
|
|
24
|
+
.trim()
|
|
25
|
+
.split(' ')
|
|
26
|
+
.filter(Boolean)
|
|
27
|
+
.forEach(k => {
|
|
28
|
+
newClasses[k] = classes[key]
|
|
29
|
+
})
|
|
30
|
+
})
|
|
31
|
+
return classMap(newClasses)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
styleMap(styles: Record<string, string | number>) {
|
|
35
|
+
return styleMap(styles)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
connectedCallback() {
|
|
39
|
+
super.connectedCallback()
|
|
40
|
+
this.setupDiscoveryResponse()
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
private setupDiscoveryResponse() {
|
|
44
|
+
const tagName = this.tagName.toLowerCase()
|
|
45
|
+
const whereAreYouEvent = `${tagName}-where-are-you`
|
|
46
|
+
const hereIAmEvent = `${tagName}-here-i-am`
|
|
47
|
+
|
|
48
|
+
fromEvent(window, whereAreYouEvent)
|
|
49
|
+
.pipe(takeUntil(this.disconnecting))
|
|
50
|
+
.subscribe(() => {
|
|
51
|
+
window.dispatchEvent(
|
|
52
|
+
new CustomEvent(hereIAmEvent, {
|
|
53
|
+
detail: { component: this },
|
|
54
|
+
bubbles: true,
|
|
55
|
+
composed: true,
|
|
56
|
+
}),
|
|
57
|
+
)
|
|
58
|
+
})
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Make discover public to match the interface
|
|
62
|
+
discover<T extends HTMLElement>(tag: string): Observable<T | null> {
|
|
63
|
+
return discoverComponent<T>(tag)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
disconnectedCallback() {
|
|
67
|
+
this.disconnecting.next(true)
|
|
68
|
+
this.disconnecting.complete()
|
|
69
|
+
super.disconnectedCallback()
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return BaseElement as Constructor<IBaseMixin> & T
|
|
73
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { fromEvent, timer, race, Observable } from 'rxjs'
|
|
2
|
+
import { takeUntil, map, defaultIfEmpty } from 'rxjs/operators'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Discover a component in the DOM using the WhereAreYou/HereIAm pattern.
|
|
6
|
+
*
|
|
7
|
+
* @param componentTag - The tag name of the component to discover (e.g., 'schmancy-navigation-rail')
|
|
8
|
+
* @param timeout - How long to wait for a response in milliseconds (default: 100)
|
|
9
|
+
* @returns Observable that emits the discovered component or null if not found
|
|
10
|
+
*/
|
|
11
|
+
export function discoverComponent<T extends HTMLElement>(
|
|
12
|
+
componentTag: string,
|
|
13
|
+
timeout = 100,
|
|
14
|
+
): Observable<T | null> {
|
|
15
|
+
const whereAreYouEvent = `${componentTag}-where-are-you`
|
|
16
|
+
const hereIAmEvent = `${componentTag}-here-i-am`
|
|
17
|
+
|
|
18
|
+
return new Observable(subscriber => {
|
|
19
|
+
// Listen for response first (you were right!)
|
|
20
|
+
const subscription = fromEvent<CustomEvent>(window, hereIAmEvent)
|
|
21
|
+
.pipe(
|
|
22
|
+
takeUntil(timer(timeout)),
|
|
23
|
+
map(e => e.detail.component as T),
|
|
24
|
+
defaultIfEmpty(null),
|
|
25
|
+
)
|
|
26
|
+
.subscribe(component => {
|
|
27
|
+
subscriber.next(component)
|
|
28
|
+
subscriber.complete()
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
// Then dispatch discovery request
|
|
32
|
+
window.dispatchEvent(
|
|
33
|
+
new CustomEvent(whereAreYouEvent, {
|
|
34
|
+
bubbles: true,
|
|
35
|
+
composed: true,
|
|
36
|
+
}),
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
// Return cleanup function
|
|
40
|
+
return () => subscription.unsubscribe()
|
|
41
|
+
})
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Discover any of multiple components using race.
|
|
46
|
+
* Returns the first component that responds.
|
|
47
|
+
*
|
|
48
|
+
* @param componentTags - Array of component tag names to discover
|
|
49
|
+
* @returns Observable that emits the first discovered component or null if none found
|
|
50
|
+
*/
|
|
51
|
+
export function discoverAnyComponent<T extends HTMLElement>(...componentTags: string[]): Observable<T | null> {
|
|
52
|
+
if (componentTags.length === 0) {
|
|
53
|
+
return new Observable(subscriber => {
|
|
54
|
+
subscriber.next(null)
|
|
55
|
+
subscriber.complete()
|
|
56
|
+
})
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return race(...componentTags.map(tag => discoverComponent<T>(tag)))
|
|
60
|
+
}
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { CSSResult, LitElement, PropertyValueMap } from 'lit'
|
|
2
|
+
import { property } from 'lit/decorators.js'
|
|
3
|
+
import { IBaseMixin } from './baseElement'
|
|
4
|
+
import { Constructor } from './constructor'
|
|
5
|
+
import { ITailwindElementMixin, TailwindElement } from './tailwind.mixin'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Interface defining the properties and methods that the FormFieldMixin adds.
|
|
9
|
+
*/
|
|
10
|
+
export interface IFormFieldMixin extends Element {
|
|
11
|
+
// Properties
|
|
12
|
+
name: string
|
|
13
|
+
value: string | string[] | boolean | number | undefined
|
|
14
|
+
label: string
|
|
15
|
+
required: boolean
|
|
16
|
+
disabled: boolean
|
|
17
|
+
readonly: boolean
|
|
18
|
+
error: boolean
|
|
19
|
+
validationMessage: string
|
|
20
|
+
hint?: string
|
|
21
|
+
id: string
|
|
22
|
+
|
|
23
|
+
// Form association
|
|
24
|
+
form: HTMLFormElement | null
|
|
25
|
+
|
|
26
|
+
// Methods
|
|
27
|
+
checkValidity(): boolean
|
|
28
|
+
reportValidity(): boolean
|
|
29
|
+
setCustomValidity(message: string): void
|
|
30
|
+
|
|
31
|
+
// Event emitter helper
|
|
32
|
+
emitChange(detail: any): void
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* A mixin that adds form field capabilities to a LitElement class.
|
|
37
|
+
* This provides common form field properties, validation, and form association.
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```ts
|
|
41
|
+
* class MyInput extends FormFieldMixin(TailwindElement(css`...`)) {
|
|
42
|
+
* // Your component code here
|
|
43
|
+
* }
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
export function FormFieldMixin<T extends Constructor<LitElement>>(superClass: T) {
|
|
47
|
+
class FormFieldMixinClass extends superClass {
|
|
48
|
+
static formAssociated = true
|
|
49
|
+
|
|
50
|
+
// Element internals for form association
|
|
51
|
+
private internals: ElementInternals | undefined
|
|
52
|
+
|
|
53
|
+
// Properties common to form fields
|
|
54
|
+
/**
|
|
55
|
+
* The name of the form field (used for form submission)
|
|
56
|
+
*/
|
|
57
|
+
@property({ type: String })
|
|
58
|
+
name: string = ''
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* The current value of the form field
|
|
62
|
+
*/
|
|
63
|
+
@property({ reflect: true })
|
|
64
|
+
value: string | string[] | boolean | number | undefined = ''
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Label text for the form field
|
|
68
|
+
*/
|
|
69
|
+
@property({ type: String })
|
|
70
|
+
label: string = ''
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Whether the field is required
|
|
74
|
+
*/
|
|
75
|
+
@property({ type: Boolean, reflect: true })
|
|
76
|
+
required: boolean = false
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Whether the field is disabled
|
|
80
|
+
*/
|
|
81
|
+
@property({ type: Boolean, reflect: true })
|
|
82
|
+
disabled: boolean = false
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Whether the field is read-only
|
|
86
|
+
*/
|
|
87
|
+
@property({ type: Boolean, reflect: true })
|
|
88
|
+
readonly: boolean = false
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Whether the field is in an error state
|
|
92
|
+
*/
|
|
93
|
+
@property({ type: Boolean, reflect: true })
|
|
94
|
+
error: boolean = false
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* The validation message to display
|
|
98
|
+
*/
|
|
99
|
+
@property({ type: String })
|
|
100
|
+
validationMessage: string = ''
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Optional hint text to display below the field
|
|
104
|
+
*/
|
|
105
|
+
@property({ type: String })
|
|
106
|
+
hint?: string
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Unique identifier for the field
|
|
110
|
+
*/
|
|
111
|
+
@property({ reflect: true })
|
|
112
|
+
override id: string = `schmancy-field-${Date.now()}-${Math.floor(Math.random() * 1000)}`
|
|
113
|
+
|
|
114
|
+
constructor(...args: any[]) {
|
|
115
|
+
super(...args)
|
|
116
|
+
try {
|
|
117
|
+
this.internals = this.attachInternals()
|
|
118
|
+
} catch {
|
|
119
|
+
this.internals = undefined
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Gets the form this element is associated with
|
|
125
|
+
*/
|
|
126
|
+
get form(): HTMLFormElement | null {
|
|
127
|
+
return this.internals?.form ?? null
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Lifecycle method called when properties change
|
|
132
|
+
*/
|
|
133
|
+
protected willUpdate(changedProps: PropertyValueMap<any>): void {
|
|
134
|
+
super.willUpdate(changedProps)
|
|
135
|
+
|
|
136
|
+
// Update form value when value changes
|
|
137
|
+
if (changedProps.has('value')) {
|
|
138
|
+
this.internals?.setFormValue(this.value as string | File | FormData | null)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Update validation state when error or validation message changes
|
|
142
|
+
if (changedProps.has('error') || changedProps.has('validationMessage')) {
|
|
143
|
+
if (this.error && this.validationMessage) {
|
|
144
|
+
this.internals?.setValidity({ customError: true }, this.validationMessage)
|
|
145
|
+
} else {
|
|
146
|
+
this.internals?.setValidity({})
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Checks if the field is valid without showing validation UI
|
|
153
|
+
*/
|
|
154
|
+
checkValidity(): boolean {
|
|
155
|
+
if (this.disabled) return true
|
|
156
|
+
|
|
157
|
+
if (this.required && (this.value === '' || this.value === undefined || this.value === null)) {
|
|
158
|
+
this.error = true
|
|
159
|
+
this.validationMessage = 'This field is required'
|
|
160
|
+
return false
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return true
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Reports validity and shows validation UI if invalid
|
|
168
|
+
*/
|
|
169
|
+
reportValidity(): boolean {
|
|
170
|
+
const isValid = this.checkValidity()
|
|
171
|
+
if (!isValid) {
|
|
172
|
+
this.internals?.reportValidity()
|
|
173
|
+
}
|
|
174
|
+
return isValid
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Sets a custom validation message
|
|
179
|
+
*/
|
|
180
|
+
setCustomValidity(message: string): void {
|
|
181
|
+
this.validationMessage = message
|
|
182
|
+
this.error = message !== ''
|
|
183
|
+
if (message) {
|
|
184
|
+
this.internals?.setValidity({ customError: true }, message)
|
|
185
|
+
} else {
|
|
186
|
+
this.internals?.setValidity({})
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Helper method to emit change events
|
|
192
|
+
*/
|
|
193
|
+
emitChange(detail: any): void {
|
|
194
|
+
this.dispatchEvent(
|
|
195
|
+
new CustomEvent('change', {
|
|
196
|
+
detail,
|
|
197
|
+
bubbles: true,
|
|
198
|
+
composed: true,
|
|
199
|
+
}),
|
|
200
|
+
)
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return FormFieldMixinClass as Constructor<IFormFieldMixin> & T
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* A convenience function that composes FormFieldMixin with TailwindElement
|
|
209
|
+
* to create a base class for Schmancy form components.
|
|
210
|
+
*
|
|
211
|
+
* @example
|
|
212
|
+
* ```ts
|
|
213
|
+
* class MyInput extends SchmancyFormField(css`...`) {
|
|
214
|
+
* // Your component code here
|
|
215
|
+
* }
|
|
216
|
+
* ```
|
|
217
|
+
*/
|
|
218
|
+
export function SchmancyFormField<T extends CSSResult>(componentStyle?: T) {
|
|
219
|
+
return FormFieldMixin(TailwindElement(componentStyle)) as Constructor<IFormFieldMixin> &
|
|
220
|
+
Constructor<ITailwindElementMixin> &
|
|
221
|
+
Constructor<LitElement> &
|
|
222
|
+
Constructor<IBaseMixin>
|
|
223
|
+
}
|
package/mixins/index.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { CSSResult, LitElement } from 'lit'
|
|
2
|
+
import { TailwindElement } from './tailwind.mixin'
|
|
3
|
+
import { BaseElement, IBaseMixin } from './baseElement'
|
|
4
|
+
import { Constructor } from './constructor'
|
|
5
|
+
|
|
6
|
+
export const $LitElement = <T extends CSSResult>(componentStyle?: T) => {
|
|
7
|
+
class TailwindMixinClass extends BaseElement(TailwindElement(componentStyle)) {
|
|
8
|
+
disconnectedCallback = () => {
|
|
9
|
+
super.disconnectedCallback()
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
return TailwindMixinClass as CustomElementConstructor &
|
|
13
|
+
Constructor<LitElement> &
|
|
14
|
+
Constructor<IBaseMixin> /* see "typing the subclass" below */
|
|
15
|
+
}
|
package/mixins/scss.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
declare module '*.scss?inline' {
|
|
2
|
+
import { type CSSResult } from 'lit'
|
|
3
|
+
const styles: CSSResult
|
|
4
|
+
export default styles
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
declare module '*.module.scss' {
|
|
8
|
+
const classes: Record<string, string>
|
|
9
|
+
export default classes
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
declare module '*.css?inline' {
|
|
13
|
+
import { type CSSResult } from 'lit'
|
|
14
|
+
const styles: CSSResult
|
|
15
|
+
export default styles
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
declare module '*.module.css' {
|
|
19
|
+
const classes: Record<string, string>
|
|
20
|
+
export default classes
|
|
21
|
+
}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
@import 'tailwindcss';
|
|
2
|
+
:host,
|
|
3
|
+
:root {
|
|
4
|
+
font-family: var(--schmancy-font-family);
|
|
5
|
+
}
|
|
6
|
+
@theme inline {
|
|
7
|
+
--md-ref-typeface-brand: var(--schmancy-font-family);
|
|
8
|
+
--md-ref-typeface-plain: var(--schmancy-font-family);
|
|
9
|
+
--md-sys-color-primary: var(--schmancy-sys-color-primary-default);
|
|
10
|
+
--md-sys-color-secondary: var(--schmancy-sys-color-secondary-default);
|
|
11
|
+
|
|
12
|
+
--*: initial;
|
|
13
|
+
--border-style: solid;
|
|
14
|
+
--spacing: 4px;
|
|
15
|
+
--color-scrim: var(--schmancy-sys-color-scrim);
|
|
16
|
+
--color-outline: var(--schmancy-sys-color-outline);
|
|
17
|
+
--color-outlineVariant: var(--schmancy-sys-color-outlineVariant);
|
|
18
|
+
--color-surface-default: var(--schmancy-sys-color-surface-default);
|
|
19
|
+
--color-surface-dim: var(--schmancy-sys-color-surface-dim);
|
|
20
|
+
--color-surface-bright: var(--schmancy-sys-color-surface-bright);
|
|
21
|
+
--color-surface-container: var(--schmancy-sys-color-surface-container);
|
|
22
|
+
--color-surface-low: var(--schmancy-sys-color-surface-low);
|
|
23
|
+
--color-surface-high: var(--schmancy-sys-color-surface-high);
|
|
24
|
+
--color-surface-highest: var(--schmancy-sys-color-surface-highest);
|
|
25
|
+
--color-surface-lowest: var(--schmancy-sys-color-surface-lowest);
|
|
26
|
+
--color-surface-on: var(--schmancy-sys-color-surface-on);
|
|
27
|
+
--color-surface-onVariant: var(--schmancy-sys-color-surface-onVariant);
|
|
28
|
+
--color-primary-default: var(--schmancy-sys-color-primary-default);
|
|
29
|
+
--color-primary-on: var(--schmancy-sys-color-primary-on);
|
|
30
|
+
--color-primary-container: var(--schmancy-sys-color-primary-container);
|
|
31
|
+
--color-primary-onContainer: var(--schmancy-sys-color-primary-onContainer);
|
|
32
|
+
--color-secondary-default: var(--schmancy-sys-color-secondary-default);
|
|
33
|
+
--color-secondary-on: var(--schmancy-sys-color-secondary-on);
|
|
34
|
+
--color-secondary-container: var(--schmancy-sys-color-secondary-container);
|
|
35
|
+
--color-secondary-onContainer: var(--schmancy-sys-color-secondary-onContainer);
|
|
36
|
+
--color-tertiary-default: var(--schmancy-sys-color-tertiary-default);
|
|
37
|
+
--color-tertiary-on: var(--schmancy-sys-color-tertiary-on);
|
|
38
|
+
--color-tertiary-container: var(--schmancy-sys-color-tertiary-container);
|
|
39
|
+
--color-tertiary-onContainer: var(--schmancy-sys-color-tertiary-onContainer);
|
|
40
|
+
--color-error-default: var(--schmancy-sys-color-error-default);
|
|
41
|
+
--color-error-on: var(--schmancy-sys-color-error-on);
|
|
42
|
+
--color-error-container: var(--schmancy-sys-color-error-container);
|
|
43
|
+
--color-error-onContainer: var(--schmancy-sys-color-error-onContainer);
|
|
44
|
+
--color-success-default: var(--schmancy-sys-color-success-default);
|
|
45
|
+
--color-success-on: var(--schmancy-sys-color-success-on);
|
|
46
|
+
--color-success-container: var(--schmancy-sys-color-success-container);
|
|
47
|
+
--color-success-onContainer: var(--schmancy-sys-color-success-onContainer);
|
|
48
|
+
--shadow-0: var(--schmancy-sys-elevation-0);
|
|
49
|
+
--shadow-1: var(--schmancy-sys-elevation-1);
|
|
50
|
+
--shadow-2: var(--schmancy-sys-elevation-2);
|
|
51
|
+
--shadow-3: var(--schmancy-sys-elevation-3);
|
|
52
|
+
--shadow-4: var(--schmancy-sys-elevation-4);
|
|
53
|
+
--shadow-5: var(--schmancy-sys-elevation-5);
|
|
54
|
+
--outline-1: var(--schmancy-sys-outline-1);
|
|
55
|
+
--font-sans:
|
|
56
|
+
var(--schmancy-font-family), ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji',
|
|
57
|
+
'Segoe UI Symbol', 'Noto Color Emoji';
|
|
58
|
+
--font-serif: var(--schmancy-font-family), ui-serif, Georgia, Cambria, 'Times New Roman', Times, serif;
|
|
59
|
+
--font-mono:
|
|
60
|
+
var(--schmancy-font-family), ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono',
|
|
61
|
+
'Courier New', monospace;
|
|
62
|
+
--color-black: #000;
|
|
63
|
+
--color-white: #fff;
|
|
64
|
+
--spacing: 0.25rem;
|
|
65
|
+
--breakpoint-sm: 40rem;
|
|
66
|
+
--breakpoint-md: 48rem;
|
|
67
|
+
--breakpoint-lg: 64rem;
|
|
68
|
+
--breakpoint-xl: 80rem;
|
|
69
|
+
--breakpoint-2xl: 96rem;
|
|
70
|
+
--container-3xs: 16rem;
|
|
71
|
+
--container-2xs: 18rem;
|
|
72
|
+
--container-xs: 20rem;
|
|
73
|
+
--container-sm: 24rem;
|
|
74
|
+
--container-md: 28rem;
|
|
75
|
+
--container-lg: 32rem;
|
|
76
|
+
--container-xl: 36rem;
|
|
77
|
+
--container-2xl: 42rem;
|
|
78
|
+
--container-3xl: 48rem;
|
|
79
|
+
--container-4xl: 56rem;
|
|
80
|
+
--container-5xl: 64rem;
|
|
81
|
+
--container-6xl: 72rem;
|
|
82
|
+
--container-7xl: 80rem;
|
|
83
|
+
--text-xs: 0.75rem;
|
|
84
|
+
--text-xs--line-height: calc(1 / 0.75);
|
|
85
|
+
--text-sm: 0.875rem;
|
|
86
|
+
--text-sm--line-height: calc(1.25 / 0.875);
|
|
87
|
+
--text-base: 1rem;
|
|
88
|
+
--text-base--line-height: calc(1.5 / 1);
|
|
89
|
+
--text-lg: 1.125rem;
|
|
90
|
+
--text-lg--line-height: calc(1.75 / 1.125);
|
|
91
|
+
--text-xl: 1.25rem;
|
|
92
|
+
--text-xl--line-height: calc(1.75 / 1.25);
|
|
93
|
+
--text-2xl: 1.5rem;
|
|
94
|
+
--text-2xl--line-height: calc(2 / 1.5);
|
|
95
|
+
--text-3xl: 1.875rem;
|
|
96
|
+
--text-3xl--line-height: calc(2.25 / 1.875);
|
|
97
|
+
--text-4xl: 2.25rem;
|
|
98
|
+
--text-4xl--line-height: calc(2.5 / 2.25);
|
|
99
|
+
--text-5xl: 3rem;
|
|
100
|
+
--text-5xl--line-height: 1;
|
|
101
|
+
--text-6xl: 3.75rem;
|
|
102
|
+
--text-6xl--line-height: 1;
|
|
103
|
+
--text-7xl: 4.5rem;
|
|
104
|
+
--text-7xl--line-height: 1;
|
|
105
|
+
--text-8xl: 6rem;
|
|
106
|
+
--text-8xl--line-height: 1;
|
|
107
|
+
--text-9xl: 8rem;
|
|
108
|
+
--text-9xl--line-height: 1;
|
|
109
|
+
--font-weight-thin: 100;
|
|
110
|
+
--font-weight-extralight: 200;
|
|
111
|
+
--font-weight-light: 300;
|
|
112
|
+
--font-weight-normal: 400;
|
|
113
|
+
--font-weight-medium: 500;
|
|
114
|
+
--font-weight-semibold: 600;
|
|
115
|
+
--font-weight-bold: 700;
|
|
116
|
+
--font-weight-extrabold: 800;
|
|
117
|
+
--font-weight-black: 900;
|
|
118
|
+
--tracking-tighter: -0.05em;
|
|
119
|
+
--tracking-tight: -0.025em;
|
|
120
|
+
--tracking-normal: 0em;
|
|
121
|
+
--tracking-wide: 0.025em;
|
|
122
|
+
--tracking-wider: 0.05em;
|
|
123
|
+
--tracking-widest: 0.1em;
|
|
124
|
+
--leading-tight: 1.25;
|
|
125
|
+
--leading-snug: 1.375;
|
|
126
|
+
--leading-normal: 1.5;
|
|
127
|
+
--leading-relaxed: 1.625;
|
|
128
|
+
--leading-loose: 2;
|
|
129
|
+
--radius-xs: 0.125rem;
|
|
130
|
+
--radius-sm: 0.25rem;
|
|
131
|
+
--radius-md: 0.375rem;
|
|
132
|
+
--radius-lg: 0.5rem;
|
|
133
|
+
--radius-xl: 0.75rem;
|
|
134
|
+
--radius-2xl: 1rem;
|
|
135
|
+
--radius-3xl: 1.5rem;
|
|
136
|
+
--radius-4xl: 2rem;
|
|
137
|
+
--shadow-2xs: 0 1px rgb(0 0 0 / 0.05);
|
|
138
|
+
--shadow-xs: 0 1px 2px 0 rgb(0 0 0 / 0.05);
|
|
139
|
+
--shadow-sm: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
|
|
140
|
+
--shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
|
|
141
|
+
--shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
|
|
142
|
+
--shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
|
|
143
|
+
--shadow-2xl: 0 25px 50px -12px rgb(0 0 0 / 0.25);
|
|
144
|
+
--inset-shadow-2xs: inset 0 1px rgb(0 0 0 / 0.05);
|
|
145
|
+
--inset-shadow-xs: inset 0 1px 1px rgb(0 0 0 / 0.05);
|
|
146
|
+
--inset-shadow-sm: inset 0 2px 4px rgb(0 0 0 / 0.05);
|
|
147
|
+
--drop-shadow-xs: 0 1px 1px rgb(0 0 0 / 0.05);
|
|
148
|
+
--drop-shadow-sm: 0 1px 2px rgb(0 0 0 / 0.15);
|
|
149
|
+
--drop-shadow-md: 0 3px 3px rgb(0 0 0 / 0.12);
|
|
150
|
+
--drop-shadow-lg: 0 4px 4px rgb(0 0 0 / 0.15);
|
|
151
|
+
--drop-shadow-xl: 0 9px 7px rgb(0 0 0 / 0.1);
|
|
152
|
+
--drop-shadow-2xl: 0 25px 25px rgb(0 0 0 / 0.15);
|
|
153
|
+
--blur-xs: 4px;
|
|
154
|
+
--blur-sm: 8px;
|
|
155
|
+
--blur-md: 12px;
|
|
156
|
+
--blur-lg: 16px;
|
|
157
|
+
--blur-xl: 24px;
|
|
158
|
+
--blur-2xl: 40px;
|
|
159
|
+
--blur-3xl: 64px;
|
|
160
|
+
--perspective-dramatic: 100px;
|
|
161
|
+
--perspective-near: 300px;
|
|
162
|
+
--perspective-normal: 500px;
|
|
163
|
+
--perspective-midrange: 800px;
|
|
164
|
+
--perspective-distant: 1200px;
|
|
165
|
+
--aspect-video: 16 / 9;
|
|
166
|
+
--ease-in: cubic-bezier(0.4, 0, 1, 1);
|
|
167
|
+
--ease-out: cubic-bezier(0, 0, 0.2, 1);
|
|
168
|
+
--ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
|
|
169
|
+
--animate-spin: spin 1s linear infinite;
|
|
170
|
+
--animate-ping: ping 1s cubic-bezier(0, 0, 0.2, 1) infinite;
|
|
171
|
+
--animate-pulse: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
|
172
|
+
--animate-bounce: bounce 1s infinite;
|
|
173
|
+
@keyframes spin {
|
|
174
|
+
to {
|
|
175
|
+
transform: rotate(360deg);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
@keyframes ping {
|
|
179
|
+
75%,
|
|
180
|
+
100% {
|
|
181
|
+
transform: scale(2);
|
|
182
|
+
opacity: 0;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
@keyframes pulse {
|
|
186
|
+
50% {
|
|
187
|
+
opacity: 0.5;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
@keyframes bounce {
|
|
191
|
+
0%,
|
|
192
|
+
100% {
|
|
193
|
+
transform: translateY(-25%);
|
|
194
|
+
animation-timing-function: cubic-bezier(0.8, 0, 1, 1);
|
|
195
|
+
}
|
|
196
|
+
50% {
|
|
197
|
+
transform: none;
|
|
198
|
+
animation-timing-function: cubic-bezier(0, 0, 0.2, 1);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { CSSResult, LitElement, unsafeCSS } from 'lit'
|
|
2
|
+
|
|
3
|
+
import style from './tailwind.css?inline'
|
|
4
|
+
import { BaseElement, IBaseMixin } from './baseElement'
|
|
5
|
+
import { Constructor } from './constructor'
|
|
6
|
+
|
|
7
|
+
export declare class ITailwindElementMixin {
|
|
8
|
+
styles: (typeof CSSResult)[]
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const tailwindStyles = unsafeCSS(style)
|
|
12
|
+
|
|
13
|
+
const TailwindElementBase = <T extends CSSResult>(componentStyle?: T) => {
|
|
14
|
+
class TailwindMixinClass extends LitElement {
|
|
15
|
+
static styles = [unsafeCSS(componentStyle), tailwindStyles]
|
|
16
|
+
}
|
|
17
|
+
return TailwindMixinClass as Constructor<LitElement> /* see "typing the subclass" below */
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const TailwindElement = <T extends CSSResult>(componentStyle?: T) => {
|
|
21
|
+
class TailwindMixinClass extends BaseElement(TailwindElementBase(componentStyle)) {
|
|
22
|
+
disconnectedCallback = () => {
|
|
23
|
+
super.disconnectedCallback()
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return TailwindMixinClass as Constructor<CustomElementConstructor> &
|
|
27
|
+
Constructor<ITailwindElementMixin> &
|
|
28
|
+
Constructor<LitElement> &
|
|
29
|
+
Constructor<IBaseMixin>
|
|
30
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mhmo91/schmancy",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.2",
|
|
4
4
|
"description": "UI library build with web components",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"exports": {
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
},
|
|
11
11
|
"./mixins": {
|
|
12
12
|
"types": "./types/mixins/index.d.ts",
|
|
13
|
-
"default": "./
|
|
13
|
+
"default": "./mixins/index.ts"
|
|
14
14
|
},
|
|
15
15
|
"./ai/*": "./ai/*",
|
|
16
16
|
"./*": {
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
"files": [
|
|
35
35
|
"dist",
|
|
36
36
|
"types",
|
|
37
|
+
"mixins",
|
|
37
38
|
"ai",
|
|
38
39
|
"README.md"
|
|
39
40
|
],
|