-
-
Notifications
You must be signed in to change notification settings - Fork 48
Button component #125
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Button component #125
Changes from 11 commits
dc4052c
68939d1
25ad213
cef3935
4c0947a
5ed0465
6aeaeb6
c117509
9319f06
763f8d0
bf742c8
c568eb1
85cd94c
d3ca898
5691c82
0faeb16
0875e2b
6b656c1
5d0ad3b
8be0ae1
7b161c6
7dbe3df
4738b50
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
--- | ||
id: components-listview | ||
title: ListView | ||
sidebar_label: ListView | ||
--- | ||
|
||
## ListView | ||
|
||
List View is a special type of view that can display items one by one based in a list. | ||
|
||
```JS | ||
const DATA = [ | ||
{ | ||
title: 'Main dishes', | ||
data: ['Pizza', 'Burger', 'Risotto'], | ||
}, | ||
{ | ||
title: 'Sides', | ||
data: ['French Fries', 'Onion Rings', 'Fried Shrimps'], | ||
}, | ||
{ | ||
title: 'Drinks', | ||
data: ['Water', 'Coke', 'Beer'], | ||
}, | ||
{ | ||
title: 'Desserts', | ||
data: ['Cheese Cake', 'Ice Cream'], | ||
}, | ||
]; | ||
|
||
function Item({ title }) { | ||
return ( | ||
<View style={styles.item}> | ||
<Text style={styles.title}>{title}</Text> | ||
</View> | ||
); | ||
} | ||
|
||
export default function App() { | ||
return ( | ||
<SectionList | ||
sections={DATA} | ||
keyExtractor={(item, index) => item + index} | ||
renderItem={({ item }) => <Item title={item} />} | ||
renderSectionHeader={({ section: { title } }) => ( | ||
<Text>{title}</Text> | ||
)} | ||
/> | ||
); | ||
} | ||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,189 @@ | ||
/** | ||
* @https://github.com/facebook/react-native/blob/main/Libraries/Components/Button.js | ||
* | ||
* @flow | ||
* | ||
*/ | ||
|
||
import {ButtonDefaults} from '../constants'; | ||
|
||
//TODO adjust Opacity when focus, Blur | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is there a ticket for this TODO? |
||
type PressEvent = {||}; | ||
type ButtonProps = {| | ||
title: string, | ||
onPress: (event?: PressEvent) => mixed, | ||
onClick: (event?: SyntheticMouseEvent<HTMLButtonElement>) => mixed, | ||
touchSoundDisabled?: ?boolean, | ||
color?: ?string, | ||
/** | ||
* TV next focus down (see documentation for the View component). | ||
* | ||
* @platform android | ||
*/ | ||
nextFocusDown?: ?number, | ||
/** | ||
* TV next focus forward (see documentation for the View component). | ||
* | ||
* @platform android | ||
*/ | ||
nextFocusForward?: ?number, | ||
|
||
/** | ||
* TV next focus left (see documentation for the View component). | ||
* | ||
* @platform android | ||
*/ | ||
nextFocusLeft?: ?number, | ||
|
||
/** | ||
* TV next focus right (see documentation for the View component). | ||
* | ||
* @platform android | ||
*/ | ||
nextFocusRight?: ?number, | ||
|
||
/** | ||
* TV next focus up (see documentation for the View component). | ||
* | ||
* @platform android | ||
*/ | ||
nextFocusUp?: ?number, | ||
|
||
/** | ||
* Text to display for blindness accessibility features | ||
*/ | ||
accessibilityLabel?: ?string, | ||
|
||
/** | ||
* If true, disable all interactions for this component. | ||
*/ | ||
disabled?: ?boolean, | ||
|
||
/** | ||
* Used to locate this view in end-to-end tests. | ||
*/ | ||
testID?: ?string, | ||
|}; | ||
|
||
function renderButton(props: ButtonProps, apeContext, parentLayout) { | ||
// | ||
console.log('[RENDER]'); | ||
hayanisaid marked this conversation as resolved.
Show resolved
Hide resolved
|
||
const {spatialGeometry = {x: 0, y: 0}} = parentLayout; | ||
const {ctx} = apeContext; | ||
|
||
// If is relative and x and y haven't be processed, don't render | ||
if (!spatialGeometry) return null; | ||
// start drawing the canvas | ||
const {title, color} = props; | ||
const borderRadius = ButtonDefaults.containerStyle.borderRadius; | ||
const backgroundColor = ButtonDefaults.containerStyle.backgroundColor; | ||
let x = spatialGeometry.x || 0; | ||
let y = spatialGeometry.y || 0; | ||
let width = x + y; | ||
let height = ButtonDefaults.containerStyle.height; | ||
let globalStyle = { | ||
width: width, | ||
height: height, | ||
color: color, | ||
borderRadius: borderRadius, | ||
backgroundColor, | ||
lineWidth: 0, | ||
borderColor: 'transparent', | ||
}; | ||
const resetStyle = newStyle => { | ||
globalStyle = {...globalStyle, newStyle}; | ||
console.log('style)))))', globalStyle); | ||
}; | ||
const redrawButton = ctx => { | ||
// TODO reset Style on focus | ||
let newStyle = { | ||
lineWidth: 2, | ||
borderColor: '#ccc', | ||
}; | ||
//let prevStyle = globalStyle | ||
resetStyle(newStyle); | ||
}; | ||
|
||
ctx.beginPath(); | ||
ctx.fillStyle = backgroundColor; | ||
ctx.moveTo(x, y); | ||
/** | ||
* Top Right Radius | ||
*/ | ||
ctx.lineTo(x + width - borderRadius, y); | ||
ctx.quadraticCurveTo(x + width, y, x + width, y + borderRadius); | ||
/** | ||
* Bottom right Radius | ||
*/ | ||
|
||
ctx.lineTo(x + width, y + height - borderRadius); | ||
ctx.quadraticCurveTo( | ||
x + width, | ||
y + height, | ||
x + width - borderRadius, | ||
y + height | ||
); | ||
|
||
/** | ||
* Bottom Left Radius | ||
*/ | ||
ctx.lineTo(x + borderRadius, y + height); | ||
ctx.quadraticCurveTo(x, y + height, x, y + height - borderRadius); | ||
/** Top left Radius */ | ||
ctx.lineTo(x, y + borderRadius); | ||
ctx.quadraticCurveTo(x, y, x + borderRadius, y); | ||
|
||
ctx.fill(); | ||
ctx.lineWidth = globalStyle.lineWidth; | ||
ctx.strokeStyle = globalStyle.borderColor; | ||
ctx.stroke(); | ||
ctx.fillStyle = color || ButtonDefaults.textStyle.color; | ||
ctx.font = `${ButtonDefaults.textStyle.fontSize} Helvetica`; | ||
//ctx.fillText('Start', x+ width/2 , y + height / 2); | ||
ctx.textAlign = 'center'; | ||
ctx.fillText(title, x + width / 2, y + height / 2); | ||
ctx.closePath(); | ||
|
||
const onClick = (event: SyntheticMouseEvent<HTMLButtonElement>) => { | ||
const rect = { | ||
x, | ||
y, | ||
height, | ||
width, | ||
}; | ||
const mousePosition = trackMousePosition(ctx.canvas, event); | ||
if (isInside(mousePosition, rect)) { | ||
redrawButton(ctx); | ||
if (props.onClick && typeof props.onClick === 'function') { | ||
props.onClick(event); | ||
} | ||
} | ||
}; | ||
function trackMousePosition(canvas, event) { | ||
hayanisaid marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return { | ||
x: event.clientX - canvas.offsetLeft, | ||
y: event.clientY - canvas.offsetTop, | ||
}; | ||
} | ||
const isInside = (pos, rect) => { | ||
hayanisaid marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return ( | ||
pos.x > rect.x && | ||
pos.x < rect.x + rect.width && | ||
pos.y < rect.y + rect.height && | ||
pos.y > rect.y | ||
); | ||
}; | ||
|
||
ctx.canvas.addEventListener('click', onClick, false); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We need to remove addEventListeners from the renderButton function because this function runs for each state/prop update. It will keep creating/refreshing listeners for every render. We can keep this way, if we run this addEventListener once by checking if the listener already exist. Note onClick will need to share scope with this function to work properly. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Working on this! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe this property will need to be coming from parent View instead of itself. Btw, if you want to join the discord to talk, there's a link here https://medium.com/@raphamorim/bet-on-canvas-for-a-high-performant-tv-application-with-react-ape-cdf8c0b77c21 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Invalid Discord link invite! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. oops, try this link https://discord.gg/njHHfRzJ42 |
||
ctx.canvas.addEventListener('focus', redrawButton); | ||
ctx.canvas.addEventListener('blur', redrawButton); | ||
|
||
// events | ||
} | ||
|
||
export default function createButtonInstance(props: ButtonProps): mixed { | ||
return { | ||
type: 'Button', | ||
render: renderButton.bind(this, props), | ||
}; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
import React from 'react'; | ||
import renderer from 'react-test-renderer'; | ||
import {ButtonDefaults} from '../../constants'; | ||
import CreateButtonInstance from '../Button'; | ||
|
||
describe('Button', () => { | ||
describe('Call the button with Props', () => { | ||
it('Should render properly', () => { | ||
const title = 'Press Me'; | ||
const color = '#f8a978'; | ||
const x = 40; | ||
const y = 20; | ||
const width = x + y; | ||
const height = ButtonDefaults.containerStyle.height; | ||
const props = {title, color}; | ||
const apeContext = { | ||
ctx: { | ||
beginPath: jest.fn(), | ||
fillStyle: jest.fn(), | ||
moveTo: jest.fn(), | ||
fillText: jest.fn(), | ||
fill: jest.fn(), | ||
stroke: jest.fn(), | ||
closePath: jest.fn(), | ||
lineTo: jest.fn(), | ||
quadraticCurveTo: jest.fn(), | ||
font: 'Helvetica', | ||
canvas: { | ||
addEventListener: jest.fn(), | ||
}, | ||
}, | ||
}; | ||
|
||
const Button = CreateButtonInstance(props); | ||
Button.render(apeContext, {spatialGeometry: {x, y}}); | ||
const { | ||
beginPath, | ||
fillStyle, | ||
moveTo, | ||
fillText, | ||
fill, | ||
stroke, | ||
closePath, | ||
lineTo, | ||
quadraticCurveTo, | ||
font, | ||
} = apeContext.ctx; | ||
expect(beginPath.mock.calls.length).toBe(1); | ||
expect(beginPath).toBeCalledWith(); | ||
expect(closePath).toBeCalledWith(); | ||
expect(stroke).toBeCalledWith(); | ||
expect(moveTo).toBeCalledWith(x, y); | ||
expect(lineTo.mock.calls.length).toEqual(4); | ||
expect(fill.mock.calls.length).toBe(1); | ||
expect(fillText.mock.calls.length).toBe(1); | ||
expect(fillText).toBeCalledWith(title, x + width / 2, y + height / 2); | ||
expect(font).toEqual(`${ButtonDefaults.textStyle.fontSize} Helvetica`); | ||
expect(quadraticCurveTo.mock.calls.length).toEqual(4); | ||
expect(fillStyle).toBe(color); | ||
expect(Button).toMatchSnapshot(); | ||
}); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
// Jest Snapshot v1, https://goo.gl/fbAQLP | ||
|
||
exports[`Button Call the button with Props Should render properly 1`] = ` | ||
Object { | ||
"render": [Function], | ||
"type": "Button", | ||
} | ||
`; |
Uh oh!
There was an error while loading. Please reload this page.