Browse Source

Improve alignment in code and canvas components

Use space character for delineating code and headers.
pull/1/head
TheoryOfNekomata 1 year ago
parent
commit
2dcda23a6d
8 changed files with 352 additions and 97 deletions
  1. +2
    -2
      packages/web-kitchensink-reactnext/src/categories/number/react/components/Slider/index.tsx
  2. +198
    -0
      packages/web-kitchensink-reactnext/src/categories/number/react/components/Spinner/index.tsx
  3. +11
    -23
      packages/web-kitchensink-reactnext/src/packages/react-binary-data-canvas/index.tsx
  4. +20
    -0
      packages/web-kitchensink-reactnext/src/packages/react-binary-data-canvas/style.module.css
  5. +37
    -23
      packages/web-kitchensink-reactnext/src/packages/react-refractor/index.tsx
  6. +36
    -0
      packages/web-kitchensink-reactnext/src/packages/react-refractor/style.module.css
  7. +13
    -0
      packages/web-kitchensink-reactnext/src/pages/examples/blog-post/index.tsx
  8. +35
    -49
      packages/web-kitchensink-reactnext/src/styles/globals.css

+ 2
- 2
packages/web-kitchensink-reactnext/src/categories/number/react/components/Slider/index.tsx View File

@@ -1,6 +1,6 @@
import * as React from 'react';
import clsx from 'clsx';
import {slider} from './style.module.css';
import styles from './style.module.css';
import * as SelectControlBase from '@tesseract-design/web-base-selectcontrol';

type SliderOrientation = 'horizontal' | 'vertical';
@@ -209,7 +209,7 @@ export const Slider = React.forwardRef<SliderDerivedElement, SliderProps>(({
type="range"
data-orient={orient}
className={clsx(
slider,
styles.slider,
'w-full h-full bg-inherit block text-primary ring-secondary/50 rounded-full',
'focus:text-secondary focus:outline-0 focus:ring-4',
'active:text-tertiary active:ring-tertiary/50',


+ 198
- 0
packages/web-kitchensink-reactnext/src/categories/number/react/components/Spinner/index.tsx View File

@@ -0,0 +1,198 @@
import * as React from 'react';
import * as TextControlBase from '@tesseract-design/web-base-textcontrol';
import clsx from 'clsx';

type MaskedTextInputDerivedElement = HTMLInputElement;

export interface MaskedTextInputProps extends Omit<React.HTMLProps<MaskedTextInputDerivedElement>, 'size' | 'type' | 'label'> {
/**
* Short textual description indicating the nature of the component's value.
*/
label?: React.ReactNode,
/**
* Short textual description as guidelines for valid input values.
*/
hint?: React.ReactNode,
/**
* Size of the component.
*/
size?: TextControlBase.TextControlSize,
/**
* Additional description, usually graphical, indicating the nature of the component's value.
*/
indicator?: React.ReactNode,
/**
* Should the component display a border?
*/
border?: boolean,
/**
* Should the component occupy the whole width of its parent?
*/
block?: boolean,
/**
* Style of the component.
*/
variant?: TextControlBase.TextControlVariant,
/**
* Is the label hidden?
*/
hiddenLabel?: boolean,
}

/**
* Component for inputting textual values.
*
* This component supports multiline input and adjusts its layout accordingly.
*/
export const MaskedTextInput = React.forwardRef<MaskedTextInputDerivedElement, MaskedTextInputProps>(
(
{
label,
hint,
indicator,
size = 'medium' as const,
border = false,
block = false,
variant = 'default' as const,
hiddenLabel = false,
className,
...etcProps
}: MaskedTextInputProps,
ref,
) => {
const labelId = React.useId();

return (
<div
className={clsx(
'relative rounded ring-secondary/50',
'focus-within:ring-4',
{
'block': block,
'inline-block align-middle': !block,
},
className,
)}
>
<input
{...etcProps}
ref={ref}
aria-labelledby={labelId}
type="password"
data-testid="input"
className={clsx(
'bg-negative rounded-inherit w-full peer block',
'focus:outline-0',
'disabled:opacity-50 disabled:cursor-not-allowed',
{
'text-xxs': size === 'small',
'text-xs': size === 'medium',
},
{
'pl-4': variant === 'default',
'pl-1.5': variant === 'alternate',
},
{
'pt-4': variant === 'alternate',
},
{
'pr-4': variant === 'default' && !indicator,
'pr-1.5': variant === 'alternate' && !indicator,
},
{
'pr-10': indicator && size === 'small',
'pr-12': indicator && size === 'medium',
'pr-16': indicator && size === 'large',
},
{
'h-10': size === 'small',
'h-12': size === 'medium',
'h-16': size === 'large',
},
)}
/>
{
label && (
<div
data-testid="label"
id={labelId}
className={clsx(
'absolute z-[1] w-full top-0.5 left-0 pointer-events-none pl-1 text-xxs font-bold peer-disabled:opacity-50 peer-focus:text-secondary text-primary leading-none bg-negative',
{
'sr-only': hiddenLabel,
},
{
'pr-1': !indicator,
},
{
'pr-10': indicator && size === 'small',
'pr-12': indicator && size === 'medium',
'pr-16': indicator && size === 'large',
},
)}
>
<div className="w-full whitespace-nowrap h-[1.1em] overflow-hidden text-ellipsis">
{label}
</div>
</div>
)
}
{hint && (
<div
data-testid="hint"
className={clsx(
'absolute left-0 px-1 pointer-events-none text-xxs peer-disabled:opacity-50 leading-none w-full bg-negative',
{
'bottom-0 pl-4 pb-1': variant === 'default',
'top-0.5': variant === 'alternate',
},
{
'pt-2': variant === 'alternate' && size === 'small',
'pt-3': variant === 'alternate' && size !== 'small',
},
{
'pr-4': !indicator && variant === 'default',
'pr-1': !indicator && variant === 'alternate',
},
{
'pr-10': indicator && size === 'small',
'pr-12': indicator && size === 'medium',
'pr-16': indicator && size === 'large',
},
)}
>
<div
className="opacity-50 whitespace-nowrap w-full h-[1.1em] overflow-hidden text-ellipsis"
>
{hint}
</div>
</div>
)}
{indicator && (
<div
className={clsx(
'text-center flex items-center justify-center peer-disabled:opacity-50 aspect-square absolute bottom-0 right-0 pointer-events-none',
{
'w-10': size === 'small',
'w-12': size === 'medium',
'w-16': size === 'large',
},
)}
>
{indicator}
</div>
)}
{
border && (
<span
data-testid="border"
className="absolute z-[1] peer-disabled:opacity-50 inset-0 rounded-inherit border-2 border-primary pointer-events-none peer-focus:border-secondary"
/>
)
}
</div>
);
},
);

MaskedTextInput.displayName = 'MaskedTextInput';

+ 11
- 23
packages/web-kitchensink-reactnext/src/packages/react-binary-data-canvas/index.tsx View File

@@ -1,7 +1,7 @@
import * as React from 'react';
import clsx from 'clsx';
import styles from './style.module.css';

type BinaryDataCanvasDerivedElement = HTMLPreElement;
type BinaryDataCanvasDerivedElement = HTMLDivElement;

export interface BinaryDataCanvasProps extends Omit<React.HTMLProps<BinaryDataCanvasDerivedElement>, 'children' | 'headers'> {
arrayBuffer?: ArrayBuffer;
@@ -43,22 +43,14 @@ export const BinaryDataCanvas = React.forwardRef<BinaryDataCanvasDerivedElement,

return (
<div
className={clsx(
'react-binary-data-canvas',
className,
)}
style={{
...(style ?? {}),
display: 'flex',
gap: '0.5rem',
alignItems: 'flex-start',
}}
{...etcProps}
className={`${styles['react-binary-data-canvas']} ${className ?? ''}`.trim()}
style={style}
ref={forwardedRef}
>
{headers && (
<pre
style={{
textAlign: 'right',
}}
className={styles['line-numbers']}
>
<code>
{'\u00a0'}
@@ -67,21 +59,17 @@ export const BinaryDataCanvas = React.forwardRef<BinaryDataCanvasDerivedElement,
<React.Fragment key={i}>
{'\n'}
<code
style={{
whiteSpace: 'nowrap',
}}
className={styles['code']}
>
0x{(BYTES_PER_LINE * i).toString(16).padStart(2, '0')}
</code>
{' '}
</React.Fragment>
))}
</pre>
)}
<div>
<pre
{...etcProps}
ref={forwardedRef}
>
<div className="highlight">
<pre>
<code>
{headers && new Array(BYTES_PER_LINE).fill(0).map((_, i) => (
<span


+ 20
- 0
packages/web-kitchensink-reactnext/src/packages/react-binary-data-canvas/style.module.css View File

@@ -0,0 +1,20 @@
.react-binary-data-canvas {
display: flex;
align-items: flex-start;
}

.react-binary-data-canvas pre {
overflow: visible;
padding: 0;
margin: 0;
line-height: 1.2;
}

.line-numbers {
text-align: right;
user-select: none;
}

.code {
white-space: nowrap;
}

+ 37
- 23
packages/web-kitchensink-reactnext/src/packages/react-refractor/index.tsx View File

@@ -1,6 +1,8 @@
import * as React from 'react';
import RefractorCore from 'react-refractor/all';
import clsx from 'clsx';
import styles from './style.module.css';
import {getFormValues} from '@theoryofnekomata/formxtra';
import {Marker} from 'react-refractor';

type PrismDerivedElement = HTMLDivElement;

@@ -11,10 +13,12 @@ export interface PrismProps extends Omit<React.HTMLProps<PrismDerivedElement>, '
tabSize?: number;
header?: boolean;
scroll?: boolean;
actions?: React.ReactNode;
markers?: Marker[];
}

export const Refractor = React.forwardRef<PrismDerivedElement, PrismProps>(({
code = '',
code: codeRaw = '',
language = 'plain',
lineNumbers = false,
tabSize = 2,
@@ -22,58 +26,68 @@ export const Refractor = React.forwardRef<PrismDerivedElement, PrismProps>(({
style,
header = false,
scroll = false,
actions,
markers = [],
...etcProps
}, forwardedRef) => {
const handleAction: React.FormEventHandler<HTMLFormElement> = React.useCallback((e) => {
e.preventDefault();
const { currentTarget, nativeEvent } = e;
const { submitter } = nativeEvent as unknown as { submitter: HTMLButtonElement };
const { action } = getFormValues(currentTarget, { submitter });
switch (action) {
case 'copyContents':
console.log('copy contents');
break;
default:
break;
}
}, []);

const code = codeRaw.trim();

return (
<div
className={clsx(
'react-refractor',
className,
)}
{...etcProps}
className={`${styles['react-refractor']} ${className ?? ''}`.trim()}
style={style}
ref={forwardedRef}
>
{header && (
<div className="flex items-center justify-between">
<div className="uppercase text-sm">
<div className={styles['header']}>
<div className={styles['language']}>
{language}
</div>
<form>
<button
type="submit"
name="action"
value="copyContents"
className="h-12 uppercase text-primary font-semi-expanded font-bold"
>
Copy
</button>
<form
onSubmit={handleAction}
>
{actions}
</form>
</div>
)}
<div
className="flex gap-2 items-start"
className={styles['content-wrapper']}
>
{lineNumbers && (
<pre
style={{
textAlign: 'right',
}}
className={styles['line-numbers']}
>
<code>
{code.split('\n').map((_, i) => (
<React.Fragment key={i}>
{i > 0 && '\n'}
{i + 1}
{' '}
</React.Fragment>
))}
</code>
</pre>
)}
<div
className={clsx('flex-auto', scroll && 'overflow-auto')}
className={`${styles['code-wrapper']} ${scroll ? styles['code-wrapper-scroll'] : ''}`.trim()}
>
<RefractorCore
{...etcProps}
markers={markers}
language={language}
value={code}
className="highlight"


+ 36
- 0
packages/web-kitchensink-reactnext/src/packages/react-refractor/style.module.css View File

@@ -0,0 +1,36 @@
.react-refractor pre {
overflow: visible;
padding: 0;
margin: 0;
line-height: 1.2;
}

.line-numbers {
text-align: right;
user-select: none;
}

.header {
display: flex;
align-items: center;
justify-content: space-between;
}

.language {
text-transform: uppercase;
font-size: 0.875em;
}

.content-wrapper {
display: flex;
align-items: flex-start;
}

.code-wrapper {
flex: auto;
}

.code-wrapper-scroll {
overflow-x: auto;
overflow-y: hidden;
}

+ 13
- 0
packages/web-kitchensink-reactnext/src/pages/examples/blog-post/index.tsx View File

@@ -1,5 +1,6 @@
import {Refractor} from '@modal-soft/react-refractor';
import {NextPage} from 'next';
import * as React from 'react';

const BlogPostPage: NextPage = () => {
return (
@@ -19,6 +20,18 @@ const BlogPostPage: NextPage = () => {
header
language="tsx"
scroll
actions={
<>
<button
type="submit"
name="action"
value="copyContents"
className="h-12 uppercase text-primary font-semi-expanded font-bold"
>
Copy
</button>
</>
}
code={`import {Refractor} from '@modal-soft/react-refractor';
import {NextPage} from 'next';



+ 35
- 49
packages/web-kitchensink-reactnext/src/styles/globals.css View File

@@ -96,55 +96,41 @@
}
}

.react-refractor pre {
overflow: visible;
padding: 0;
margin: 0;
line-height: 1.2;
}

.react-refractor .token.number { color: rgb(var(--color-code-number)); }
.react-refractor .token.keyword { color: rgb(var(--color-code-keyword)); }
.react-refractor .token.tag { color: rgb(var(--color-code-keyword)); }
.react-refractor .token.type { color: rgb(var(--color-code-type)); }
.react-refractor .token.instance-attribute { color: rgb(var(--color-code-instance-attribute)); }
.react-refractor .token.maybe-class-name { color: rgb(var(--color-code-function)); font-style: italic; }
.react-refractor .token.function { color: rgb(var(--color-code-function)); font-style: italic; }
.react-refractor .token.parameter { color: rgb(var(--color-code-parameter)); }
.react-refractor .token.property { color: rgb(var(--color-code-property)); }
.react-refractor .token.attr-name { color: rgb(var(--color-code-property)); font-style: italic; }
.react-refractor .token.string { color: rgb(var(--color-code-string)); }
.react-refractor .token.attr-value { color: rgb(var(--color-code-string)); }
.react-refractor .token.attr-value .attr-equals { color: rgb(var(--color-positive)); }
.react-refractor .token.variable { color: rgb(var(--color-code-variable)); }
.react-refractor .token.regexp { color: rgb(var(--color-code-regexp)); }
.react-refractor .token.url { color: rgb(var(--color-code-url)); }
.react-refractor .token.global { color: rgb(var(--color-code-global)); }
.react-refractor .token.comment { opacity: 0.5; }

.react-binary-data-canvas pre {
overflow: visible;
padding: 0;
margin: 0;
line-height: 1.2;
}

.react-binary-data-canvas .x00 { color: rgb(var(--color-code-keyword)); }
.react-binary-data-canvas .x10 { color: rgb(var(--color-code-global)); }
.react-binary-data-canvas .x20 { color: rgb(var(--color-code-string)); }
.react-binary-data-canvas .x30 { color: rgb(var(--color-code-number)); }
.react-binary-data-canvas .x40 { color: rgb(var(--color-code-url)); }
.react-binary-data-canvas .x50 { color: rgb(var(--color-code-type)); }
.react-binary-data-canvas .x60 { color: rgb(var(--color-code-parameter)); }
.react-binary-data-canvas .x70 { color: rgb(var(--color-code-property)); }
.react-binary-data-canvas .x80 { color: rgb(var(--color-negative)); background-color: rgb(var(--color-code-keyword)); }
.react-binary-data-canvas .x90 { color: rgb(var(--color-negative)); background-color: rgb(var(--color-code-global)); }
.react-binary-data-canvas .xa0 { color: rgb(var(--color-negative)); background-color: rgb(var(--color-code-string)); }
.react-binary-data-canvas .xb0 { color: rgb(var(--color-negative)); background-color: rgb(var(--color-code-number)); }
.react-binary-data-canvas .xc0 { color: rgb(var(--color-negative)); background-color: rgb(var(--color-code-url)); }
.react-binary-data-canvas .xd0 { color: rgb(var(--color-negative)); background-color: rgb(var(--color-code-type)); }
.react-binary-data-canvas .xe0 { color: rgb(var(--color-negative)); background-color: rgb(var(--color-code-parameter)); }
.react-binary-data-canvas .xf0 { color: rgb(var(--color-negative)); background-color: rgb(var(--color-code-property)); }
.highlight .token.number { color: rgb(var(--color-code-number)); }
.highlight .token.keyword { color: rgb(var(--color-code-keyword)); }
.highlight .token.tag { color: rgb(var(--color-code-keyword)); }
.highlight .token.type { color: rgb(var(--color-code-type)); }
.highlight .token.instance-attribute { color: rgb(var(--color-code-instance-attribute)); }
.highlight .token.maybe-class-name { color: rgb(var(--color-code-function)); font-style: italic; }
.highlight .token.function { color: rgb(var(--color-code-function)); font-style: italic; }
.highlight .token.parameter { color: rgb(var(--color-code-parameter)); }
.highlight .token.property { color: rgb(var(--color-code-property)); }
.highlight .token.attr-name { color: rgb(var(--color-code-property)); font-style: italic; }
.highlight .token.string { color: rgb(var(--color-code-string)); }
.highlight .token.attr-value { color: rgb(var(--color-code-string)); }
.highlight .token.attr-value .attr-equals { color: rgb(var(--color-positive)); }
.highlight .token.variable { color: rgb(var(--color-code-variable)); }
.highlight .token.regexp { color: rgb(var(--color-code-regexp)); }
.highlight .token.url { color: rgb(var(--color-code-url)); }
.highlight .token.global { color: rgb(var(--color-code-global)); }
.highlight .token.comment { opacity: 0.5; }

.highlight .x00 { color: rgb(var(--color-code-keyword)); }
.highlight .x10 { color: rgb(var(--color-code-global)); }
.highlight .x20 { color: rgb(var(--color-code-string)); }
.highlight .x30 { color: rgb(var(--color-code-number)); }
.highlight .x40 { color: rgb(var(--color-code-url)); }
.highlight .x50 { color: rgb(var(--color-code-type)); }
.highlight .x60 { color: rgb(var(--color-code-parameter)); }
.highlight .x70 { color: rgb(var(--color-code-property)); }
.highlight .x80 { color: rgb(var(--color-negative)); background-color: rgb(var(--color-code-keyword)); }
.highlight .x90 { color: rgb(var(--color-negative)); background-color: rgb(var(--color-code-global)); }
.highlight .xa0 { color: rgb(var(--color-negative)); background-color: rgb(var(--color-code-string)); }
.highlight .xb0 { color: rgb(var(--color-negative)); background-color: rgb(var(--color-code-number)); }
.highlight .xc0 { color: rgb(var(--color-negative)); background-color: rgb(var(--color-code-url)); }
.highlight .xd0 { color: rgb(var(--color-negative)); background-color: rgb(var(--color-code-type)); }
.highlight .xe0 { color: rgb(var(--color-negative)); background-color: rgb(var(--color-code-parameter)); }
.highlight .xf0 { color: rgb(var(--color-negative)); background-color: rgb(var(--color-code-property)); }

.focus\:outline-0:-moz-focusring {
outline: 0;


Loading…
Cancel
Save