- added NcModal.vue, NcActions.vue, NcButton.vue, FirstrunWizard.vue, Card.vue, Page0.vue, Page1.vue, Page2.vue, Page3.vue and some icons
Some checks failed
CI Pipeline / japa-tests (push) Failing after 51s
Some checks failed
CI Pipeline / japa-tests (push) Failing after 51s
- added lime color inside tailwind.config.js - added some utilities scripts needed for components - npm updates - changed postcss.config.js for nesting css styles - added about function to NavBar.vue
This commit is contained in:
parent
cefd9081ae
commit
87e9314b00
25 changed files with 3475 additions and 401 deletions
544
resources/js/Components/NcButton.vue
Normal file
544
resources/js/Components/NcButton.vue
Normal file
|
@ -0,0 +1,544 @@
|
|||
<template>
|
||||
<component :is="is" :class="componentClass" :target="target" href="href" aria-label="ariaLabel" aria-pressed="pressed"
|
||||
:disabled="disabled" :type="type" v-on:click="click">
|
||||
|
||||
<span class="button-vue__wrapper">
|
||||
<!-- <span v-if="hasIcon" class="button-vue__icon" aria-hidden="true">
|
||||
{{ this.$slots.icon }}-->
|
||||
<BaseIcon v-if="icon" :path="icon" class="button-vue__icon" aria-hidden="true" :size="size" />
|
||||
|
||||
<!-- <span v-if="hasText" class="button-vue__text">{{ this.$slots.default }}</span> -->
|
||||
<span v-if="label" class="font-bold whitespace-nowrap text-ellipsis overflow-hidden" :class="labelClass">{{
|
||||
label }}</span>
|
||||
</span>
|
||||
</component>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import BaseIcon from '@/Components/BaseIcon.vue';
|
||||
// import { h } from 'vue';
|
||||
export default {
|
||||
name: 'NcButton',
|
||||
|
||||
components: {
|
||||
BaseIcon
|
||||
},
|
||||
|
||||
props: {
|
||||
size: {
|
||||
type: [String, Number],
|
||||
default: 16,
|
||||
},
|
||||
small: Boolean,
|
||||
roundedFull: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
label: {
|
||||
type: [String, Number],
|
||||
default: null,
|
||||
},
|
||||
icon: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
/**
|
||||
* Set the text and icon alignment
|
||||
*
|
||||
* @default 'center'
|
||||
*/
|
||||
alignment: {
|
||||
type: String,
|
||||
default: 'center',
|
||||
validator: (alignment: string) => ['start', 'start-reverse', 'center', 'center-reverse', 'end', 'end-reverse'].includes(alignment),
|
||||
},
|
||||
|
||||
/**
|
||||
* Toggles the disabled state of the button on and off.
|
||||
*/
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
|
||||
/**
|
||||
* Specifies the button type
|
||||
* Accepted values: primary, secondary, tertiary, tertiary-no-background, tertiary-on-primary, error, warning, success. If left empty,
|
||||
* the default button style will be applied.
|
||||
*/
|
||||
type: {
|
||||
type: String,
|
||||
validator(value: string) {
|
||||
return (
|
||||
[
|
||||
'primary',
|
||||
'secondary',
|
||||
'tertiary',
|
||||
'tertiary-no-background',
|
||||
'tertiary-on-primary',
|
||||
'error',
|
||||
'warning',
|
||||
'success',
|
||||
].indexOf(value) !== -1
|
||||
);
|
||||
},
|
||||
default: 'secondary',
|
||||
},
|
||||
|
||||
/**
|
||||
* Specifies the button native type
|
||||
* Accepted values: submit, reset, button. If left empty,
|
||||
* the default "button" type will be used.
|
||||
*/
|
||||
nativeType: {
|
||||
type: String,
|
||||
validator(value: string) {
|
||||
return ['submit', 'reset', 'button'].indexOf(value) !== -1;
|
||||
},
|
||||
default: 'button',
|
||||
},
|
||||
|
||||
/**
|
||||
* Specifies whether the button should span all the available width.
|
||||
* By default, buttons span the whole width of the container.
|
||||
*/
|
||||
wide: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
|
||||
/**
|
||||
* Always try to provide an aria-label to your button. Make it more
|
||||
* specific than the button's name by provide some more context. E.g. if
|
||||
* the name of the button is "send" in the Mail app, the aria label could
|
||||
* be "Send email".
|
||||
*/
|
||||
ariaLabel: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
|
||||
/**
|
||||
* Providing the href attribute turns the button component into an `a`
|
||||
* element.
|
||||
*/
|
||||
href: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
|
||||
/**
|
||||
* Providing the download attribute with href downloads file when clicking.
|
||||
*/
|
||||
download: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
|
||||
/**
|
||||
* Providing the to attribute turns the button component into a `router-link`
|
||||
* element. Takes precedence over the href attribute.
|
||||
*/
|
||||
to: {
|
||||
type: [String, Object],
|
||||
default: null,
|
||||
},
|
||||
|
||||
/**
|
||||
* Pass in `true` if you want the matching behaviour of `router-link` to
|
||||
* be non-inclusive: https://router.vuejs.org/api/#exact
|
||||
*/
|
||||
exact: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
/**
|
||||
* @deprecated To be removed in @nextcloud/vue 9. Migration guide: remove ariaHidden prop from NcAction* components.
|
||||
* @todo Add a check in @nextcloud/vue 9 that this prop is not provided,
|
||||
* otherwise root element will inherit incorrect aria-hidden.
|
||||
*/
|
||||
ariaHidden: {
|
||||
type: Boolean,
|
||||
default: null,
|
||||
},
|
||||
|
||||
/**
|
||||
* The pressed state of the button if it has a checked state
|
||||
* This will add the `aria-pressed` attribute and for the button to have the primary style in checked state.
|
||||
*/
|
||||
pressed: {
|
||||
type: Boolean,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
|
||||
emits: ['update:pressed', 'click'],
|
||||
|
||||
computed: {
|
||||
labelClass() {
|
||||
return (this.small && this.icon) ? 'px-1' : 'px-2';
|
||||
},
|
||||
is() {
|
||||
if (this.href) {
|
||||
return 'a';
|
||||
}
|
||||
return 'button';
|
||||
},
|
||||
hasText() {
|
||||
// return !!this.$slots.default;
|
||||
return this.label != undefined;
|
||||
},
|
||||
hasIcon() {
|
||||
// return this.$slots?.icon;
|
||||
return this.icon != undefined;
|
||||
},
|
||||
// type() {
|
||||
// return this.href ? null : this.nativeType;
|
||||
// },
|
||||
|
||||
componentClass() {
|
||||
const base = [
|
||||
'button-vue',
|
||||
this.roundedFull ? 'rounded-full' : 'rounded',
|
||||
'inline-flex',
|
||||
'cursor-pointer',
|
||||
'justify-center',
|
||||
'items-center',
|
||||
'whitespace-nowrap',
|
||||
'focus:outline-none',
|
||||
'transition-colors',
|
||||
// 'focus:ring-2',
|
||||
{
|
||||
'button-vue--icon-only': this.hasIcon && !this.hasText,
|
||||
'button-vue--text-only': this.hasText && !this.hasIcon,
|
||||
'button-vue--icon-and-text': this.hasIcon && this.hasText,
|
||||
[`button-vue--vue-${this.realType}`]: this.realType,
|
||||
'button-vue--wide': this.wide,
|
||||
[`button-vue--${this.flexAlignment}`]: this.flexAlignment !== 'center',
|
||||
'button-vue--reverse': this.isReverseAligned,
|
||||
// Add other classes based on conditions as needed
|
||||
|
||||
|
||||
},
|
||||
];
|
||||
if (this.small) {
|
||||
base.push('text-sm', this.roundedFull ? 'px-3 py-1' : 'p-1');
|
||||
} else {
|
||||
base.push('py-2', this.roundedFull ? 'px-6' : 'px-3');
|
||||
}
|
||||
return base;
|
||||
},
|
||||
|
||||
target() {
|
||||
return (!this.to && this.href) ? '_self' : null;
|
||||
},
|
||||
|
||||
/**
|
||||
* The real type to be used for the button, enforces `primary` for pressed state and, if stateful button, any other type for not pressed state
|
||||
* Otherwise the type property is used.
|
||||
*/
|
||||
realType() {
|
||||
// Force *primary* when pressed
|
||||
if (this.pressed) {
|
||||
return 'primary';
|
||||
}
|
||||
// If not pressed but button is configured as stateful button then the type must not be primary
|
||||
if (this.pressed === false && this.type === 'primary') {
|
||||
return 'secondary';
|
||||
}
|
||||
return this.type;
|
||||
},
|
||||
|
||||
/**
|
||||
* The flexbox alignment of the button content
|
||||
*/
|
||||
flexAlignment() {
|
||||
return this.alignment.split('-')[0];
|
||||
},
|
||||
|
||||
/**
|
||||
* If the button content should be reversed (icon on the end)
|
||||
*/
|
||||
isReverseAligned() {
|
||||
return this.alignment.includes('-');
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
click($event) {
|
||||
// Update pressed prop on click if it is set
|
||||
if (typeof this.pressed === 'boolean') {
|
||||
/**
|
||||
* Update the current pressed state of the button (if the `pressed` property was configured)
|
||||
*
|
||||
* @property {boolean} newValue The new `pressed`-state
|
||||
*/
|
||||
this.$emit('update:pressed', !this.pressed)
|
||||
}
|
||||
// We have to both navigate and emit the click event
|
||||
this.$emit('click', $event)
|
||||
// navigate?.($event)
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="css" scoped>
|
||||
.button-vue {
|
||||
position: relative;
|
||||
|
||||
/* width: fit-content;
|
||||
overflow: hidden;
|
||||
border: 0; */
|
||||
padding: 0;
|
||||
/* font-size: var(--default-font-size);
|
||||
font-weight: bold; */
|
||||
min-height: 44px;
|
||||
min-width: 44px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
/* justify-content: center; */
|
||||
|
||||
/* Cursor pointer on element and all children */
|
||||
cursor: pointer;
|
||||
|
||||
& *,
|
||||
span {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* border-radius: math.div($clickable-area, 2); */
|
||||
/* transition-property: color, */
|
||||
/* border-color,
|
||||
background-color;
|
||||
transition-duration: 0.1s;
|
||||
transition-timing-function: linear; */
|
||||
|
||||
/* border-radius: math.div($clickable-area, 2); */
|
||||
/* border-radius: calc(50% - var(--clickable-area) / 2);
|
||||
transition-property: color, border-color, background-color;
|
||||
transition-duration: 0.1s;
|
||||
transition-timing-function: linear; */
|
||||
|
||||
/* No outline feedback for focus. Handled with a toggled class in js (see data) */
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
cursor: default;
|
||||
|
||||
& * {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
opacity: 0.5;
|
||||
/* // Gives a wash out effect */
|
||||
filter: saturate(0.7l);
|
||||
}
|
||||
|
||||
/* // Default button type */
|
||||
color: var(--color-primary-element-light-text);
|
||||
background-color: var(--color-primary-element-light);
|
||||
|
||||
&:hover:not(:disabled) {
|
||||
background-color: var(--color-primary-element-light-hover);
|
||||
}
|
||||
|
||||
|
||||
/* Back to the default color for this button when active */
|
||||
/* TODO: add ripple effect */
|
||||
&:active {
|
||||
background-color: var(--color-primary-element-light);
|
||||
}
|
||||
|
||||
&__wrapper {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&--end &__wrapper {
|
||||
justify-content: end;
|
||||
}
|
||||
|
||||
&--start &__wrapper {
|
||||
justify-content: start;
|
||||
}
|
||||
|
||||
&--reverse &__wrapper {
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
|
||||
&--reverse &--icon-and-text {
|
||||
padding-inline: calc(var(--default-grid-baseline) * 4) var(--default-grid-baseline);
|
||||
}
|
||||
|
||||
&__icon {
|
||||
height: 44px;
|
||||
width: 44px;
|
||||
min-height: 44px;
|
||||
min-width: 44px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* &__text {
|
||||
font-weight: bold;
|
||||
margin-bottom: 1px;
|
||||
padding: 2px 0;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
} */
|
||||
|
||||
/* Icon-only button */
|
||||
&--icon-only {
|
||||
width: 44px !important;
|
||||
}
|
||||
|
||||
/* Text-only button */
|
||||
&--text-only {
|
||||
padding: 0 12px;
|
||||
|
||||
& .button-vue__text {
|
||||
margin-left: 4px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Icon and text button */
|
||||
&--icon-and-text {
|
||||
padding-block: 0;
|
||||
padding-inline: var(--default-grid-baseline) calc(var(--default-grid-baseline) * 4);
|
||||
}
|
||||
|
||||
/* Wide button spans the whole width of the container */
|
||||
&--wide {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
outline: 2px solid var(--color-main-text) !important;
|
||||
box-shadow: 0 0 0 4px var(--color-main-background) !important;
|
||||
|
||||
&.button-vue--vue-tertiary-on-primary {
|
||||
outline: 2px solid var(--color-primary-element-text);
|
||||
border-radius: var(--border-radius);
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
/* Button types */
|
||||
|
||||
/* // Primary */
|
||||
&--vue-primary {
|
||||
background-color: var(--color-primary-element);
|
||||
color: var(--color-primary-element-text);
|
||||
|
||||
&:hover:not(:disabled) {
|
||||
background-color: var(--color-primary-element-hover);
|
||||
}
|
||||
|
||||
/* Back to the default color for this button when active
|
||||
TODO: add ripple effect */
|
||||
&:active {
|
||||
background-color: var(--color-primary-element);
|
||||
}
|
||||
}
|
||||
|
||||
/* Secondary */
|
||||
&--vue-secondary {
|
||||
color: var(--color-primary-element-light-text);
|
||||
background-color: var(--color-primary-element-light);
|
||||
|
||||
&:hover:not(:disabled) {
|
||||
color: var(--color-primary-element-light-text);
|
||||
background-color: var(--color-primary-element-light-hover);
|
||||
}
|
||||
}
|
||||
|
||||
/* Tertiary */
|
||||
&--vue-tertiary {
|
||||
color: var(--color-main-text);
|
||||
background-color: transparent;
|
||||
|
||||
&:hover:not(:disabled) {
|
||||
background-color: var(--color-background-hover);
|
||||
}
|
||||
}
|
||||
|
||||
/* Tertiary, no background */
|
||||
&--vue-tertiary-no-background {
|
||||
color: var(--color-main-text);
|
||||
background-color: transparent;
|
||||
|
||||
&:hover:not(:disabled) {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
/* Tertiary on primary color (like the header) */
|
||||
&--vue-tertiary-on-primary {
|
||||
color: var(--color-primary-element-text);
|
||||
background-color: transparent;
|
||||
|
||||
&:hover:not(:disabled) {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
/* Success */
|
||||
&--vue-success {
|
||||
background-color: var(--color-success);
|
||||
color: white;
|
||||
|
||||
&:hover:not(:disabled) {
|
||||
background-color: var(--color-success-hover);
|
||||
}
|
||||
|
||||
/* Back to the default color for this button when active
|
||||
: add ripple effect */
|
||||
&:active {
|
||||
background-color: var(--color-success);
|
||||
}
|
||||
}
|
||||
|
||||
/* Warning */
|
||||
&--vue-warning {
|
||||
background-color: var(--color-warning);
|
||||
color: white;
|
||||
|
||||
&:hover:not(:disabled) {
|
||||
background-color: var(--color-warning-hover);
|
||||
}
|
||||
|
||||
/* Back to the default color for this button when active
|
||||
TODO: add ripple effect */
|
||||
&:active {
|
||||
background-color: var(--color-warning);
|
||||
}
|
||||
}
|
||||
|
||||
/* Error */
|
||||
&--vue-error {
|
||||
background-color: var(--color-error);
|
||||
color: white;
|
||||
|
||||
&:hover:not(:disabled) {
|
||||
background-color: var(--color-error-hover);
|
||||
}
|
||||
|
||||
/* Back to the default color for this button when active
|
||||
TODO: add ripple effect */
|
||||
&:active {
|
||||
background-color: var(--color-error);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
Loading…
Add table
editor.link_modal.header
Reference in a new issue