Comparing version 4.1.0 to 4.2.0
@@ -5,155 +5,9 @@ import React from 'react'; | ||
import { type DOMElement } from '../dom.js'; | ||
export type Props = Except<Styles, 'textWrap'> & { | ||
/** | ||
* Size of the gap between an element's columns. | ||
* | ||
* @default 0 | ||
*/ | ||
readonly columnGap?: number; | ||
/** | ||
* Size of the gap between element's rows. | ||
* | ||
* @default 0 | ||
*/ | ||
readonly rowGap?: number; | ||
/** | ||
* Size of the gap between an element's columns and rows. Shorthand for `columnGap` and `rowGap`. | ||
* | ||
* @default 0 | ||
*/ | ||
readonly gap?: number; | ||
/** | ||
* Margin on all sides. Equivalent to setting `marginTop`, `marginBottom`, `marginLeft` and `marginRight`. | ||
* | ||
* @default 0 | ||
*/ | ||
readonly margin?: number; | ||
/** | ||
* Horizontal margin. Equivalent to setting `marginLeft` and `marginRight`. | ||
* | ||
* @default 0 | ||
*/ | ||
readonly marginX?: number; | ||
/** | ||
* Vertical margin. Equivalent to setting `marginTop` and `marginBottom`. | ||
* | ||
* @default 0 | ||
*/ | ||
readonly marginY?: number; | ||
/** | ||
* Padding on all sides. Equivalent to setting `paddingTop`, `paddingBottom`, `paddingLeft` and `paddingRight`. | ||
* | ||
* @default 0 | ||
*/ | ||
readonly padding?: number; | ||
/** | ||
* Horizontal padding. Equivalent to setting `paddingLeft` and `paddingRight`. | ||
* | ||
* @default 0 | ||
*/ | ||
readonly paddingX?: number; | ||
/** | ||
* Vertical padding. Equivalent to setting `paddingTop` and `paddingBottom`. | ||
* | ||
* @default 0 | ||
*/ | ||
readonly paddingY?: number; | ||
/** | ||
* Behavior for an element's overflow in both directions. | ||
* | ||
* @default 'visible' | ||
*/ | ||
readonly overflow?: 'visible' | 'hidden'; | ||
/** | ||
* Behavior for an element's overflow in horizontal direction. | ||
* | ||
* @default 'visible' | ||
*/ | ||
readonly overflowX?: 'visible' | 'hidden'; | ||
/** | ||
* Behavior for an element's overflow in vertical direction. | ||
* | ||
* @default 'visible' | ||
*/ | ||
readonly overflowY?: 'visible' | 'hidden'; | ||
}; | ||
export type Props = Except<Styles, 'textWrap'>; | ||
/** | ||
* `<Box>` is an essential Ink component to build your layout. It's like `<div style="display: flex">` in the browser. | ||
*/ | ||
declare const Box: React.ForwardRefExoticComponent<Except<Styles, "textWrap"> & { | ||
/** | ||
* Size of the gap between an element's columns. | ||
* | ||
* @default 0 | ||
*/ | ||
readonly columnGap?: number | undefined; | ||
/** | ||
* Size of the gap between element's rows. | ||
* | ||
* @default 0 | ||
*/ | ||
readonly rowGap?: number | undefined; | ||
/** | ||
* Size of the gap between an element's columns and rows. Shorthand for `columnGap` and `rowGap`. | ||
* | ||
* @default 0 | ||
*/ | ||
readonly gap?: number | undefined; | ||
/** | ||
* Margin on all sides. Equivalent to setting `marginTop`, `marginBottom`, `marginLeft` and `marginRight`. | ||
* | ||
* @default 0 | ||
*/ | ||
readonly margin?: number | undefined; | ||
/** | ||
* Horizontal margin. Equivalent to setting `marginLeft` and `marginRight`. | ||
* | ||
* @default 0 | ||
*/ | ||
readonly marginX?: number | undefined; | ||
/** | ||
* Vertical margin. Equivalent to setting `marginTop` and `marginBottom`. | ||
* | ||
* @default 0 | ||
*/ | ||
readonly marginY?: number | undefined; | ||
/** | ||
* Padding on all sides. Equivalent to setting `paddingTop`, `paddingBottom`, `paddingLeft` and `paddingRight`. | ||
* | ||
* @default 0 | ||
*/ | ||
readonly padding?: number | undefined; | ||
/** | ||
* Horizontal padding. Equivalent to setting `paddingLeft` and `paddingRight`. | ||
* | ||
* @default 0 | ||
*/ | ||
readonly paddingX?: number | undefined; | ||
/** | ||
* Vertical padding. Equivalent to setting `paddingTop` and `paddingBottom`. | ||
* | ||
* @default 0 | ||
*/ | ||
readonly paddingY?: number | undefined; | ||
/** | ||
* Behavior for an element's overflow in both directions. | ||
* | ||
* @default 'visible' | ||
*/ | ||
readonly overflow?: "visible" | "hidden" | undefined; | ||
/** | ||
* Behavior for an element's overflow in horizontal direction. | ||
* | ||
* @default 'visible' | ||
*/ | ||
readonly overflowX?: "visible" | "hidden" | undefined; | ||
/** | ||
* Behavior for an element's overflow in vertical direction. | ||
* | ||
* @default 'visible' | ||
*/ | ||
readonly overflowY?: "visible" | "hidden" | undefined; | ||
} & { | ||
declare const Box: React.ForwardRefExoticComponent<Props & { | ||
children?: React.ReactNode; | ||
} & React.RefAttributes<DOMElement>>; | ||
export default Box; |
@@ -1,2 +0,1 @@ | ||
/* eslint-disable @typescript-eslint/prefer-nullish-coalescing */ | ||
import React, { forwardRef } from 'react'; | ||
@@ -7,18 +6,7 @@ /** | ||
const Box = forwardRef(({ children, ...style }, ref) => { | ||
const transformedStyle = { | ||
...style, | ||
columnGap: style.columnGap || style.gap || 0, | ||
rowGap: style.rowGap || style.gap || 0, | ||
marginLeft: style.marginLeft || style.marginX || style.margin || 0, | ||
marginRight: style.marginRight || style.marginX || style.margin || 0, | ||
marginTop: style.marginTop || style.marginY || style.margin || 0, | ||
marginBottom: style.marginBottom || style.marginY || style.margin || 0, | ||
paddingLeft: style.paddingLeft || style.paddingX || style.padding || 0, | ||
paddingRight: style.paddingRight || style.paddingX || style.padding || 0, | ||
paddingTop: style.paddingTop || style.paddingY || style.padding || 0, | ||
paddingBottom: style.paddingBottom || style.paddingY || style.padding || 0, | ||
overflowX: style.overflowX || style.overflow || 'visible', | ||
overflowY: style.overflowY || style.overflow || 'visible' | ||
}; | ||
return (React.createElement("ink-box", { ref: ref, style: transformedStyle }, children)); | ||
return (React.createElement("ink-box", { ref: ref, style: { | ||
...style, | ||
overflowX: style.overflowX ?? style.overflow ?? 'visible', | ||
overflowY: style.overflowY ?? style.overflow ?? 'visible' | ||
} }, children)); | ||
}); | ||
@@ -25,0 +13,0 @@ Box.displayName = 'Box'; |
// eslint-disable-next-line n/file-extension-in-import | ||
import Yoga from 'yoga-wasm-web/auto'; | ||
import measureText from './measure-text.js'; | ||
import applyStyles from './styles.js'; | ||
import wrapText from './wrap-text.js'; | ||
@@ -73,5 +72,2 @@ import squashTextNodes from './squash-text-nodes.js'; | ||
node.style = style; | ||
if (node.yogaNode) { | ||
applyStyles(node.yogaNode, style); | ||
} | ||
}; | ||
@@ -78,0 +74,0 @@ export const createTextNode = (text) => { |
import { useEffect } from 'react'; | ||
import { isUpperCase } from 'is-upper-case'; | ||
import parseKeypress, { nonAlphanumericKeys } from '../parse-keypress.js'; | ||
import useStdin from './use-stdin.js'; | ||
@@ -44,43 +46,37 @@ /** | ||
const handleData = (data) => { | ||
let input = String(data); | ||
const keypress = parseKeypress(data); | ||
const key = { | ||
upArrow: input === '\u001B[A', | ||
downArrow: input === '\u001B[B', | ||
leftArrow: input === '\u001B[D', | ||
rightArrow: input === '\u001B[C', | ||
pageDown: input === '\u001B[6~', | ||
pageUp: input === '\u001B[5~', | ||
return: input === '\r', | ||
escape: input === '\u001B', | ||
ctrl: false, | ||
shift: false, | ||
tab: input === '\t' || input === '\u001B[Z', | ||
backspace: input === '\u0008', | ||
delete: input === '\u007F' || input === '\u001B[3~', | ||
meta: false | ||
upArrow: keypress.name === 'up', | ||
downArrow: keypress.name === 'down', | ||
leftArrow: keypress.name === 'left', | ||
rightArrow: keypress.name === 'right', | ||
pageDown: keypress.name === 'pagedown', | ||
pageUp: keypress.name === 'pageup', | ||
return: keypress.name === 'return', | ||
escape: keypress.name === 'escape', | ||
ctrl: keypress.ctrl, | ||
shift: keypress.shift, | ||
tab: keypress.name === 'tab', | ||
backspace: keypress.name === 'backspace', | ||
delete: keypress.name === 'delete', | ||
// `parseKeypress` parses \u001B\u001B[A (meta + up arrow) as meta = false | ||
// but with option = true, so we need to take this into account here | ||
// to avoid breaking changes in Ink. | ||
// TODO(vadimdemedes): consider removing this in the next major version. | ||
meta: keypress.meta || keypress.name === 'escape' || keypress.option | ||
}; | ||
// Copied from `keypress` module | ||
if (input <= '\u001A' && !key.return) { | ||
// eslint-disable-next-line unicorn/prefer-code-point | ||
input = String.fromCharCode( | ||
// eslint-disable-next-line unicorn/prefer-code-point | ||
input.charCodeAt(0) + 'a'.charCodeAt(0) - 1); | ||
key.ctrl = true; | ||
let input = keypress.ctrl ? keypress.name : keypress.sequence; | ||
if (nonAlphanumericKeys.includes(keypress.name)) { | ||
input = ''; | ||
} | ||
// Strip meta if it's still remaining after `parseKeypress` | ||
// TODO(vadimdemedes): remove this in the next major version. | ||
if (input.startsWith('\u001B')) { | ||
input = input.slice(1); | ||
key.meta = true; | ||
} | ||
const isLatinUppercase = input >= 'A' && input <= 'Z'; | ||
const isCyrillicUppercase = input >= 'А' && input <= 'Я'; | ||
if (input.length === 1 && (isLatinUppercase || isCyrillicUppercase)) { | ||
if (input.length === 1 && | ||
typeof input[0] === 'string' && | ||
isUpperCase(input[0])) { | ||
key.shift = true; | ||
} | ||
// Shift+Tab | ||
if (key.tab && input === '[Z') { | ||
key.shift = true; | ||
} | ||
if (key.tab || key.backspace || key.delete) { | ||
input = ''; | ||
} | ||
// If app is not supposed to exit on Ctrl+C, then let input listener handle it | ||
@@ -87,0 +83,0 @@ if (!(input === 'c' && key.ctrl) || !internal_exitOnCtrlC) { |
@@ -7,2 +7,3 @@ import process from 'node:process'; | ||
import { createTextNode, appendChildNode, insertBeforeNode, removeChildNode, setStyle, setTextNodeValue, createNode, setAttribute } from './dom.js'; | ||
import applyStyles from './styles.js'; | ||
// We need to conditionally perform devtools connection to avoid | ||
@@ -30,2 +31,28 @@ // accidentally breaking other third-party code. | ||
} | ||
const diff = (before, after) => { | ||
if (before === after) { | ||
return; | ||
} | ||
if (!before) { | ||
return after; | ||
} | ||
const changed = {}; | ||
let isChanged = false; | ||
for (const key of Object.keys(before)) { | ||
const isDeleted = after ? !Object.hasOwnProperty.call(after, key) : true; | ||
if (isDeleted) { | ||
changed[key] = undefined; | ||
isChanged = true; | ||
} | ||
} | ||
if (after) { | ||
for (const key of Object.keys(after)) { | ||
if (after[key] !== before[key]) { | ||
changed[key] = after[key]; | ||
isChanged = true; | ||
} | ||
} | ||
} | ||
return isChanged ? changed : undefined; | ||
}; | ||
const cleanupYogaNode = (node) => { | ||
@@ -83,2 +110,5 @@ node?.unsetMeasureFunc(); | ||
setStyle(node, value); | ||
if (node.yogaNode) { | ||
applyStyles(node.yogaNode, value); | ||
} | ||
continue; | ||
@@ -154,65 +184,29 @@ } | ||
} | ||
const updatePayload = {}; | ||
const keys = Object.keys(newProps); | ||
for (const key of keys) { | ||
if (newProps[key] !== oldProps[key]) { | ||
const isStyle = key === 'style' && | ||
typeof newProps['style'] === 'object' && | ||
typeof oldProps['style'] === 'object'; | ||
if (isStyle) { | ||
const newStyle = newProps['style']; | ||
const oldStyle = oldProps['style']; | ||
const styleKeys = Object.keys(newStyle); | ||
for (const styleKey of styleKeys) { | ||
// Always include `borderColor` and `borderStyle` to ensure border is rendered, | ||
// and `overflowX` and `overflowY` to ensure content is clipped, | ||
// otherwise resulting `updatePayload` may not contain them | ||
// if they weren't changed during this update | ||
if (styleKey === 'borderStyle' || styleKey === 'borderColor') { | ||
if (typeof updatePayload['style'] !== 'object') { | ||
// Linter didn't like `= {} as Style` | ||
const style = {}; | ||
updatePayload['style'] = style; | ||
} | ||
updatePayload['style'].borderStyle = | ||
newStyle.borderStyle; | ||
updatePayload['style'].borderColor = | ||
newStyle.borderColor; | ||
updatePayload['style'].overflowX = newStyle.overflowX; | ||
updatePayload['style'].overflowY = newStyle.overflowY; | ||
} | ||
if (newStyle[styleKey] !== oldStyle[styleKey]) { | ||
if (typeof updatePayload['style'] !== 'object') { | ||
// Linter didn't like `= {} as Style` | ||
const style = {}; | ||
updatePayload['style'] = style; | ||
} | ||
updatePayload['style'][styleKey] = newStyle[styleKey]; | ||
} | ||
} | ||
const props = diff(oldProps, newProps); | ||
const style = diff(oldProps['style'], newProps['style']); | ||
if (!props && !style) { | ||
return null; | ||
} | ||
return { props, style }; | ||
}, | ||
commitUpdate(node, { props, style }) { | ||
if (props) { | ||
for (const [key, value] of Object.entries(props)) { | ||
if (key === 'style') { | ||
setStyle(node, value); | ||
continue; | ||
} | ||
updatePayload[key] = newProps[key]; | ||
if (key === 'internal_transform') { | ||
node.internal_transform = value; | ||
continue; | ||
} | ||
if (key === 'internal_static') { | ||
node.internal_static = true; | ||
continue; | ||
} | ||
setAttribute(node, key, value); | ||
} | ||
} | ||
return updatePayload; | ||
}, | ||
commitUpdate(node, updatePayload) { | ||
for (const [key, value] of Object.entries(updatePayload)) { | ||
if (key === 'children') { | ||
continue; | ||
} | ||
if (key === 'style') { | ||
setStyle(node, value); | ||
continue; | ||
} | ||
if (key === 'internal_transform') { | ||
node.internal_transform = value; | ||
continue; | ||
} | ||
if (key === 'internal_static') { | ||
node.internal_static = true; | ||
continue; | ||
} | ||
setAttribute(node, key, value); | ||
if (style && node.yogaNode) { | ||
applyStyles(node.yogaNode, style); | ||
} | ||
@@ -219,0 +213,0 @@ }, |
import cliBoxes from 'cli-boxes'; | ||
import colorize from './colorize.js'; | ||
const renderBorder = (x, y, node, output) => { | ||
if (typeof node.style.borderStyle === 'string') { | ||
if (node.style.borderStyle) { | ||
const width = node.yogaNode.getComputedWidth(); | ||
const height = node.yogaNode.getComputedHeight(); | ||
const color = node.style.borderColor; | ||
const box = cliBoxes[node.style.borderStyle]; | ||
const topBorder = colorize(box.topLeft + box.top.repeat(width - 2) + box.topRight, color, 'foreground'); | ||
const verticalBorder = (colorize(box.left, color, 'foreground') + '\n').repeat(height - 2); | ||
const bottomBorder = colorize(box.bottomLeft + box.bottom.repeat(width - 2) + box.bottomRight, color, 'foreground'); | ||
output.write(x, y, topBorder, { transformers: [] }); | ||
output.write(x, y + 1, verticalBorder, { transformers: [] }); | ||
output.write(x + width - 1, y + 1, verticalBorder, { transformers: [] }); | ||
output.write(x, y + height - 1, bottomBorder, { transformers: [] }); | ||
const box = typeof node.style.borderStyle === 'string' | ||
? cliBoxes[node.style.borderStyle] | ||
: node.style.borderStyle; | ||
const topBorderColor = node.style.borderTopColor ?? node.style.borderColor; | ||
const bottomBorderColor = node.style.borderBottomColor ?? node.style.borderColor; | ||
const leftBorderColor = node.style.borderLeftColor ?? node.style.borderColor; | ||
const rightBorderColor = node.style.borderRightColor ?? node.style.borderColor; | ||
const showTopBorder = node.style.borderTop !== false; | ||
const showBottomBorder = node.style.borderBottom !== false; | ||
const showLeftBorder = node.style.borderLeft !== false; | ||
const showRightBorder = node.style.borderRight !== false; | ||
const contentWidth = width - (showLeftBorder ? 1 : 0) - (showRightBorder ? 1 : 0); | ||
const topBorder = showTopBorder | ||
? colorize((showLeftBorder ? box.topLeft : '') + | ||
box.top.repeat(contentWidth) + | ||
(showRightBorder ? box.topRight : ''), topBorderColor, 'foreground') | ||
: undefined; | ||
let verticalBorderHeight = height; | ||
if (showTopBorder) { | ||
verticalBorderHeight -= 1; | ||
} | ||
if (showBottomBorder) { | ||
verticalBorderHeight -= 1; | ||
} | ||
const leftBorder = (colorize(box.left, leftBorderColor, 'foreground') + '\n').repeat(verticalBorderHeight); | ||
const rightBorder = (colorize(box.right, rightBorderColor, 'foreground') + '\n').repeat(verticalBorderHeight); | ||
const bottomBorder = showBottomBorder | ||
? colorize((showLeftBorder ? box.bottomLeft : '') + | ||
box.bottom.repeat(contentWidth) + | ||
(showRightBorder ? box.bottomRight : ''), bottomBorderColor, 'foreground') | ||
: undefined; | ||
const offsetY = showTopBorder ? 1 : 0; | ||
if (topBorder) { | ||
output.write(x, y, topBorder, { transformers: [] }); | ||
} | ||
if (showLeftBorder) { | ||
output.write(x, y + offsetY, leftBorder, { transformers: [] }); | ||
} | ||
if (showRightBorder) { | ||
output.write(x + width - 1, y + offsetY, rightBorder, { | ||
transformers: [] | ||
}); | ||
} | ||
if (bottomBorder) { | ||
output.write(x, y + height - 1, bottomBorder, { transformers: [] }); | ||
} | ||
} | ||
@@ -17,0 +54,0 @@ }; |
@@ -61,4 +61,4 @@ import widestLine from 'widest-line'; | ||
renderBorder(x, y, node, output); | ||
const clipHorizontally = node.style.overflowX === 'hidden'; | ||
const clipVertically = node.style.overflowY === 'hidden'; | ||
const clipHorizontally = node.style.overflowX === 'hidden' || node.style.overflow === 'hidden'; | ||
const clipVertically = node.style.overflowY === 'hidden' || node.style.overflow === 'hidden'; | ||
if (clipHorizontally || clipVertically) { | ||
@@ -65,0 +65,0 @@ const x1 = clipHorizontally |
@@ -1,2 +0,2 @@ | ||
import { type Boxes } from 'cli-boxes'; | ||
import { type Boxes, type BoxStyle } from 'cli-boxes'; | ||
import { type LiteralUnion } from 'type-fest'; | ||
@@ -9,10 +9,26 @@ import { type ForegroundColorName } from 'chalk'; | ||
/** | ||
* Gap between element's columns. | ||
* Size of the gap between an element's columns. | ||
*/ | ||
readonly columnGap?: number; | ||
/** | ||
* Gap between element's rows. | ||
* Size of the gap between element's rows. | ||
*/ | ||
readonly rowGap?: number; | ||
/** | ||
* Size of the gap between an element's columns and rows. Shorthand for `columnGap` and `rowGap`. | ||
*/ | ||
readonly gap?: number; | ||
/** | ||
* Margin on all sides. Equivalent to setting `marginTop`, `marginBottom`, `marginLeft` and `marginRight`. | ||
*/ | ||
readonly margin?: number; | ||
/** | ||
* Horizontal margin. Equivalent to setting `marginLeft` and `marginRight`. | ||
*/ | ||
readonly marginX?: number; | ||
/** | ||
* Vertical margin. Equivalent to setting `marginTop` and `marginBottom`. | ||
*/ | ||
readonly marginY?: number; | ||
/** | ||
* Top margin. | ||
@@ -34,2 +50,14 @@ */ | ||
/** | ||
* Padding on all sides. Equivalent to setting `paddingTop`, `paddingBottom`, `paddingLeft` and `paddingRight`. | ||
*/ | ||
readonly padding?: number; | ||
/** | ||
* Horizontal padding. Equivalent to setting `paddingLeft` and `paddingRight`. | ||
*/ | ||
readonly paddingX?: number; | ||
/** | ||
* Vertical padding. Equivalent to setting `paddingTop` and `paddingBottom`. | ||
*/ | ||
readonly paddingY?: number; | ||
/** | ||
* Top padding. | ||
@@ -116,10 +144,62 @@ */ | ||
*/ | ||
readonly borderStyle?: keyof Boxes; | ||
readonly borderStyle?: keyof Boxes | BoxStyle; | ||
/** | ||
* Determines whether top border is visible. | ||
* | ||
* @default true | ||
*/ | ||
readonly borderTop?: boolean; | ||
/** | ||
* Determines whether bottom border is visible. | ||
* | ||
* @default true | ||
*/ | ||
readonly borderBottom?: boolean; | ||
/** | ||
* Determines whether left border is visible. | ||
* | ||
* @default true | ||
*/ | ||
readonly borderLeft?: boolean; | ||
/** | ||
* Determines whether right border is visible. | ||
* | ||
* @default true | ||
*/ | ||
readonly borderRight?: boolean; | ||
/** | ||
* Change border color. | ||
* Accepts the same values as `color` in <Text> component. | ||
* Shorthand for setting `borderTopColor`, `borderRightColor`, `borderBottomColor` and `borderLeftColor`. | ||
*/ | ||
readonly borderColor?: LiteralUnion<ForegroundColorName, string>; | ||
/** | ||
* Change top border color. | ||
* Accepts the same values as `color` in `Text` component. | ||
*/ | ||
readonly borderTopColor?: LiteralUnion<ForegroundColorName, string>; | ||
/** | ||
* Change bottom border color. | ||
* Accepts the same values as `color` in `Text` component. | ||
*/ | ||
readonly borderBottomColor?: LiteralUnion<ForegroundColorName, string>; | ||
/** | ||
* Change left border color. | ||
* Accepts the same values as `color` in `Text` component. | ||
*/ | ||
readonly borderLeftColor?: LiteralUnion<ForegroundColorName, string>; | ||
/** | ||
* Change right border color. | ||
* Accepts the same values as `color` in `Text` component. | ||
*/ | ||
readonly borderRightColor?: LiteralUnion<ForegroundColorName, string>; | ||
/** | ||
* Behavior for an element's overflow in both directions. | ||
* | ||
* @default 'visible' | ||
*/ | ||
readonly overflow?: 'visible' | 'hidden'; | ||
/** | ||
* Behavior for an element's overflow in horizontal direction. | ||
* | ||
* @default 'visible' | ||
*/ | ||
@@ -129,2 +209,4 @@ readonly overflowX?: 'visible' | 'hidden'; | ||
* Behavior for an element's overflow in vertical direction. | ||
* | ||
* @default 'visible' | ||
*/ | ||
@@ -131,0 +213,0 @@ readonly overflowY?: 'visible' | 'hidden'; |
@@ -11,2 +11,11 @@ // eslint-disable-next-line n/file-extension-in-import | ||
const applyMarginStyles = (node, style) => { | ||
if ('margin' in style) { | ||
node.setMargin(Yoga.EDGE_ALL, style.margin ?? 0); | ||
} | ||
if ('marginX' in style) { | ||
node.setMargin(Yoga.EDGE_HORIZONTAL, style.marginX ?? 0); | ||
} | ||
if ('marginY' in style) { | ||
node.setMargin(Yoga.EDGE_VERTICAL, style.marginY ?? 0); | ||
} | ||
if ('marginLeft' in style) { | ||
@@ -26,2 +35,11 @@ node.setMargin(Yoga.EDGE_START, style.marginLeft || 0); | ||
const applyPaddingStyles = (node, style) => { | ||
if ('padding' in style) { | ||
node.setPadding(Yoga.EDGE_ALL, style.padding ?? 0); | ||
} | ||
if ('paddingX' in style) { | ||
node.setPadding(Yoga.EDGE_HORIZONTAL, style.paddingX ?? 0); | ||
} | ||
if ('paddingY' in style) { | ||
node.setPadding(Yoga.EDGE_VERTICAL, style.paddingY ?? 0); | ||
} | ||
if ('paddingLeft' in style) { | ||
@@ -177,10 +195,21 @@ node.setPadding(Yoga.EDGE_LEFT, style.paddingLeft || 0); | ||
if ('borderStyle' in style) { | ||
const borderWidth = typeof style.borderStyle === 'string' ? 1 : 0; | ||
node.setBorder(Yoga.EDGE_TOP, borderWidth); | ||
node.setBorder(Yoga.EDGE_BOTTOM, borderWidth); | ||
node.setBorder(Yoga.EDGE_LEFT, borderWidth); | ||
node.setBorder(Yoga.EDGE_RIGHT, borderWidth); | ||
const borderWidth = style.borderStyle ? 1 : 0; | ||
if (style.borderTop !== false) { | ||
node.setBorder(Yoga.EDGE_TOP, borderWidth); | ||
} | ||
if (style.borderBottom !== false) { | ||
node.setBorder(Yoga.EDGE_BOTTOM, borderWidth); | ||
} | ||
if (style.borderLeft !== false) { | ||
node.setBorder(Yoga.EDGE_LEFT, borderWidth); | ||
} | ||
if (style.borderRight !== false) { | ||
node.setBorder(Yoga.EDGE_RIGHT, borderWidth); | ||
} | ||
} | ||
}; | ||
const applyGapStyles = (node, style) => { | ||
if ('gap' in style) { | ||
node.setGap(Yoga.GUTTER_ALL, style.gap ?? 0); | ||
} | ||
if ('columnGap' in style) { | ||
@@ -187,0 +216,0 @@ node.setGap(Yoga.GUTTER_COLUMN, style.columnGap ?? 0); |
{ | ||
"name": "ink", | ||
"version": "4.1.0", | ||
"version": "4.2.0", | ||
"description": "React for CLI", | ||
@@ -21,2 +21,3 @@ "license": "MIT", | ||
"scripts": { | ||
"dev": "tsc --watch", | ||
"build": "tsc", | ||
@@ -55,2 +56,4 @@ "prepare": "npm run build", | ||
"is-ci": "^3.0.1", | ||
"is-lower-case": "^2.0.2", | ||
"is-upper-case": "^2.0.2", | ||
"lodash": "^4.17.21", | ||
@@ -152,2 +155,5 @@ "patch-console": "^2.0.0", | ||
}, | ||
"ignores": [ | ||
"src/parse-keypress.ts" | ||
], | ||
"overrides": [ | ||
@@ -154,0 +160,0 @@ { |
144
readme.md
@@ -171,3 +171,3 @@ [![](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner2-direct.svg)](https://github.com/vshymanskyy/StandWithUkraine/blob/main/docs/README.md) | ||
<details><summary>Manual setup</summary> | ||
<details><summary>Manual JavaScript setup</summary> | ||
<p> | ||
@@ -185,13 +185,3 @@ Ink requires the same Babel setup as you would do for regular React-based apps in the browser. | ||
{ | ||
"presets": [ | ||
"@babel/preset-react", | ||
[ | ||
"@babel/preset-env", | ||
{ | ||
"targets": { | ||
"node": true | ||
} | ||
} | ||
] | ||
] | ||
"presets": ["@babel/preset-react"] | ||
} | ||
@@ -614,3 +604,3 @@ ``` | ||
```jsx | ||
<Box gap={1}> | ||
<Box columnGap={1}> | ||
<Text>A</Text> | ||
@@ -630,3 +620,3 @@ <Text>B</Text> | ||
```jsx | ||
<Box flexDirection="column" gap={1}> | ||
<Box flexDirection="column" rowGap={1}> | ||
<Text>A</Text> | ||
@@ -936,3 +926,3 @@ <Text>B</Text> | ||
Type: `string`\ | ||
Allowed values: `single` `double` `round` `bold` `singleDouble` `doubleSingle` `classic` | ||
Allowed values: `single` `double` `round` `bold` `singleDouble` `doubleSingle` `classic` | `BoxStyle` | ||
@@ -981,4 +971,23 @@ Add a border with a specified style. | ||
See example in [examples/borders](examples/borders/borders.js). | ||
Alternatively, pass a custom border style like so: | ||
```jsx | ||
<Box | ||
borderStyle={{ | ||
topLeft: '↘', | ||
top: '↓', | ||
topRight: '↙', | ||
left: '→', | ||
bottomLeft: '↗', | ||
bottom: '↑', | ||
bottomRight: '↖', | ||
right: '←' | ||
}} | ||
> | ||
<Text>Custom</Text> | ||
</Box> | ||
``` | ||
See example in [examples/borders](examples/borders/borders.tsx). | ||
##### borderColor | ||
@@ -989,3 +998,3 @@ | ||
Change border color. | ||
Accepts the same values as [`color`](#color) in `<Text>` component. | ||
Shorthand for setting `borderTopColor`, `borderRightColor`, `borderBottomColor` and `borderLeftColor`. | ||
@@ -1000,2 +1009,95 @@ ```jsx | ||
##### borderTopColor | ||
Type: `string` | ||
Change top border color. | ||
Accepts the same values as [`color`](#color) in `<Text>` component. | ||
```jsx | ||
<Box borderStyle="round" borderTopColor="green"> | ||
<Text>Hello world</Text> | ||
</Box> | ||
``` | ||
##### borderRightColor | ||
Type: `string` | ||
Change right border color. | ||
Accepts the same values as [`color`](#color) in `<Text>` component. | ||
```jsx | ||
<Box borderStyle="round" borderRightColor="green"> | ||
<Text>Hello world</Text> | ||
</Box> | ||
``` | ||
##### borderRightColor | ||
Type: `string` | ||
Change right border color. | ||
Accepts the same values as [`color`](#color) in `<Text>` component. | ||
```jsx | ||
<Box borderStyle="round" borderRightColor="green"> | ||
<Text>Hello world</Text> | ||
</Box> | ||
``` | ||
##### borderBottomColor | ||
Type: `string` | ||
Change bottom border color. | ||
Accepts the same values as [`color`](#color) in `<Text>` component. | ||
```jsx | ||
<Box borderStyle="round" borderBottomColor="green"> | ||
<Text>Hello world</Text> | ||
</Box> | ||
``` | ||
##### borderLeftColor | ||
Type: `string` | ||
Change left border color. | ||
Accepts the same values as [`color`](#color) in `<Text>` component. | ||
```jsx | ||
<Box borderStyle="round" borderLeftColor="green"> | ||
<Text>Hello world</Text> | ||
</Box> | ||
``` | ||
##### borderTop | ||
Type: `boolean`\ | ||
Default: `true` | ||
Determines whether top border is visible. | ||
##### borderRight | ||
Type: `boolean`\ | ||
Default: `true` | ||
Determines whether right border is visible. | ||
##### borderBottom | ||
Type: `boolean`\ | ||
Default: `true` | ||
Determines whether bottom border is visible. | ||
##### borderLeft | ||
Type: `boolean`\ | ||
Default: `true` | ||
Determines whether left border is visible. | ||
### `<Newline>` | ||
@@ -1144,3 +1246,3 @@ | ||
See [examples/static](examples/static/static.js) for an example usage of `<Static>` component. | ||
See [examples/static](examples/static/static.tsx) for an example usage of `<Static>` component. | ||
@@ -1235,3 +1337,3 @@ #### items | ||
However, if user pastes text and it's more than one character, the callback will be called only once and the whole string will be passed as `input`. | ||
You can find a full example of using `useInput` at [examples/use-input](examples/use-input/use-input.js). | ||
You can find a full example of using `useInput` at [examples/use-input](examples/use-input/use-input.tsx). | ||
@@ -1519,3 +1621,3 @@ ```jsx | ||
See additional usage example in [examples/use-stdout](examples/use-stdout/use-stdout.js). | ||
See additional usage example in [examples/use-stdout](examples/use-stdout/use-stdout.tsx). | ||
@@ -1613,3 +1715,3 @@ ### useStderr() | ||
See example in [examples/use-focus](examples/use-focus/use-focus.js) and [examples/use-focus-with-id](examples/use-focus-with-id/use-focus-with-id.js). | ||
See example in [examples/use-focus](examples/use-focus/use-focus.tsx) and [examples/use-focus-with-id](examples/use-focus-with-id/use-focus-with-id.tsx). | ||
@@ -1616,0 +1718,0 @@ ### useFocusManager() |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
256928
126
3536
2058
27
+ Addedis-lower-case@^2.0.2
+ Addedis-upper-case@^2.0.2
+ Addedis-lower-case@2.0.2(transitive)
+ Addedis-upper-case@2.0.2(transitive)
+ Addedtslib@2.8.1(transitive)