Skip to content

Commit

Permalink
feat: add basic form elements to forms pkg
Browse files Browse the repository at this point in the history
  • Loading branch information
josemarluedke committed Feb 24, 2024
1 parent 296aead commit 9986d87
Show file tree
Hide file tree
Showing 14 changed files with 503 additions and 22 deletions.
6 changes: 3 additions & 3 deletions packages/forms-legacy/src/components/form-field/feedback.gts
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@ export default class FormFieldFeedback extends Component<FormFieldFeedbackSignat
}

get classes() {
const { feedback } = useStyles();
const { formFeedback } = useStyles();

return feedback({
return formFeedback({
size: this.args.size,
isError: this.isError,
intent: this.isError ? 'danger' : 'primary',
class: this.args.class
});
}
Expand Down
4 changes: 2 additions & 2 deletions packages/forms-legacy/src/components/form-field/hint.gts
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ export interface FormFieldHintSignature {

export default class FormFieldHint extends Component<FormFieldHintSignature> {
get classes() {
const { hint } = useStyles();
const { formDescription } = useStyles();

return hint({
return formDescription({
size: this.args.size,
class: this.args.class
});
Expand Down
5 changes: 4 additions & 1 deletion packages/forms-legacy/src/components/form-field/label.gts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@ export default class FormFieldLabel extends Component<FormFieldLabelSignature> {
get classes() {
const { label } = useStyles();

return label({ size: this.args.size, class: this.args.class });
const { base } = label({
size: this.args.size || 'md'
});
return base({ class: this.args.class });
}

<template>
Expand Down
9 changes: 8 additions & 1 deletion packages/forms/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,15 @@
"type": "addon",
"main": "addon-main.js",
"app-js": {
"./components/checkbox.js": "./dist/_app_/components/checkbox.js",
"./components/form-description.js": "./dist/_app_/components/form-description.js",
"./components/form-feedback.js": "./dist/_app_/components/form-feedback.js",
"./components/input.js": "./dist/_app_/components/input.js",
"./components/label.js": "./dist/_app_/components/label.js",
"./components/native-select.js": "./dist/_app_/components/native-select.js",
"./components/select.js": "./dist/_app_/components/select.js"
"./components/radio.js": "./dist/_app_/components/radio.js",
"./components/select.js": "./dist/_app_/components/select.js",
"./components/textarea.js": "./dist/_app_/components/textarea.js"
}
},
"exports": {
Expand Down
61 changes: 61 additions & 0 deletions packages/forms/src/components/checkbox.gts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { on } from '@ember/modifier';
import { useStyles } from '@frontile/theme';

interface CheckboxSignature {
Args: {
id?: string;
checked?: boolean;
name?: string;
size?: 'sm' | 'md' | 'lg';
class?: string;

/*
* Callback when onchange is triggered
*/
onChange?: (value: boolean, event: Event) => void;
};
Element: HTMLInputElement;
}

class Checkbox extends Component<CheckboxSignature> {
get isChecked(): boolean {
return !!this.args.checked;
}

@action handleChange(event: Event): void {
event.preventDefault();

const value = !this.args.checked;

if (typeof this.args.onChange === 'function') {
this.args.onChange(value, event);
}
}

get classes() {
const { checkbox } = useStyles();

return checkbox({
size: this.args.size,
class: this.args.class
});
}

<template>
<input
{{on "change" this.handleChange}}
id={{@id}}
name={{@name}}
checked={{this.isChecked}}
type="checkbox"
class={{this.classes}}
data-component="checkbox"
...attributes
/>
</template>
}

export { Checkbox, type CheckboxSignature };
export default Checkbox;
43 changes: 43 additions & 0 deletions packages/forms/src/components/form-description.gts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import Component from '@glimmer/component';
import { useStyles } from '@frontile/theme';

interface FormDescriptionSignature {
Args: {
id?: string;
/*
* @defaultValue 'md'
*/
size?: 'sm' | 'md' | 'lg';

class?: string;
};
Element: HTMLDivElement;
Blocks: {
default: [];
};
}

class FormDescription extends Component<FormDescriptionSignature> {
get classes() {
const { formDescription } = useStyles();

return formDescription({
size: this.args.size || 'md',
class: this.args.class
});
}

<template>
<div
id={{@id}}
class={{this.classes}}
data-component="form-description"
...attributes
>
{{yield}}
</div>
</template>
}

export { FormDescription, type FormDescriptionSignature };
export default FormDescription;
73 changes: 73 additions & 0 deletions packages/forms/src/components/form-feedback.gts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import Component from '@glimmer/component';
import { useStyles } from '@frontile/theme';

interface FormFeedbackSignature {
Args: {
id?: string;
/*
* The intent of the feedback
* @defaultValue 'danger'
*/
intent?: 'primary' | 'success' | 'danger' | 'warning';

/*
* A list of messages or a single message string
*/
messages?: string[] | string;
/*
* @defaultValue 'md'
*/
size?: 'sm' | 'md' | 'lg';
class?: string;
};
Element: HTMLDivElement;
Blocks: {
default: [];
};
}

class FormFeedback extends Component<FormFeedbackSignature> {
get isError(): boolean {
return (
typeof this.args.messages !== 'undefined' ||
this.args.intent === 'danger' ||
typeof this.args.intent === 'undefined'
);
}

get classes() {
const { formFeedback } = useStyles();

return formFeedback({
size: this.args.size || 'md',
intent: this.args.intent || 'danger',
class: this.args.class
});
}

get messageText(): string {
if (!this.args.messages) return '';

if (typeof this.args.messages === 'string') {
return this.args.messages;
} else {
return this.args.messages.join('; ');
}
}

<template>
<div
id={{@id}}
class={{this.classes}}
data-component="form-feedback"
aria-live={{if this.isError "assertive" "polite"}}
...attributes
>
{{this.messageText}}
{{yield}}
</div>
</template>
}

export { FormFeedback, type FormFeedbackSignature };
export default FormFeedback;
73 changes: 73 additions & 0 deletions packages/forms/src/components/input.gts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { on } from '@ember/modifier';
import { useStyles } from '@frontile/theme';

interface InputSignature {
Args: {
id?: string;
type?: string;
size?: 'sm' | 'md' | 'lg';
class?: string;
value?: string;

// Callback when oninput is triggered
onInput?: (value: string, event: InputEvent) => void;

// Callback when onchange is triggered
onChange?: (value: string, event: InputEvent) => void;
};
Element: HTMLInputElement;
}

class Input extends Component<InputSignature> {
get type(): string {
if (typeof this.args.type === 'string') {
return this.args.type;
}
return 'text';
}

@action handleOnInput(event: Event): void {
if (typeof this.args.onInput === 'function') {
this.args.onInput(
(event.target as HTMLInputElement).value,
event as InputEvent
);
}
}

@action handleOnChange(event: Event): void {
if (typeof this.args.onChange === 'function') {
this.args.onChange(
(event.target as HTMLInputElement).value,
event as InputEvent
);
}
}

get classes() {
const { input } = useStyles();

return input({
size: this.args.size,
class: this.args.class
});
}

<template>
<input
{{on "input" this.handleOnInput}}
{{on "change" this.handleOnChange}}
id={{@id}}
value={{@value}}
type={{this.type}}
class={{this.classes}}
data-component="input"
...attributes
/>
</template>
}

export { Input, type InputSignature };
export default Input;
58 changes: 58 additions & 0 deletions packages/forms/src/components/label.gts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import Component from '@glimmer/component';
import { useStyles } from '@frontile/theme';

interface LabelSignature {
Args: {
/**
* The 'for' attribute of a <label>.
*/
for?: string;

/*
* @defaultValue 'md'
*/
size?: 'sm' | 'md' | 'lg';

/*
* Whether the field is required or not, if true, an asterisk will be added to the label.
* @defaultValue false
*/
isRequired?: boolean;
class?: string;
};
Element: HTMLLabelElement;
Blocks: {
default: [];
};
}

class Label extends Component<LabelSignature> {
get classes() {
const { label } = useStyles();
const { base, asterisk } = label({
size: this.args.size || 'md'
});

return {
base: base({ class: this.args.class }),
asterisk: asterisk()
};
}

<template>
<label
for={{@for}}
class={{this.classes.base}}
data-component="label"
...attributes
>
{{yield}}
{{#if @isRequired}}
<span class={{this.classes.asterisk}}>*</span>
{{/if}}
</label>
</template>
}

export { Label, type LabelSignature };
export default Label;
Loading

0 comments on commit 9986d87

Please sign in to comment.