improve game creation accessibility for blind players

pull/4844/head
Thibault Duplessis 2019-01-18 11:44:27 +08:00
parent 555e5d970f
commit 7f347bde6d
5 changed files with 103 additions and 65 deletions

View File

@ -11,6 +11,24 @@ import lila.tournament.System
trait SetupHelper { self: I18nHelper =>
type SelectChoice = (String, String, Option[String])
val clockTimeChoices: List[SelectChoice] = List(
("0", "0", none),
("0.25", "¼", none),
("0.5", "½", none),
("0.75", "¾", none)
) ::: List(
"1", "1.5", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16",
"17", "18", "19", "20", "25", "30", "35", "40", "45", "60", "90", "120", "150", "180"
).map { v => (v.toString, v.toString, none) }
val clockIncrementChoices: List[SelectChoice] = {
(0 to 20).toList ::: List(25, 30, 35, 40, 45, 60, 90, 120, 150, 180)
} map { s =>
(s.toString, s.toString, none)
}
def translatedTimeModeChoices(implicit ctx: Context) = List(
(TimeMode.RealTime.id.toString, I18nKeys.realTime.txt(), none),
(TimeMode.Correspondence.id.toString, I18nKeys.correspondence.txt(), none),
@ -38,7 +56,7 @@ trait SetupHelper { self: I18nHelper =>
System.Arena.id.toString -> "Arena"
)
private def variantTuple(variant: chess.variant.Variant)(implicit ctx: Context): (String, String, Option[String]) =
private def variantTuple(variant: chess.variant.Variant)(implicit ctx: Context): SelectChoice =
(variant.id.toString, variant.name, variant.title.some)
def translatedVariantChoices(implicit ctx: Context) = List(

View File

@ -10,6 +10,8 @@ import controllers.routes
private object bits {
val prefix = "sf_"
def fenInput(field: Field, strict: Boolean, validFen: Option[lila.setup.ValidFen])(implicit ctx: Context) = {
val url = field.value.fold(routes.Editor.index)(routes.Editor.load).url
div(cls := "fen_position optional_config")(
@ -34,31 +36,33 @@ private object bits {
)
}
def renderVariant(form: Form[_], variants: List[(String, String, Option[String])])(implicit ctx: Context) =
def renderVariant(form: Form[_], variants: List[SelectChoice])(implicit ctx: Context) =
div(cls := "variant label_select")(
label(`for` := "variant")(trans.variant.frag()),
renderLabel(form("variant"), trans.variant.frag()),
renderSelect(form("variant"), variants)
)
def renderSelect(field: Field, options: Seq[(String, String, Option[String])]) =
select(id := field.id, name := field.name)(
options.map {
case (value, name, title) => option(
st.value := value,
cls := s"${field.name}_$value",
st.title := title,
selected := field.value.has(value).option(true)
)(name)
}
)
def renderSelect(
field: Field,
options: Seq[SelectChoice],
compare: (String, String) => Boolean = (a, b) => a == b
) = select(id := s"$prefix${field.id}", name := field.name)(
options.map {
case (value, name, title) => option(
st.value := value,
st.title := title,
selected := field.value.exists(v => compare(v, value)).option(true)
)(name)
}
)
def renderRadios(field: Field, options: Seq[(String, String, Option[String])]) =
def renderRadios(field: Field, options: Seq[SelectChoice]) =
st.group(cls := "radio")(
options.map {
case (key, name, hint) => div(
input(
`type` := "radio",
id := s"${field.id}_${key}",
id := s"$prefix${field.id}_${key}",
st.name := field.name,
value := key,
checked := field.value.has(key).option(true)
@ -66,32 +70,47 @@ private object bits {
label(
cls := List("required" -> true, "hint--top" -> hint.isDefined),
dataHint := hint,
`for` := s"${field.id}_$key"
`for` := s"$prefix${field.id}_$key"
)(name)
)
}
)
def renderInput(field: Field) =
input(id := field.id, name := field.name, value := field.value)
input(name := field.name, value := field.value, `type` := "hidden")
def renderLabel(field: Field, content: Frag) =
label(`for` := s"$prefix${field.id}")(content)
def renderTimeMode(form: Form[_], config: lila.setup.BaseConfig)(implicit ctx: Context) =
div(cls := "time_mode_config optional_config")(
div(cls := "label_select")(
label(`for` := "timeMode")(trans.timeControl()),
renderLabel(form("timeMode"), trans.timeControl()),
renderSelect(form("timeMode"), translatedTimeModeChoices)
),
div(cls := "time_choice slider")(
trans.minutesPerSide(),
": ",
span(chess.Clock.Config(~form("time").value.map(x => (x.toDouble * 60).toInt), 0).limitString.toString),
renderInput(form("time"))
),
div(cls := "increment_choice slider")(
trans.incrementInSeconds(),
": ",
span(form("increment").value),
renderInput(form("increment"))
if (ctx.blindMode) frag(
div(cls := "time_choice")(
renderLabel(form("time"), trans.minutesPerSide()),
renderSelect(form("time"), clockTimeChoices, (a, b) => a.replace(".0", "") == b)
),
div(cls := "increment_choice")(
renderLabel(form("increment"), trans.incrementInSeconds()),
renderSelect(form("increment"), clockIncrementChoices)
)
)
else frag(
div(cls := "time_choice slider")(
trans.minutesPerSide(),
": ",
span(chess.Clock.Config(~form("time").value.map(x => (x.toDouble * 60).toInt), 0).limitString.toString),
renderInput(form("time"))
),
div(cls := "increment_choice slider")(
trans.incrementInSeconds(),
": ",
span(form("increment").value),
renderInput(form("increment"))
)
),
div(cls := "correspondence")(
div(cls := "days_choice slider")(

View File

@ -30,14 +30,13 @@ object forms {
div(cls := "mode_choice buttons")(
renderRadios(form("mode"), translatedModeChoices)
),
div(cls := "optional_config")(
!ctx.blindMode option div(cls := "optional_config")(
div(cls := "rating_range_config slider")(
trans.ratingRange(),
": ",
span(cls := "range")("? - ?"),
div(cls := "rating_range")(
renderInput(form("ratingRange"))(
`type` := "hidden",
dataMin := RatingRange.min,
dataMax := RatingRange.max
)
@ -54,15 +53,21 @@ object forms {
renderVariant(form, translatedAiVariantChoices),
fenInput(form("fen"), true, validFen),
renderTimeMode(form, lila.setup.AiConfig),
trans.level(),
div(cls := "level buttons")(
div(id := "config_level")(
renderRadios(form("level"), lila.setup.AiConfig.levelChoices)
),
div(cls := "ai_info")(
ratings.toList.map {
case (level, rating) => div(cls := s"level_$level")(trans.aiNameLevelAiLevel("A.I.", level))
}
if (ctx.blindMode) frag(
renderLabel(form("level"), trans.level()),
renderSelect(form("level"), lila.setup.AiConfig.levelChoices)
)
else frag(
trans.level(),
div(cls := "level buttons")(
div(id := "config_level")(
renderRadios(form("level"), lila.setup.AiConfig.levelChoices)
),
div(cls := "ai_info")(
ratings.toList.map {
case (level, rating) => div(cls := s"${prefix}level_$level")(trans.aiNameLevelAiLevel("A.I.", level))
}
)
)
)
)
@ -125,11 +130,11 @@ object forms {
case (key, name) => button(
disabled := typ == "hook" option true,
`type` := "submit",
dataHint := name,
dataHint := !ctx.blindMode option name,
cls := s"button hint--bottom $key",
st.name := form("color").id,
value := key
)(i)
)(if (ctx.blindMode) name else i)
}
)
)

View File

@ -20,17 +20,6 @@ ul.ui-autocomplete li a.ui-state-focus {
border-color: #b84400 !important;
box-shadow: 0 3px 4px rgba(0, 0, 0, 0.15) inset !important;
}
.ui-corner-right {
border-top-right-radius: 2px;
border-bottom-right-radius: 2px;
}
.ui-corner-left {
border-top-left-radius: 2px;
border-bottom-left-radius: 2px;
}
.ui-corner-all {
border-radius: 2px;
}
/* slider */
.ui-slider {

View File

@ -100,8 +100,8 @@ module.exports = function(cfg, element) {
var $startButtons = $('#start_buttons');
var sliderTimes = [
0, 0.25, 0.5, 0.75, 1, 1.5, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 25, 30, 35, 40, 45, 60, 90, 120, 150, 180
0, 1/4, 1/2, 3/4, 1, 3/2, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 25, 30, 35, 40, 45, 60, 90, 120, 150, 180
];
function sliderTime(v) {
@ -175,16 +175,16 @@ module.exports = function(cfg, element) {
function prepareForm() {
var $form = $('.lichess_overboard');
var $timeModeSelect = $form.find('#timeMode');
var $timeModeSelect = $form.find('#sf_timeMode');
var $modeChoicesWrap = $form.find('.mode_choice');
var $modeChoices = $modeChoicesWrap.find('input');
var $casual = $modeChoices.eq(0),
$rated = $modeChoices.eq(1);
var $variantSelect = $form.find('#variant');
var $variantSelect = $form.find('#sf_variant');
var $fenPosition = $form.find(".fen_position");
var $timeInput = $form.find('.time_choice input');
var $incrementInput = $form.find('.increment_choice input');
var $daysInput = $form.find('.days_choice input');
var $timeInput = $form.find('.time_choice [name=time]');
var $incrementInput = $form.find('.increment_choice [name=increment]');
var $daysInput = $form.find('.days_choice [name=days]');
var typ = $form.data('type');
var $ratings = $form.find('.ratings > div');
var randomColorVariants = $form.data('random-color-variants').split(',');
@ -196,6 +196,8 @@ module.exports = function(cfg, element) {
var rated = $rated.prop('checked');
var limit = $timeInput.val();
var inc = $incrementInput.val();
console.log($timeInput, $incrementInput);
console.log(limit, inc);
// no rated variants with less than 30s on the clock
var cantBeRated = (timeMode == '1' && variantId != '1' && limit < 0.5 && inc == 0) ||
(variantId != '1' && timeMode != '1');
@ -285,12 +287,17 @@ module.exports = function(cfg, element) {
} else $formTag.one('submit', function() {
$submits.hide().end().append(lichess.spinnerHtml);
});
lichess.slider().done(function() {
if ($('body').hasClass('blind_mode')) {
$timeInput.add($incrementInput).on('change', function() {
toggleButtons();
showRating();
});
} else lichess.slider().done(function() {
$timeInput.add($incrementInput).each(function() {
var $input = $(this),
$value = $input.siblings('span');
var isTimeSlider = $input.parent().hasClass('time_choice');
$input.hide().after($('<div>').slider({
$input.after($('<div>').slider({
value: sliderInitVal(parseFloat($input.val()), isTimeSlider ? sliderTime : sliderIncrement, 100),
min: 0,
max: isTimeSlider ? 34 : 30,
@ -308,7 +315,7 @@ module.exports = function(cfg, element) {
$daysInput.each(function() {
var $input = $(this),
$value = $input.siblings('span');
$input.hide().after($('<div>').slider({
$input.after($('<div>').slider({
value: sliderInitVal(parseInt($input.val()), sliderDays, 20),
min: 1,
max: 7,
@ -401,7 +408,7 @@ module.exports = function(cfg, element) {
});
$(this).find('#config_level').mouseleave(function() {
var level = $(this).find('input:checked').val();
$infos.hide().filter('.level_' + level).show();
$infos.hide().filter('.sf_level_' + level).show();
}).trigger('mouseout');
});