This commit is contained in:
Adrian Ortiz 2025-04-13 04:09:58 +02:00 committed by GitHub
commit bb4cd65f15
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 315 additions and 3 deletions

View file

@ -0,0 +1,11 @@
import { expect, test } from '@playwright/test';
test.describe('Tool - Border generator', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/border-generator');
});
test('Has correct title', async ({ page }) => {
await expect(page).toHaveTitle('Border Generator - IT Tools');
});
});

View file

@ -0,0 +1,39 @@
import { describe, expect, it } from 'vitest';
import { generateCSSOutput } from './border-generator.service';
import type { Borders } from './border-generator.service';
describe('border-generator service', () => {
describe('generateCSSOutput', () => {
it('generates correct CSS with default values', () => {
const borders: Borders = {
topLeft: { label: 'Top Left', value: 10, max: 100 },
topRight: { label: 'Top Right', value: 10, max: 100 },
bottomRight: { label: 'Bottom Right', value: 10, max: 100 },
bottomLeft: { label: 'Bottom Left', value: 10, max: 100 },
};
const cssOutput = generateCSSOutput(borders, 1, 'solid', 'px');
expect(cssOutput).toEqual('border: 1px solid #000000; border-radius: 10px 10px 10px 10px;');
});
it('generates correct CSS with custom values', () => {
const borders: Borders = {
topLeft: { label: 'Top Left', value: 20, max: 100 },
topRight: { label: 'Top Right', value: 15, max: 100 },
bottomRight: { label: 'Bottom Right', value: 5, max: 100 },
bottomLeft: { label: 'Bottom Left', value: 25, max: 100 },
};
const cssOutput = generateCSSOutput(borders, 2, 'dashed', '%');
expect(cssOutput).toEqual('border: 2px dashed #000000; border-radius: 20% 15% 5% 25%;');
});
it('throws an error when borders are missing', () => {
const borders = {} as Borders;
expect(() => generateCSSOutput(borders, 1, 'solid', 'px')).toThrowError();
});
});
});

View file

@ -0,0 +1,15 @@
export interface Border {
label: string
value: number
max: number
}
export interface Borders {
[key: string]: Border
}
// Asegúrate de que esta función esté correctamente exportada
export function generateCSSOutput(borders: Borders, borderWidth: number, borderStyle: string, unit: string): string {
const { topLeft, topRight, bottomRight, bottomLeft } = borders;
return `border: ${borderWidth}px ${borderStyle} #000000; border-radius: ${topLeft.value}${unit} ${topRight.value}${unit} ${bottomRight.value}${unit} ${bottomLeft.value}${unit};`;
}

View file

@ -0,0 +1,214 @@
<script lang="ts">
import { computed, defineComponent, ref } from 'vue';
import { NCard, NColorPicker, NForm, NFormItem, NSelect, NSlider } from 'naive-ui';
import type { Borders } from './border-generator.service';
import TextareaCopyable from '@/components/TextareaCopyable.vue';
export default defineComponent({
name: 'BorderRadiusViewer',
components: {
NSlider,
NForm,
NFormItem,
NSelect,
NCard,
NColorPicker,
TextareaCopyable,
},
setup() {
const borders = ref<Borders>({
topLeft: { label: 'Top Left', value: 0, max: 100 },
topRight: { label: 'Top Right', value: 0, max: 100 },
bottomLeft: { label: 'Bottom Left', value: 0, max: 100 },
bottomRight: { label: 'Bottom Right', value: 0, max: 100 },
});
const unit = ref('px');
const borderWidth = ref<number>(0);
const borderStyle = ref('solid');
const borderColor = ref('#000000');
const unitOptions = [
{ label: 'Pixels (px)', value: 'px' },
{ label: 'Percentage (%)', value: '%' },
];
const borderStyles = [
{ label: 'Solid', value: 'solid' },
{ label: 'Dashed', value: 'dashed' },
{ label: 'Dotted', value: 'dotted' },
{ label: 'Double', value: 'double' },
{ label: 'Groove', value: 'groove' },
{ label: 'Ridge', value: 'ridge' },
{ label: 'Inset', value: 'inset' },
{ label: 'Outset', value: 'outset' },
{ label: 'None', value: 'none' },
];
const styleObject = computed(() => ({
border: `${borderWidth.value}px ${borderStyle.value} ${borderColor.value}`,
borderRadius: `${borders.value.topLeft.value}${unit.value} ${borders.value.topRight.value}${unit.value} ${borders.value.bottomRight.value}${unit.value} ${borders.value.bottomLeft.value}${unit.value}`,
}));
const cssOutput = computed(
() =>
`border: ${borderWidth.value}px ${borderStyle.value} ${borderColor.value};
border-radius: ${borders.value.topLeft.value}${unit.value} ${borders.value.topRight.value}${unit.value} ${borders.value.bottomRight.value}${unit.value} ${borders.value.bottomLeft.value}${unit.value};`,
);
function updateCSSOutput() {
// Forces update computed properties
}
return {
borders,
unit,
borderWidth,
borderStyle,
borderColor,
unitOptions,
borderStyles,
cssOutput,
updateCSSOutput,
styleObject,
};
},
});
</script>
<template>
<div class="container">
<div class="square" :style="styleObject" />
<NCard title="Border Radius and Style Editor" class="controls">
<NForm>
<NFormItem label="Units">
<NSelect v-model:value="unit" :options="unitOptions" @update:value="updateCSSOutput" />
</NFormItem>
<div class="radius-controls">
<NFormItem label="Top Left" class="half-slider">
<NSlider
v-model:value="borders.topLeft.value"
:min="0"
:max="borders.topLeft.max"
:step="1"
@update:value="updateCSSOutput"
/>
<span>{{ borders.topLeft.value + unit }}</span>
</NFormItem>
<NFormItem label="Top Right" class="half-slider">
<NSlider
v-model:value="borders.topRight.value"
:min="0"
:max="borders.topRight.max"
:step="1"
@update:value="updateCSSOutput"
/>
<span>{{ borders.topRight.value + unit }}</span>
</NFormItem>
</div>
<div class="radius-controls">
<NFormItem label="Bottom Left" class="half-slider">
<NSlider
v-model:value="borders.bottomLeft.value"
:min="0"
:max="borders.bottomLeft.max"
:step="1"
@update:value="updateCSSOutput"
/>
<span>{{ borders.bottomLeft.value + unit }}</span>
</NFormItem>
<NFormItem label="Bottom Right" class="half-slider">
<NSlider
v-model:value="borders.bottomRight.value"
:min="0"
:max="borders.bottomRight.max"
:step="1"
@update:value="updateCSSOutput"
/>
<span>{{ borders.bottomRight.value + unit }}</span>
</NFormItem>
</div>
<div class="border-controls">
<NFormItem label="Border Width" class="border-width-slider">
<NSlider v-model:value="borderWidth" :min="0" :max="100" :step="1" @update:value="updateCSSOutput" />
<span>{{ `${borderWidth}px` }}</span>
</NFormItem>
<NFormItem label="Border Style" class="border-style-select">
<NSelect v-model:value="borderStyle" :options="borderStyles" @update:value="updateCSSOutput" />
</NFormItem>
</div>
<NFormItem label="Border Color">
<NColorPicker v-model:value="borderColor" @update:value="updateCSSOutput" />
</NFormItem>
<NFormItem label="CSS Properties">
<TextareaCopyable :value="cssOutput" language="css" />
</NFormItem>
</NForm>
</NCard>
</div>
</template>
<style scoped>
.container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 10px;
}
.square {
width: 250px;
height: 250px;
background-color: #f3f3f3;
}
.controls {
width: 100%;
}
.radius-controls,
.border-controls {
display: flex;
justify-content: space-around;
align-items: center;
gap: 20px;
}
.half-slider,
.border-width-slider {
position: relative;
padding: 10px 0;
width: 60%;
}
.half-slider span,
.border-width-slider span {
position: absolute;
right: 0;
top: 30px;
background-color: #fff;
padding: 2px 6px;
border-radius: 5px;
font-size: 0.9em;
color: #333;
}
.border-style-select {
width: 30%;
}
n-slider {
width: calc(100% - 60px);
}
.controls .n-form-item__label {
min-width: 70px;
text-align: right;
}
n-select,
n-color-picker {
width: 100%;
}
</style>

View file

@ -0,0 +1,12 @@
import { Square } from '@vicons/tabler';
import { defineTool } from '../tool';
export const tool = defineTool({
name: 'Border Generator',
path: '/border-generator',
description: 'Generate a complete CSS border properties.',
keywords: ['border', 'generator', 'css'],
component: () => import('./border-generator.vue'),
icon: Square,
createdAt: new Date('2024-09-06'),
});

View file

@ -1,6 +1,7 @@
import { tool as base64FileConverter } from './base64-file-converter';
import { tool as base64StringConverter } from './base64-string-converter';
import { tool as basicAuthGenerator } from './basic-auth-generator';
import { tool as borderGenerator } from './border-generator';
import { tool as emailNormalizer } from './email-normalizer';
import { tool as asciiTextDrawer } from './ascii-text-drawer';
@ -91,7 +92,19 @@ import { tool as yamlViewer } from './yaml-viewer';
export const toolsByCategory: ToolCategory[] = [
{
name: 'Crypto',
components: [tokenGenerator, hashText, bcrypt, uuidGenerator, ulidGenerator, cypher, bip39, hmacGenerator, rsaKeyPairGenerator, passwordStrengthAnalyser, pdfSignatureChecker],
components: [
tokenGenerator,
hashText,
bcrypt,
uuidGenerator,
ulidGenerator,
cypher,
bip39,
hmacGenerator,
rsaKeyPairGenerator,
passwordStrengthAnalyser,
pdfSignatureChecker,
],
},
{
name: 'Converter',
@ -137,6 +150,7 @@ export const toolsByCategory: ToolCategory[] = [
httpStatusCodes,
jsonDiff,
safelinkDecoder,
borderGenerator,
],
},
{
@ -164,7 +178,14 @@ export const toolsByCategory: ToolCategory[] = [
},
{
name: 'Network',
components: [ipv4SubnetCalculator, ipv4AddressConverter, ipv4RangeExpander, macAddressLookup, macAddressGenerator, ipv6UlaGenerator],
components: [
ipv4SubnetCalculator,
ipv4AddressConverter,
ipv4RangeExpander,
macAddressLookup,
macAddressGenerator,
ipv6UlaGenerator,
],
},
{
name: 'Math',
@ -194,5 +215,5 @@ export const toolsByCategory: ToolCategory[] = [
export const tools = toolsByCategory.flatMap(({ components }) => components);
export const toolsWithCategory = toolsByCategory.flatMap(({ components, name }) =>
components.map(tool => ({ category: name, ...tool })),
components.map(tool => ({ ...tool, category: name })),
);