JSFiddle - React, Tailwind, and code Playground

DPadComponentMockup

by Retsam19

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.0/knockout-min.js"></script>

<!-- ko template: {
  name: "layout",
  data: main
} -->
<!-- /ko -->


<template id="layout">
  <div class="layout">
    <!-- ko template: {
      name: 'layout-cell',
      foreach: children
    } -->
    <!-- /ko -->
    <div class="overlay">
      <!-- ko template: {
        name: "dpad"
      } -->
      <!-- /ko -->
    </div>
  </div>
</template>

<template id="layout-cell">
  <div class="layout-cell" data-bind="
    style: {
      width: width,
      height: height
    }
  "></div>
</template>

<template id="dpad">
  <div class="dpad">
    <div class="quarter quarter1" data-bind="
      click: clickHandler('up')
    "></div>
    <div class="quarter quarter2" data-bind="
      click: clickHandler('right')
    "></div> 
    <div class="quarter quarter3" data-bind="
      click: clickHandler('left')
    "></div>
    <div class="quarter quarter4" data-bind="
      click: clickHandler('down')
    "></div>
    <div class="cutout"></div>
  </div>
</template>

SCSS

html, body {
  height: 100%
}

.layout {
  position: relative;
  height: 100%;
  border: 1px solid black;
}

.layout-cell {
  background-color: rgba(0, 0, 255, 10%)
}

.overlay {
  position: absolute;
  left: 0; right: 0; top: 0; bottom: 0;
  display: flex;
  align-items: center;
  /* background-color: rgba(255, 0, 0, 30%); */
}


/**
* Quarter Circles
*/

.dpad {
	position:relative;
	width:100px;
	height:100px;
	margin:0 auto;
}

.quarter {
	position:absolute;
	width:50%;
	height:50%;
	transform: rotate(45deg);
  cursor: pointer;
}
.quarter:hover {
	background-color:pink;
}
.quarter1 {
	top:0;
	left:0;
	background-color: #CCC;
	border-radius:100% 0 0 0;
	transform-origin: bottom right;
}
.quarter2 {
	top:0;
	right:0;
	background-color: #DDD;
	border-radius:0 100% 0 0;
	transform-origin: bottom left;
}
.quarter3 {
	bottom:0;
	left:0;
	background-color: #DDD;
	border-radius:0 0 0 100%;
	transform-origin: top right;
}
.quarter4 {
	bottom:0;
	right:0;
	background-color: #CCC;
	border-radius:0 0 100% 0 ;
	transform-origin: top left;
}
.cutout {
	width:50%;
	height:50%;
	background-color:white;
	position:absolute;
	top:25%;
	left:25%;
	border-radius:50%;
	/* pointer-events:none; */
  cursor: default;
}

Babel + JSX

const STRETCH = "stretch";
const ROW = "row";
const COLUMN = "column";

class LayoutCell {
  constructor(type) {
      if(!([STRETCH, ROW, COLUMN].includes(type))) {
        throw new Error("Invalid type: " + type)
      }
      
      this.width = ko.computed(function () {
        return {
          [STRETCH]: "100%",
          [ROW]: "100%",
          [COLUMN]: "100px"
        }[type];
      });
      this.height = ko.computed(function () {
        return {
          [STRETCH]: "100%",
          [ROW]: "100px",
          [COLUMN]: "100%"
        }[type];
      });
  }
}

class LayoutContainer {
  constructor() {
    this.children = ko.observableArray([
    	new LayoutCell(STRETCH)
    ]);
  }

  clickHandler(dir) {
    return function () {
      if(!this.direction) {
        this.direction = (dir === "up" || dir === "down") ?
          COLUMN : ROW;
          
        
      }
      console.log("Clicked " + dir);
    }
  }
}

ko.applyBindings({
  main: new LayoutContainer()
});