Skip to content

Commit

Permalink
feat: implement masked prop
Browse files Browse the repository at this point in the history
  • Loading branch information
logaretm committed Mar 9, 2025
1 parent 620746e commit 696cbe2
Show file tree
Hide file tree
Showing 3 changed files with 24 additions and 4 deletions.
6 changes: 6 additions & 0 deletions packages/core/src/useOtpField/useOtpField.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ export interface OTPFieldProps {
*/
disabled?: boolean;

/**
* Whether the OTP field is masked.
*/
masked?: boolean;

/**
* Whether the OTP field is readonly.
*/
Expand Down Expand Up @@ -193,6 +198,7 @@ export function useOtpField(_props: Reactivify<OTPFieldProps, 'schema'>) {
disabled: prefix.length ? prefix.length > index : isDisabled.value,
readonly: toValue(props.readonly),
accept: toValue(props.accept),
masked: prefix.length <= index && toValue(props.masked),
}));
});

Expand Down
20 changes: 17 additions & 3 deletions packages/core/src/useOtpField/useOtpSlot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ export interface OtpSlotProps {
*/
readonly?: boolean;

/**
* Whether the slot is masked.
*/
masked?: boolean;

/**
* The type of the slot.
*/
Expand Down Expand Up @@ -62,6 +67,14 @@ export function useOtpSlot(_props: Reactivify<OtpSlotProps>) {
}
}

function withMask(value: string | undefined) {
if (!toValue(props.masked) || !value) {
return value ?? '';
}

return '•'.repeat(value.length);
}

function setElementValue(value: string) {
if (!slotEl.value) {
return;
Expand All @@ -73,7 +86,7 @@ export function useOtpSlot(_props: Reactivify<OtpSlotProps>) {
return;
}

slotEl.value.textContent = value;
slotEl.value.textContent = withMask(value);
}

const handlers = {
Expand Down Expand Up @@ -146,6 +159,7 @@ export function useOtpSlot(_props: Reactivify<OtpSlotProps>) {
baseProps.contenteditable = isDisabled.value ? 'false' : isFirefox() ? 'true' : 'plaintext-only';
} else {
baseProps.value = toValue(props.value);
baseProps.type = toValue(props.masked) ? 'password' : 'text';
}

return withRefCapture(baseProps, slotEl);
Expand All @@ -154,7 +168,7 @@ export function useOtpSlot(_props: Reactivify<OtpSlotProps>) {
return {
slotProps,
key: id,
value: computed(() => toValue(props.value)),
value: computed(() => withMask(toValue(props.value))),
};
}

Expand All @@ -163,7 +177,7 @@ export function useOtpSlot(_props: Reactivify<OtpSlotProps>) {
*/
export const OtpSlot = /*#__PURE__*/ defineComponent<OtpSlotProps>({
name: 'OtpSlot',
props: ['value', 'disabled', 'readonly', 'accept'],
props: ['value', 'disabled', 'readonly', 'accept', 'masked'],
setup(props) {
const { slotProps, value, key } = useOtpSlot(props);

Expand Down
2 changes: 1 addition & 1 deletion packages/playground/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ import OtpField from '@/components/OtpField.vue';
</script>

<template>
<OtpField name="otp" label="OTP" :length="4" accept="numeric" prefix="G-" required />
<OtpField name="otp" label="OTP" :length="4" accept="numeric" prefix="G-" required masked />
</template>

0 comments on commit 696cbe2

Please sign in to comment.