diff --git a/src/plugin/composables/__tests__/styles.test.ts b/src/plugin/composables/__tests__/styles.test.ts index 7e3f150..f618b34 100644 --- a/src/plugin/composables/__tests__/styles.test.ts +++ b/src/plugin/composables/__tests__/styles.test.ts @@ -95,19 +95,71 @@ describe('Styles Composable', () => { ).toMatchSnapshot(); }); - // TODO (fix): The border radius is not set correctly if using multiple units with tabs // - it('should return the set radius', () => { - expect( - usePreTagStyles({ copyTab, height, maxHeight, radius: '0 1em', runTab, tabs: true, useTheme: defaultTheme }) - ).toMatchInlineSnapshot(` - { - "borderRadius": "0 1em 0 0 1em 0 1em !important", - "display": "flex", - "height": "auto", - "maxHeight": "auto", - "overflow": "auto", - } - `); + describe('border radius', () => { + const getRadius = (value: string | undefined, withTabs: boolean) => usePreTagStyles({ + copyTab: true, + height, + maxHeight, + radius: value, + runTab: false, + tabs: withTabs, + useTheme: defaultTheme, + }).borderRadius; + + it.each([ + ['0.5rem', '0.5rem 0 0.5rem 0.5rem !important'], + ['0 1em', '0 0 0 1em !important'], + ['1px 2px 3px', '1px 0 3px 2px !important'], + ['8px 4px 2px 1px', '8px 0 2px 1px !important'], + ['10px 5px / 20px', '10px 0 10px 5px / 20px 0 20px 20px !important'], + ['8', '8px 0 8px 8px !important'], + [' 0 1em ', '0 0 0 1em !important'], + ])('should square only the top right corner with tabs (%s)', (value, expected) => { + expect(getRadius(value, true)).toBe(expected); + }); + + it.each([ + ['0.5rem', '0.5rem'], + ['0 1em', '0 1em'], + ['8px 4px 2px 1px', '8px 4px 2px 1px'], + ['10px 5px / 20px', '10px 5px / 20px'], + ['8', '8px'], + ])('should keep the radius without tabs (%s)', (value, expected) => { + expect(getRadius(value, false)).toBe(expected); + }); + + it('should keep the radius when tabs are on but there is no copy or run tab', () => { + expect(usePreTagStyles({ + copyTab: false, + height, + maxHeight, + radius: '0 1em', + runTab: false, + tabs: true, + useTheme: defaultTheme, + }).borderRadius).toBe('0 1em'); + }); + + it('should square the top right corner with only a run tab', () => { + expect(usePreTagStyles({ + copyTab: false, + height, + maxHeight, + radius: '0 1em', + runTab: true, + tabs: true, + useTheme: defaultTheme, + }).borderRadius).toBe('0 0 0 1em !important'); + }); + + it('should not set a radius when the value is empty', () => { + expect(getRadius('', true)).toBeUndefined(); + expect(getRadius(undefined, false)).toBeUndefined(); + }); + + it('should leave a value with more than four corners unchanged', () => { + expect(getRadius('1px 2px 3px 4px 5px', true)).toBe('1px 2px 3px 4px 5px !important'); + }); }); }); diff --git a/src/plugin/composables/styles.ts b/src/plugin/composables/styles.ts index e1dbb54..f88f74a 100644 --- a/src/plugin/composables/styles.ts +++ b/src/plugin/composables/styles.ts @@ -34,14 +34,44 @@ export const useHeaderStyles: UseHeaderStyles = (options) => { return styles as CSSProperties; }; -// TODO (fix): The border radius is not set correctly if using multiple units with tabs // +/** + * Formats a border-radius value, adding units to bare numbers. + * When squareTopRight is set (tabs sit on that corner), the value is expanded to its + * four corners (1-4 values, either side of an optional "/") and the top-right corner is set to 0. + */ +function formatBorderRadius(radius: string | undefined, squareTopRight: boolean): string | undefined { + const value = String(radius ?? '').trim(); + + if (!value) { + return undefined; + } + + const sides = value.split('/').map((side) => side.trim().split(/\s+/) + .map((part) => useConvertToUnit({ str: part }) as string)); + + // Not a valid shorthand, leave it as it is // + if (sides.length > 2 || sides.some((corners) => corners.length > 4)) { + return value; + } + + if (!squareTopRight) { + return sides.map((corners) => corners.join(' ')).join(' / '); + } + + return sides.map((corners) => { + const [topLeft, topRight = topLeft, bottomRight = topLeft, bottomLeft = topRight] = corners; + + return `${topLeft} 0 ${bottomRight} ${bottomLeft}`; + }).join(' / '); +} + export const usePreTagStyles: UsePreTagStyles = (options) => { const { copyTab, height, maxHeight, radius, runTab, tabs, useTheme } = options; - const unrefRadius = unref(radius); - let borderRadius = `${unrefRadius} 0 ${unrefRadius} ${unrefRadius} !important`; + const hasTab = !!unref(tabs) && (!!unref(copyTab) || !!unref(runTab)); + let borderRadius = formatBorderRadius(unref(radius), hasTab); - if (!unref(tabs) || (!unref(copyTab) && !unref(runTab))) { - borderRadius = unrefRadius as string; + if (hasTab && borderRadius) { + borderRadius = `${borderRadius} !important`; } const display = unref(useTheme) !== 'funky' ? 'flex' : 'block';