tabris-js icon indicating copy to clipboard operation
tabris-js copied to clipboard

RadioButton behaves like a checkbox when it's attached to Cell/ListView

Open ahmadov opened this issue 5 years ago • 3 comments

Problem description

According to the docs, Cell could be a parent component of RadioButton. But, when it's attached to the Cell it behaves like a checkbox, previously selected RadioButtons are still checked when another one is selected.

Expected behavior

Only one RadioButton can be selected at a time.

Environment

  • Tabris.js version: 3.6
  • Device: iPad Pro 3rd gen and Pixel 3
  • OS: iOS 13.6 and Android 10

Code snippet

import { contentView, Stack, RadioButton, TextView } from 'tabris';
import { ListView, Cell } from 'tabris-decorators'; 

const ITEM_HEIGHT = 36;
const options = [
  { label: 'foo' },
  { label: 'bar' },
  { label: 'baz' }
];
const title = 'Example text';

contentView.append(
  <Stack alignment="stretchX" stretch spacing={16}>
    <TextView text={title} />
    <ListView
      height={ITEM_HEIGHT * options.length}
      items={options}
      >
        <Cell height={ITEM_HEIGHT}>
          <RadioButton bind-text="item.label" />
        </Cell>
    </ListView>
  </Stack>
);

ahmadov avatar Aug 27 '20 14:08 ahmadov

@ahmadov, with your snippet several Cells are created and all of them have a single RadioButton child. If you want to group the RadioButtons, you should add multiple RadioButtons to a single Cell.

<Cell height={ITEM_HEIGHT}>
  <RadioButton class='first' stretchX bind-text="..." />
  <RadioButton class='second' stretchX top='prev()' bind-text="..." />
  <RadioButton class='third' stretchX top='prev()' bind-text="..." />
</Cell>

elshadsm avatar Sep 09 '20 13:09 elshadsm

@elshadsm but this is not what I want. I want to create a simple list (maybe a long one) that each cell contains a RadioButton and TextView as follows: Screenshot from 2020-09-10 15-32-08

ahmadov avatar Sep 10 '20 13:09 ahmadov

@ahmadov, in this case, you can use ScrollView instead of ListView OR you can use the hacky solution below for the long lists:

import { Stack, TextView, CollectionView, Composite, RadioButton, contentView } from 'tabris';

const ITEM_HEIGHT = 36;
const options = [
  { label: 'foo', checked: false },
  { label: 'bar', checked: false },
  { label: 'baz', checked: false }
];
const title = 'Example text';

let oldCheckedIndex = -1;
let newCheckedIndex = -1;

contentView.append(
  <Stack alignment="stretchX" stretch spacing={16}>
    <TextView text={title} />
    <CollectionView
      height={ITEM_HEIGHT * options.length}
      itemCount={options.length}
      cellHeight={ITEM_HEIGHT}
      createCell={createCell}
      updateCell={updateCell} />
  </Stack>
);

function createCell() {
  return (
    <Composite>
      <RadioButton
        onSelect={({ target }) => handleRadioButtonSelect(target)} />
    </Composite>
  );
}

function updateCell(cell: Composite, index: number) {
  cell.data.index = index;
  const button = cell.find(RadioButton).first();
  const option = options[index];
  button.text = option.label;
  if (option.checked) {
    button.checked = !(oldCheckedIndex === index);
  }
}

function handleRadioButtonSelect(button: RadioButton) {
  if (newCheckedIndex > -1) {
    oldCheckedIndex = newCheckedIndex;
  }
  if (oldCheckedIndex > -1) {
    contentView.find(CollectionView).first().refresh(oldCheckedIndex);
  }
  const index = button.parent().data.index;
  options[index].checked = true;
  newCheckedIndex = index;
}

elshadsm avatar Sep 10 '20 15:09 elshadsm