CHANGED: style and components directories, removed redundant imports from styles
ADDED: container and card component, card-subcomponents WIP
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
'use client';
|
||||
|
||||
import React, {
|
||||
forwardRef,
|
||||
useEffect,
|
||||
useImperativeHandle,
|
||||
useRef,
|
||||
} from 'react';
|
||||
import { bool, string } from 'prop-types';
|
||||
import { CheckboxLayoutProps } from './checkbox-layout.types';
|
||||
|
||||
export const CheckBoxLayout = forwardRef<HTMLInputElement, CheckboxLayoutProps>(
|
||||
({ indeterminate, typeInput, type, ...props }, ref) => {
|
||||
const checkboxRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
checkboxRef.current.indeterminate = indeterminate === true;
|
||||
}, []);
|
||||
|
||||
useImperativeHandle(ref, () => checkboxRef.current);
|
||||
|
||||
const classesType = typeInput || type;
|
||||
|
||||
const classes =
|
||||
props.className !== undefined
|
||||
? `m3 m3-${type} ${props.className}`
|
||||
: `m3 m3-${classesType}`;
|
||||
|
||||
return (
|
||||
<input
|
||||
ref={checkboxRef}
|
||||
{...props}
|
||||
className={classes.trimEnd()}
|
||||
type={type}
|
||||
/>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
CheckBoxLayout.propTypes = {
|
||||
typeInput: string,
|
||||
indeterminate: bool,
|
||||
};
|
||||
@@ -0,0 +1,12 @@
|
||||
import { InputHTMLAttributes } from 'react';
|
||||
|
||||
export interface CheckboxLayoutProps
|
||||
extends InputHTMLAttributes<HTMLInputElement> {
|
||||
indeterminate?: boolean;
|
||||
typeInput?: string;
|
||||
type?: string;
|
||||
}
|
||||
|
||||
export interface LabelPlacement {
|
||||
labelPlacement?: 'left' | 'right';
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
'use client';
|
||||
|
||||
import { bool } from 'prop-types';
|
||||
import { CheckboxProps } from './checkbox.types';
|
||||
import { RippleArea } from '../../ripple/ripple-area';
|
||||
import useRippleEffect from '../../ripple/hooks/useRippleEffect';
|
||||
import { CheckBoxLayout } from '../checkbox-layout/check-box-layout';
|
||||
import {
|
||||
forwardRef,
|
||||
useEffect,
|
||||
useImperativeHandle,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react';
|
||||
|
||||
/**
|
||||
* Checkbox component
|
||||
** description
|
||||
*/
|
||||
|
||||
export const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>(
|
||||
({ centralRipple, ...props }, ref) => {
|
||||
const [isActive, setIsActive] = useState<boolean>(false),
|
||||
[checked, setChecked] = useState<boolean>(props.checked ?? false),
|
||||
ripplesRef = useRef(null),
|
||||
checkboxRef = useRef(null),
|
||||
events = useRippleEffect(ripplesRef, setIsActive);
|
||||
|
||||
const classes =
|
||||
`m3 m3-checkbox-label ${isActive === true ? 'visible' : ''}`.trimEnd();
|
||||
|
||||
useImperativeHandle(ref, () => checkboxRef.current);
|
||||
|
||||
useEffect(() => {
|
||||
setChecked(!checked);
|
||||
}, [checkboxRef.current?.checked]);
|
||||
|
||||
return (
|
||||
<label {...events} className={classes}>
|
||||
<CheckBoxLayout
|
||||
{...props}
|
||||
indeterminate={props.indeterminate}
|
||||
ref={checkboxRef}
|
||||
type={'checkbox'}
|
||||
/>
|
||||
<span className={'m3 m3-checkbox-state'} />
|
||||
<span className={'m3 m3-checkbox-state-layer'} />
|
||||
<RippleArea
|
||||
callback={setIsActive}
|
||||
central={centralRipple}
|
||||
className={'m3-checkbox-ripple-layer'}
|
||||
ref={ripplesRef}
|
||||
/>
|
||||
{props.children}
|
||||
</label>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
Checkbox.propTypes = {
|
||||
indeterminate: bool,
|
||||
centralRipple: bool,
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
import { InputHTMLAttributes } from 'react';
|
||||
import { RipplePropsForComponents } from '../../ripple/ripple.types';
|
||||
|
||||
export type CheckboxProps = InputHTMLAttributes<HTMLInputElement> &
|
||||
RipplePropsForComponents<HTMLInputElement> & {
|
||||
indeterminate?: boolean;
|
||||
};
|
||||
54
src/primitive-components/input-components/radio/radio.tsx
Normal file
54
src/primitive-components/input-components/radio/radio.tsx
Normal file
@@ -0,0 +1,54 @@
|
||||
'use client';
|
||||
|
||||
import { bool, string } from 'prop-types';
|
||||
import { RadioProps } from './radio.types';
|
||||
import { RippleArea } from '../../ripple/ripple-area';
|
||||
import { forwardRef, useRef, useState } from 'react';
|
||||
import useRippleEffect from '../../ripple/hooks/useRippleEffect';
|
||||
import { CheckBoxLayout } from '../checkbox-layout/check-box-layout';
|
||||
|
||||
/**
|
||||
* Radio component
|
||||
** description
|
||||
*/
|
||||
|
||||
export const Radio = forwardRef<HTMLInputElement, RadioProps>(
|
||||
({ centralRipple, ...props }, ref) => {
|
||||
const [isActive, setIsActive] = useState<boolean>(false),
|
||||
ripplesRef = useRef(null),
|
||||
events = useRippleEffect(ripplesRef, setIsActive);
|
||||
|
||||
const classes =
|
||||
`m3 m3-radio ${isActive === true ? 'visible' : ''}`.trimEnd();
|
||||
|
||||
return (
|
||||
<div {...events} className={classes}>
|
||||
<CheckBoxLayout {...props} ref={ref} type={'radio'} />
|
||||
<span className={'m3 m3-radio-state-layer'} />
|
||||
<svg height={'20px'} viewBox={'0 0 20 20'} width={'20px'}>
|
||||
<circle
|
||||
className={'m3-radio-outline'}
|
||||
cx={'50%'}
|
||||
cy={'50%'}
|
||||
/>
|
||||
<circle
|
||||
className={'m3-radio-state'}
|
||||
cx={'50%'}
|
||||
cy={'50%'}
|
||||
/>
|
||||
</svg>
|
||||
<RippleArea
|
||||
callback={setIsActive}
|
||||
central={centralRipple}
|
||||
className={'m3-checkbox-ripple-layer'}
|
||||
ref={ripplesRef}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
Radio.propTypes = {
|
||||
children: string,
|
||||
centralRipple: bool,
|
||||
};
|
||||
@@ -0,0 +1,8 @@
|
||||
import { InputHTMLAttributes } from 'react';
|
||||
import { RipplePropsForComponents } from '../../ripple/ripple.types';
|
||||
|
||||
export type RadioProps = InputHTMLAttributes<HTMLInputElement> &
|
||||
RipplePropsForComponents<HTMLInputElement> & {
|
||||
children?: string;
|
||||
centralRipple?: boolean;
|
||||
};
|
||||
42
src/primitive-components/input-components/switch/switch.tsx
Normal file
42
src/primitive-components/input-components/switch/switch.tsx
Normal file
@@ -0,0 +1,42 @@
|
||||
'use client';
|
||||
|
||||
import { bool } from 'prop-types';
|
||||
import React, { forwardRef } from 'react';
|
||||
import { SwitchMainProps } from './switch.types';
|
||||
import { CheckBoxLayout } from '../checkbox-layout/check-box-layout';
|
||||
import { LabelPlacement } from '../checkbox-layout/checkbox-layout.types';
|
||||
|
||||
/**
|
||||
* Switch component
|
||||
** description
|
||||
*/
|
||||
|
||||
export const Switch = forwardRef<
|
||||
HTMLInputElement,
|
||||
SwitchMainProps & LabelPlacement
|
||||
>(({ icon, selected = false, ...props }, ref) => (
|
||||
<div className={'m3 m3-switch'}>
|
||||
<CheckBoxLayout
|
||||
{...props}
|
||||
className={`m3 ${props.className ?? ''}`.trimEnd()}
|
||||
ref={ref}
|
||||
type={'checkbox'}
|
||||
/>
|
||||
<svg>
|
||||
<rect className={'m3 m3-switch-track'} />
|
||||
<circle className={'m3 m3-switch-handler'} />
|
||||
<circle className={'m3 m3-switch-handler-state-layer'} />
|
||||
<g>
|
||||
{icon && !selected && (
|
||||
<text className={'m3 m3-icon-unchecked'}>close</text>
|
||||
)}
|
||||
{icon && <text className={'m3 m3-icon-checked'}>check</text>}
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
));
|
||||
|
||||
Switch.propTypes = {
|
||||
icon: bool,
|
||||
selected: bool,
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
import { InputHTMLAttributes } from 'react';
|
||||
|
||||
export interface SwitchMainProps extends InputHTMLAttributes<HTMLInputElement> {
|
||||
disabled?: boolean;
|
||||
icon?: boolean;
|
||||
selected?: boolean;
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
'use client';
|
||||
|
||||
import { bool, oneOf, string } from 'prop-types';
|
||||
import { TextFieldInterface } from './text-field.types';
|
||||
import React, { FocusEvent, forwardRef, useState } from 'react';
|
||||
|
||||
export const TextField = forwardRef<HTMLInputElement, TextFieldInterface>(
|
||||
(
|
||||
{
|
||||
variant = 'filled',
|
||||
withAfterIcon,
|
||||
withBeforeIcon,
|
||||
supportingText,
|
||||
...props
|
||||
},
|
||||
ref,
|
||||
) => {
|
||||
const [raised, setRaised] = useState<boolean>(!!props.placeholder);
|
||||
|
||||
const callback = (e: FocusEvent<HTMLInputElement>): void => {
|
||||
if (
|
||||
e.type === 'blur' &&
|
||||
e.target.value.length === 0 &&
|
||||
!props.placeholder
|
||||
) {
|
||||
setRaised(false);
|
||||
} else if (e.type === 'focus') {
|
||||
setRaised(true);
|
||||
}
|
||||
};
|
||||
|
||||
const iconStyles =
|
||||
withBeforeIcon && withAfterIcon
|
||||
? 'with-before-icon with-after-icon'
|
||||
: withBeforeIcon
|
||||
? 'with-before-icon'
|
||||
: withAfterIcon
|
||||
? 'with-after-icon'
|
||||
: '';
|
||||
|
||||
return (
|
||||
<span>
|
||||
<div className={`m3 m3-text-field ${variant}`.trimEnd()}>
|
||||
{variant === 'outlined' && (
|
||||
<fieldset>
|
||||
<legend className={raised ? 'raised' : ''}>
|
||||
<span>Label</span>
|
||||
</legend>
|
||||
</fieldset>
|
||||
)}
|
||||
{withBeforeIcon && (
|
||||
<span className={'m3-icon icon-before'}>
|
||||
{withBeforeIcon && 'search'}
|
||||
</span>
|
||||
)}
|
||||
<input
|
||||
ref={ref}
|
||||
{...props}
|
||||
className={`${props.className ?? ''} ${iconStyles}`.trim()}
|
||||
onBlur={event => {
|
||||
callback(event);
|
||||
if (props.onBlur) {
|
||||
props.onBlur(event);
|
||||
}
|
||||
}}
|
||||
onFocus={event => {
|
||||
callback(event);
|
||||
if (props.onFocus) {
|
||||
props.onFocus(event);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<label className={raised ? 'raised' : ''}>
|
||||
{props.children ?? 'Label'}
|
||||
</label>
|
||||
<span className={'m3-text-field-state-layer'} />
|
||||
{withAfterIcon && (
|
||||
<span className={'m3-icon'}>
|
||||
{withAfterIcon && 'cancel'}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
{supportingText !== '' && (
|
||||
<span className={'m3-text-field-supporting-text'}>
|
||||
{supportingText}
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
TextField.propTypes = {
|
||||
children: string,
|
||||
withAfterIcon: bool,
|
||||
withBeforeIcon: bool,
|
||||
supportingText: string,
|
||||
variant: oneOf(['filled', 'outlined']),
|
||||
};
|
||||
@@ -0,0 +1,9 @@
|
||||
import { InputHTMLAttributes } from 'react';
|
||||
|
||||
export interface TextFieldInterface
|
||||
extends InputHTMLAttributes<HTMLInputElement> {
|
||||
variant: 'filled' | 'outlined';
|
||||
withAfterIcon?: boolean;
|
||||
withBeforeIcon?: boolean;
|
||||
supportingText?: string;
|
||||
}
|
||||
Reference in New Issue
Block a user