Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -17,33 +17,31 @@
tabindex="0"
class="oc-bottom-drawer fixed inset-x-0 bg-role-surface-container-high rounded-t-sm w-full max-h-[66vh] overflow-y-auto bottom-[-100%] transition-all duration-200"
>
<div class="oc-card bg-transparent">
<div class="oc-card-header border-b-0 px-4 pt-4">
<div class="flex justify-between items-center">
<oc-button
v-if="isNestedElement"
appearance="raw"
class="raw-hover-surface oc-bottom-drawer-back-button"
:aria-label="$gettext('Open the parent context menu')"
@click="openParentDrawer"
>
<oc-icon name="arrow-left" fill-type="line" />
</oc-button>
<span class="font-semibold" v-text="title" />
<oc-button
appearance="raw"
class="raw-hover-surface oc-bottom-drawer-close-button"
:aria-label="$gettext('Close the context menu')"
@click="hide"
>
<oc-icon name="close" fill-type="line" />
</oc-button>
</div>
</div>
<div ref="bottomDrawerCardBodyRef" class="oc-card-body px-4 pb-4 pt-2">
<oc-card class="bg-transparent" header-class="flex flex-row justify-between items-center">
<template #header>
<oc-button
v-if="isNestedElement"
appearance="raw"
class="raw-hover-surface oc-bottom-drawer-back-button"
:aria-label="$gettext('Open the parent context menu')"
@click="openParentDrawer"
>
<oc-icon name="arrow-left" fill-type="line" />
</oc-button>
<span class="font-semibold" v-text="title" />
<oc-button
appearance="raw"
class="raw-hover-surface oc-bottom-drawer-close-button"
:aria-label="$gettext('Close the context menu')"
@click="hide"
>
<oc-icon name="close" fill-type="line" />
</oc-button>
</template>
<div ref="bottomDrawerCardBodyRef">
<slot />
</div>
</div>
</oc-card>
</div>
</focus-trap>
</div>
Expand All @@ -65,6 +63,7 @@ import { useGettext } from 'vue3-gettext'
import { FocusTrap } from 'focus-trap-vue'
import { onKeyStroke } from '@vueuse/core'
import OcButton from '../OcButton/OcButton.vue'
import OcCard from '../OcCard/OcCard.vue'

interface Props {
/**
Expand Down
4 changes: 2 additions & 2 deletions packages/design-system/src/components/OcButton/OcButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
'no-hover': noHover
}
]"
class="oc-button inline-flex cursor-pointer disabled:opacity-60 disabled:cursor-default"
class="oc-button cursor-pointer disabled:opacity-60 disabled:cursor-default"
v-on="handlers"
>
<oc-spinner v-if="showSpinner" size="small" class="spinner" />
Expand Down Expand Up @@ -178,7 +178,7 @@ const onClick = (event: MouseEvent) => {
@apply py-1.5 px-2.5;
}
.oc-button {
@apply rounded-sm items-center;
@apply rounded-sm items-center inline-flex;
}
.oc-button-group {
@apply inline-flex flex-row flex-wrap rounded-sm outline outline-role-secondary outline-offset-[-1px];
Expand Down
127 changes: 127 additions & 0 deletions packages/design-system/src/components/OcCard/OcCard.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
<template>
<component :is="tag" class="oc-card">
<div v-if="hasSlotHeader" class="oc-card-header" :class="headerClass">
<slot name="header">
<img
v-if="logoUrl"
:src="logoUrl"
alt=""
:aria-hidden="true"
class="max-w-48 max-h-48 absolute -translate-y-16"
/>
<component :is="titleTag" v-if="title" class="mt-0">
{{ title }}
</component>
</slot>
</div>
<div class="oc-card-body" :class="bodyClass">
<slot />
</div>
<div v-if="hasSlotFooter" class="oc-card-footer" :class="footerClass">
<slot name="footer" />
</div>
</component>
</template>

<script setup lang="ts">
import { computed, unref } from 'vue'

export interface Props {
/**
* @docs The html tag being used to render this card.
* @default 'div'
*/
tag?: string
/**
* @docs The title for the default card header. Don't provide one if you fill the 'header' slot yourself.
*/
title?: string
/**
* @docs The html tag being used to render the title of this card.
* @default 'h2'
*/
titleTag?: string
/**
* @docs The url of the logo to be rendered in the header of this card.
* @default ''
*/
logoUrl?: string
/**
* @docs The classes to be applied on the card header
* @default ''
*/
headerClass?: string | string[] | Record<string, boolean> | Record<string, boolean>[]
/**
* @docs The classes to be applied on the card body.
* @default ''
*/
bodyClass?: string | string[] | Record<string, boolean> | Record<string, boolean>[]
/**
* @docs The classes to be applied on the card footer.
* @default ''
*/
footerClass?: string | string[] | Record<string, boolean> | Record<string, boolean>[]
}

export interface Slots {
/**
* @docs Content of the card.
*/
default?: () => unknown
/**
* @docs Content of the header. By default, this is filled with a `<h2>` tag which renders the title.
*/
header?: () => unknown
/**
* @docs Content of the footer. Empty by default.
*/
footer?: () => unknown
}

const {
tag = 'div',
title,
titleTag = 'h2',
logoUrl = '',
headerClass = '',
bodyClass = '',
footerClass = ''
} = defineProps<Props>()

const slots = defineSlots<Slots>()

const hasSlotHeader = computed(() => {
return Object.hasOwn(slots, 'header') || !!unref(title) || !!unref(logoUrl)
})
const hasSlotFooter = computed(() => {
return Object.hasOwn(slots, 'footer')
})
</script>

<style>
@reference "@opencloud-eu/design-system/tailwind";

@layer components {
.oc-card {
@apply bg-role-surface text-role-on-surface rounded-sm relative;
}

.oc-card-header {
@apply p-4 pb-0 flex flex-col items-center relative;
}

.oc-card-body {
@apply p-4 flow-root;
}

.oc-card-footer {
@apply p-4 pt-0 flex flex-col items-center text-sm;
}

.oc-card-body > :last-child,
.oc-card-header > :last-child,
.oc-card-footer > :last-child {
@apply mb-0;
}
}
</style>
8 changes: 3 additions & 5 deletions packages/design-system/src/components/OcDrop/OcDrop.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,9 @@
<slot />
</oc-bottom-drawer>
<div v-else :id="dropId" ref="drop" class="oc-drop shadow-md/20 rounded-sm" @click="onClick">
<div
v-if="$slots.default"
:class="['oc-card oc-card-body', getTailwindPaddingClass(paddingSize)]"
>
<oc-card v-if="$slots.default" :body-class="[getTailwindPaddingClass(paddingSize)]">
<slot />
</div>
</oc-card>
<slot v-else name="special" />
</div>
</template>
Expand All @@ -43,6 +40,7 @@ import {
} from 'vue'
import { useIsMobile } from '../../composables'
import OcBottomDrawer from '../OcBottomDrawer/OcBottomDrawer.vue'
import OcCard from '../OcCard/OcCard.vue'

export interface Props {
/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,21 @@ exports[`OcDrop > tippy > renders tippy 1`] = `
id="oc-drop-15"
>
<div
class="oc-card oc-card-body p-4"
class="oc-card"
>

show

<!--v-if-->

<div
class="oc-card-body p-4"
>


show


</div>

<!--v-if-->
</div>
</div>
</div>
Expand Down Expand Up @@ -83,11 +93,21 @@ exports[`OcDrop > tippy > renders tippy 2`] = `
id="oc-drop-15"
>
<div
class="oc-card oc-card-body p-4"
class="oc-card"
>

show

<!--v-if-->

<div
class="oc-card-body p-4"
>


show


</div>

<!--v-if-->
</div>
</div>
</div>
Expand Down Expand Up @@ -131,11 +151,21 @@ exports[`OcDrop > tippy > renders tippy 3`] = `
id="oc-drop-15"
>
<div
class="oc-card oc-card-body p-4"
class="oc-card"
>

show

<!--v-if-->

<div
class="oc-card-body p-4"
>


show


</div>

<!--v-if-->
</div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,10 @@ exports[`OcModal > displays loading state 1`] = `
<!--v-if-->
</div>
<div class="oc-modal-body-actions flex justify-end p-4 text-right">
<div class="oc-modal-body-actions-grid grid grid-flow-col auto-cols-1fr"><button type="button" disabled="" class="oc-button-secondary oc-button-outline oc-button-secondary-outline gap-2 justify-center text-base min-h-4 oc-button inline-flex cursor-pointer disabled:opacity-60 disabled:cursor-default oc-modal-body-actions-cancel">
<div class="oc-modal-body-actions-grid grid grid-flow-col auto-cols-1fr"><button type="button" disabled="" class="oc-button-secondary oc-button-outline oc-button-secondary-outline gap-2 justify-center text-base min-h-4 oc-button cursor-pointer disabled:opacity-60 disabled:cursor-default oc-modal-body-actions-cancel">
<!--v-if-->
<!-- @slot Content of the button --> Cancel
</button> <button type="button" disabled="" class="oc-button-secondary oc-button-outline oc-button-secondary-outline gap-2 justify-center text-base min-h-4 oc-button inline-flex cursor-pointer disabled:opacity-60 disabled:cursor-default oc-modal-body-actions-confirm ml-2"><span class="oc-spinner inline-block after:block after:bg-transparent after:border after:border-current after:rounded-full after:size-full relative after:relative animate-spin after:content-[''] after:border-b-transparent size-4 spinner" aria-label="" tabindex="-1" role="img"></span> <!-- @slot Content of the button --> Confirm</button></div>
</button> <button type="button" disabled="" class="oc-button-secondary oc-button-outline oc-button-secondary-outline gap-2 justify-center text-base min-h-4 oc-button cursor-pointer disabled:opacity-60 disabled:cursor-default oc-modal-body-actions-confirm ml-2"><span class="oc-spinner inline-block after:block after:bg-transparent after:border after:border-current after:rounded-full after:size-full relative after:relative animate-spin after:content-[''] after:border-b-transparent size-4 spinner" aria-label="" tabindex="-1" role="img"></span> <!-- @slot Content of the button --> Confirm</button></div>
</div>
</div>
</focus-trap-stub>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ exports[`OcTextInput > password input field > password policy > displays error s
</label>
<div class="relative">
<!--v-if-->
<div class="oc-text-input-password-wrapper flex flex-row border rounded-sm border-role-outline"><input id="oc-textinput-20" aria-invalid="false" class="oc-text-input oc-input grow-2 border-0 focus:border-0 focus:outline-0" type="password"> <button type="button" aria-label="Show password" class="oc-button-secondary oc-button-raw oc-button-secondary-raw gap-2 justify-center text-sm min-h-3 oc-button inline-flex cursor-pointer disabled:opacity-60 disabled:cursor-default oc-text-input-show-password-toggle px-2">
<div class="oc-text-input-password-wrapper flex flex-row border rounded-sm border-role-outline"><input id="oc-textinput-20" aria-invalid="false" class="oc-text-input oc-input grow-2 border-0 focus:border-0 focus:outline-0" type="password"> <button type="button" aria-label="Show password" class="oc-button-secondary oc-button-raw oc-button-secondary-raw gap-2 justify-center text-sm min-h-3 oc-button cursor-pointer disabled:opacity-60 disabled:cursor-default oc-text-input-show-password-toggle px-2">
<!--v-if-->
<!-- @slot Content of the button --> <span class="oc-icon box-content size-4"><svg data-testid="inline-svg-stub" src="icons/eye-fill.svg" transform-source="(svg) => {
if (__props.accessibleLabel !== &quot;&quot;) {
Expand All @@ -17,7 +17,7 @@ exports[`OcTextInput > password input field > password policy > displays error s
};
return svg;
}" aria-hidden="true" focusable="false" class="size-4"></svg></span>
</button> <button type="button" aria-label="Copy password" class="oc-button-secondary oc-button-raw oc-button-secondary-raw gap-2 justify-center text-sm min-h-3 oc-button inline-flex cursor-pointer disabled:opacity-60 disabled:cursor-default oc-text-input-copy-password-button px-2">
</button> <button type="button" aria-label="Copy password" class="oc-button-secondary oc-button-raw oc-button-secondary-raw gap-2 justify-center text-sm min-h-3 oc-button cursor-pointer disabled:opacity-60 disabled:cursor-default oc-text-input-copy-password-button px-2">
<!--v-if-->
<!-- @slot Content of the button --> <span class="oc-icon box-content size-4"><svg data-testid="inline-svg-stub" src="icons/file-copy-fill.svg" transform-source="(svg) => {
if (__props.accessibleLabel !== &quot;&quot;) {
Expand Down Expand Up @@ -59,7 +59,7 @@ exports[`OcTextInput > password input field > password policy > displays success
</label>
<div class="relative">
<!--v-if-->
<div class="oc-text-input-password-wrapper flex flex-row border rounded-sm border-role-outline"><input id="oc-textinput-21" aria-invalid="false" class="oc-text-input oc-input grow-2 border-0 focus:border-0 focus:outline-0" type="password"> <button type="button" aria-label="Show password" class="oc-button-secondary oc-button-raw oc-button-secondary-raw gap-2 justify-center text-sm min-h-3 oc-button inline-flex cursor-pointer disabled:opacity-60 disabled:cursor-default oc-text-input-show-password-toggle px-2">
<div class="oc-text-input-password-wrapper flex flex-row border rounded-sm border-role-outline"><input id="oc-textinput-21" aria-invalid="false" class="oc-text-input oc-input grow-2 border-0 focus:border-0 focus:outline-0" type="password"> <button type="button" aria-label="Show password" class="oc-button-secondary oc-button-raw oc-button-secondary-raw gap-2 justify-center text-sm min-h-3 oc-button cursor-pointer disabled:opacity-60 disabled:cursor-default oc-text-input-show-password-toggle px-2">
<!--v-if-->
<!-- @slot Content of the button --> <span class="oc-icon box-content size-4"><svg data-testid="inline-svg-stub" src="icons/eye-fill.svg" transform-source="(svg) => {
if (__props.accessibleLabel !== &quot;&quot;) {
Expand All @@ -70,7 +70,7 @@ exports[`OcTextInput > password input field > password policy > displays success
};
return svg;
}" aria-hidden="true" focusable="false" class="size-4"></svg></span>
</button> <button type="button" aria-label="Copy password" class="oc-button-secondary oc-button-raw oc-button-secondary-raw gap-2 justify-center text-sm min-h-3 oc-button inline-flex cursor-pointer disabled:opacity-60 disabled:cursor-default oc-text-input-copy-password-button px-2">
</button> <button type="button" aria-label="Copy password" class="oc-button-secondary oc-button-raw oc-button-secondary-raw gap-2 justify-center text-sm min-h-3 oc-button cursor-pointer disabled:opacity-60 disabled:cursor-default oc-text-input-copy-password-button px-2">
<!--v-if-->
<!-- @slot Content of the button --> <span class="oc-icon box-content size-4"><svg data-testid="inline-svg-stub" src="icons/file-copy-fill.svg" transform-source="(svg) => {
if (__props.accessibleLabel !== &quot;&quot;) {
Expand Down
1 change: 1 addition & 0 deletions packages/design-system/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export { default as OcAvatars } from './OcAvatars/OcAvatars.vue'
export { default as OcBottomDrawer } from './OcBottomDrawer/OcBottomDrawer.vue'
export { default as OcBreadcrumb } from './OcBreadcrumb/OcBreadcrumb.vue'
export { default as OcButton } from './OcButton/OcButton.vue'
export { default as OcCard } from './OcCard/OcCard.vue'
export { default as OcCheckbox } from './OcCheckbox/OcCheckbox.vue'
export { default as OcColorInput } from './OcColorInput/OcColorInput.vue'
export { default as OcContextualHelper } from './OcContextualHelper/OcContextualHelper.vue'
Expand Down
Loading