Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions resources/js/components/ui/Field.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script setup>
import { cva } from 'cva';
import { computed } from 'vue';
import { computed, useSlots } from 'vue';
import Description from './Description.vue';
import Label from './Label.vue';
import ErrorMessage from './ErrorMessage.vue';
Expand All @@ -11,6 +11,8 @@ defineOptions({
inheritAttrs: false,
});

const slots = useSlots();

const props = defineProps({
/** When `true`, the field is styled as a configuration field with a two-column grid layout. */
inline: { type: Boolean, default: false },
Expand All @@ -30,13 +32,16 @@ const props = defineProps({
instructionsBelow: { type: Boolean, default: false },
/** Label text for the field. */
label: { type: String },
/** When `true`, the display label is hidden (e.g. visually hidden for accessibility only). */
hideDisplay: { type: Boolean, default: false },
readOnly: { type: Boolean, default: false },
required: { type: Boolean, default: false },
});

const labelProps = computed(() => ({
badge: props.badge,
for: props.id,
hideDisplay: props.hideDisplay,
required: props.required,
text: props.label,
}));
Expand Down Expand Up @@ -92,6 +97,14 @@ const hasErrors = computed(() => {
if (!errors.value) return false;
return Array.isArray(errors.value) ? errors.value.length > 0 : Object.keys(errors.value).length > 0;
});

const hasVisibleLabel = computed(() => {
if (props.hideDisplay) {
return false;
}

return !!slots.label || !!props.label?.trim() || props.required;
});
</script>

<template>
Expand All @@ -113,7 +126,7 @@ const hasErrors = computed(() => {
<div
v-if="(!$slots.actions && (label || (instructions && !instructionsBelow) || $slots.label)) || ($slots.actions && instructions && !instructionsBelow)"
data-ui-field-text
:class="inline ? 'mb-0' : 'mb-2'"
:class="inline ? 'mb-0' : hasVisibleLabel ? 'mb-2' : undefined"
>
<slot v-if="!$slots.actions" name="label">
<Label v-if="label" v-bind="labelProps" class="flex-1" />
Expand Down
15 changes: 13 additions & 2 deletions resources/js/components/ui/Label.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script setup>
import { useSlots } from 'vue';
import { computed, useSlots } from 'vue';
import Badge from './Badge.vue';

const slots = useSlots();
Expand All @@ -10,15 +10,26 @@ const props = defineProps({
for: { type: String, default: null },
/** Optional badge text to display on the right side of the label */
badge: { type: String, default: '' },
/** When `true`, the display label is hidden (e.g. visually hidden for accessibility only). */
hideDisplay: { type: Boolean, default: false },
required: { type: Boolean, default: false },
/** The label text to display */
text: { type: [String, Number, Boolean, null], default: null },
});

const hasVisibleLabel = computed(() => {
if (props.hideDisplay) {
return false;
}

return hasDefaultSlot || !!props.text?.trim() || props.required;
});
</script>

<template>
<label
class="flex justify-between mb-1.5 text-sm font-medium [&_button]:font-medium text-gray-925 select-none dark:text-gray-300 [&_button:has(svg)]:h-auto"
class="flex justify-between text-sm font-medium [&_button]:font-medium text-gray-925 select-none dark:text-gray-300 [&_button:has(svg)]:h-auto"
:class="{ 'mb-1.5': hasVisibleLabel }"
data-ui-label
:for="for"
>
Expand Down
3 changes: 2 additions & 1 deletion resources/js/components/ui/Publish/Field.vue
Original file line number Diff line number Diff line change
Expand Up @@ -239,10 +239,11 @@ const fieldtypeComponentEvents = computed(() => ({
:read-only="isReadOnly"
:inline="asConfig"
:full-width-setting="config.full_width_setting"
:hide-display="config.hide_display"
v-bind="$attrs"
>
<template #label v-if="shouldShowLabel">
<Label :for="fieldId" :required="isRequired" class="relative">
<Label :for="fieldId" :required="isRequired" :hide-display="config.hide_display" class="relative">
<Transition name="lock-avatar-pop" mode="out-in">
<Avatar
v-if="isLocked"
Expand Down
22 changes: 22 additions & 0 deletions resources/js/stories/Label.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,25 @@ export const _WithSlot: Story = {
template: withSlotCode,
}),
};

const hideDisplayCode = `
<Field hide-display>
<Label for="section" hide-display>
<span class="sr-only">Section</span>
</Label>
<Input id="section" placeholder="Content…" />
</Field>
`;

export const _HideDisplay: Story = {
tags: ['!dev'],
parameters: {
docs: {
source: { code: hideDisplayCode }
}
},
render: () => ({
components: { Label, Input, Field },
template: hideDisplayCode,
}),
};
Loading