<commit_before>import React from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';

import styles from 'molecules/formfields/shared/formfields.module.scss';

function DateField(props) {
  const {
    className,
    children,
    label,
    meta
  } = props;

  const baseClassName = classnames(
    styles['datefield'],
    { [styles.focused]: meta && meta.active },
    { [styles.hasError]: meta && meta.touched && meta.error && !meta.active },
    className
  );

  const childClassName = classnames(
    styles['datefield-input'],
    { [styles['datefield-input-focused']]: meta && meta.active }
  );

  return (

        {label}

          { React.Children.map(children,
            child =>

                { child }

          )}

      {meta && meta.touched && meta.error &&

{ meta.error }

      }

  );
}

DateField.propTypes = {
  /**
   * This prop will add a new className to any inherent classNames
   * provided in the component's index.js file.
   */
  className: PropTypes.string,
  /**
   * Label.
   */
  label: PropTypes.string,
  /**
   * Meta object is passed from reduxForm
   */
  meta: PropTypes.object
};

export default DateField;
<commit_msg>The fix that fixes everything<commit_after>import React from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';

import styles from 'molecules/formfields/shared/formfields.module.scss';

function DateField(props) {
  const {
    className,
    children,
    label,
    meta,
    input
  } = props;

  const baseClassName = classnames(
    styles['datefield'],
    { [styles.focused]: meta && meta.active },
    { [styles.hasError]: meta && meta.touched && meta.error && !meta.active },
    className
  );

  const childClassName = classnames(
    styles['datefield-input'],
    { [styles['datefield-input-focused']]: meta && meta.active }
  );

  return (

        {label}

          { React.Children.map(children,
            child =>

                { child }

          )}

      {meta && meta.touched && meta.error &&

{ meta.error }

      }

  );
}

DateField.propTypes = {
  /**
   * This prop will add a new className to any inherent classNames
   * provided in the component's index.js file.
   */
  className: PropTypes.string,
  /**
   * Label.
   */
  label: PropTypes.string,
  /**
   * Meta object is passed from reduxForm
   */
  meta: PropTypes.object,
  /**
   * Input object is passed from reduxForm
   */
  input: PropTypes.object
};

export default DateField;
<|endoftext|>


<commit_before>const downloader = require("./chart-downloader");
const {
  helmInstallUpgrade,
  helmStatus,
  helmUninstall,
  helmList,
  helmTemplate
} = require("./helm");
const execa = require("execa");
const { join } = require("path");
const { installIstio, upgradeIstio } = require("./istioctl");
const pRetry = require("p-retry");
const k8s = require("@kubernetes/client-node");
const {
  debug,
  k8sCoreV1Api,
  kubectlDelete,
  kubectlApplyDir,
  kubectlApply,
  k8sDelete,
  deleteAllK8sResources,
  deleteNamespaces,
  getAllCRDs,
  k8sApply,
  waitForDeployment,
  waitForStatefulSet
} = require("../utils");
const notDeployed = "not-deployed";
const kymaCrds = require("./kyma-crds");
const defaultIstioVersion = "1.10.0";

async function waitForNodesToBeReady(timeout = "180s") {
  await execa("kubectl", [
    "wait",
    "--for=condition=Ready",
    "nodes",
    "--all",
    `--timeout=${timeout}`,
  ]);
  debug("All nodes are ready!");
}

async function updateCoreDNSConfigMap() {

  const cmName = "coredns";
  const cmNamespace = "kube-system";

  const { body } = await k8sCoreV1Api.readNamespacedConfigMap(
    "coredns",
    "kube-system"
  );

  const { stdout: registryIP } = await execa("docker", [
    "inspect",
    "-f",
    "{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}",
    "/registry.localhost",
  ]).catch(() => "127.0.0.1");

  // this file need to be updated when we update k3s to use k8s 1.20
  body.data["Corefile"] = `registry.localhost:53 {
    hosts {
        ${registryIP} registry.localhost
    }
}
.:53 {
    errors
    health
    rewrite name regex (.*)\.local\.kyma\.dev istio-ingressgateway.istio-system.svc.cluster.local
    ready
    kubernetes cluster.local in-addr.arpa ip6.arpa {
      pods insecure
      upstream
      fallthrough in-addr.arpa ip6.arpa
    }
    hosts /etc/coredns/NodeHosts {
      reload 1s
      fallthrough
    }
    prometheus :9153
    forward . /etc/resolv.conf
    cache 30
    loop
    reload
    loadbalance
}
`;
  await k8sCoreV1Api.replaceNamespacedConfigMap(cmName, cmNamespace, body);
}

async function removeKymaGatewayCertsYaml(pathToResources) {
  // TODO: find some other mechanism, deleting that file is destructive
  // we can copy resources directory so that we can act on it
  try {
    await execa("rm", [
      join(
        pathToResources,
        "core/charts/gateway/templates/kyma-gateway-certs.yaml"
      ),
    ]);
  } catch (err) {
    console.log("kyma-gateway-certs.yaml already deleted");
  }
}
function skipHelmTest(r) {
  return !(r.metadata.annotations && r.metadata.annotations["helm.sh/hook"] && r.metadata.annotations["helm.sh/hook"].includes("test-success"));
}
function skipNull(r) {
  return (r != null);
}

async function applyRelease(
  release,
  namespace = "kyma-system",
  chart,
  values,
  profile,
  isUpgrade
) {
  const { stdout } = await helmTemplate(release, chart, namespace, values, profile);
  const yamls = k8s.loadAllYaml(stdout).filter(skipNull).filter(skipHelmTest);
  const deployments = yamls.filter((r) => r.kind === 'Deployment');
  const statefulsets = yamls.filter((r) => r.kind === 'StatefulSet');
  await k8sApply(yamls, namespace);
  for (let deployment of deployments) {
    console.log("Waitng for deployment: ", deployment)
    await waitForDeployment(deployment.metadata.name, deployment.metadata.namespace || namespace);
  }
  for (let ss of statefulsets) {
    console.log("Waitng for StatefulSet: ", ss)
    await waitForStatefulSet(ss.metadata.name, ss.metadata.namespace || namespace);
  }
}

async function installRelease(
  release,
  namespace = "kyma-system",
  chart,
  values,
  profile,
  isUpgrade
) {

  let status = await helmStatus(release, namespace);
  switch (status) {
    case "pending-install":
      debug(
        `Deleting ${release} from namespace ${namespace} because previous installation got stuck in pending-install`
      );
      await helmUninstall(release, namespace);
      break;
    case "failed":
      debug(
        `Deleting ${release} from namespace ${namespace} because previous installation failed`
      );
      await helmUninstall(release, namespace);
      break;
    case notDeployed:
      debug(`Installing release ${release}`);
      await helmInstallUpgrade(release, chart, namespace, values, profile);
      break;
    case "deployed":
      if (isUpgrade) {
        debug(`Upgrading release ${release}`);
        await helmInstallUpgrade(release, chart, namespace, values, profile);

      } else {
        debug(`Release ${release} already installed - skipped`);
        return;
      }
    default:
      break;
  }

  status = await helmStatus(release, namespace);
  if (status !== "deployed") {
    throw new Error(
      `Release ${release} is in status ${status}, which is not "deployed"`
    );
  }
  debug(`Release ${release} deployed`);
}

async function chartList(options) {
  const gardenerDomain = await getGardenerDomain();
  const domain = process.env["KYMA_DOMAIN"] || gardenerDomain || "local.kyma.dev";

  const isGardener = (gardenerDomain ? true : false) || (process.env["GARDENER"] === "true");
  const isCompassEnabled = !!options.withCompass;
  const isCentralApplicationConnectivityEnabled = !!options.withCentralAppConnectivity;

  const overrides = [
    `authProxy.config.useDex=false`,
    `global.centralApplicationConnectivityValidatorEnabled=${isCentralApplicationConnectivityEnabled}`,
    `central_application_gateway.enabled=${isCentralApplicationConnectivityEnabled}`,
    `global.disableLegacyConnectivity=${isCompassEnabled}`,
    `global.domainName=${domain}`,
    `global.environment.gardener=${isGardener}`,
    `global.isLocalEnv=false`,
    `global.domainName=${domain}`,
    `global.tlsCrt=ZHVtbXkK`
  ].join(',');

  // https://github.com/kyma-project/test-infra/pull/2967
  let registryOverrides;
  if (isGardener == true) {
    registryOverrides = [
      `dockerRegistry.enableInternal=true`,
      `global.domainName=${domain}`
    ].join(',');
  } else {
    registryOverrides = [
      `containers.manager.envs.functionBuildExecutorImage.value=eu.gcr.io/kyma-project/external/aerfio/kaniko-executor:v1.3.0`,
      `dockerRegistry.enableInternal=false`,
      `dockerRegistry.registryAddress=registry.localhost:5000`,
      `dockerRegistry.serverAddress=registry.localhost:5000`,
      `global.domainName=${domain}`
    ].join(',');
  }
  const kialiOverrides = overrides + ',kcproxy.enabled=false,virtualservice.enabled=false';
  const tracingOverrides = overrides + ',kcproxy.enabled=false,virtualservice.enabled=false';

  const kymaCharts = [
    {
      release: "pod-preset",
      namespace: "kyma-system",
      customPath: (root) =>
        join(root, "cluster-essentials", "charts", "pod-preset"),
    },
    {
      release: `cluster-users`,
      namespace: "kyma-system",
      values: `${overrides}`,
    },
    {
      release: "core",
      namespace: "kyma-system",
      values: `${overrides}`,
    },
    {
      release: "ory",
      namespace: "kyma-system",
      values: `${overrides}`,
    },
    {
      release: "serverless",
      namespace: "kyma-system",
      values: registryOverrides,
    },
    {
      release: "api-gateway",
      namespace: "kyma-system",
      values: `${overrides},deployment.resources.requests.cpu=10m`,
    },
    {
      release: "rafter",
      namespace: "kyma-system",
      values: `${overrides}`,
    },
    {
      release: "service-catalog",
      namespace: "kyma-system",
      values: `${overrides}`,
    },
    {
      release: "service-catalog-addons",
      namespace: "kyma-system",
      values: `${overrides}`,
    },
    {
      release: "helm-broker",
      namespace: "kyma-system",
      values: `${overrides}`,
    },
    {
      release: "eventing",
      namespace: "kyma-system",
      values: `${overrides}`
    },
    {
      release: "application-connector",
      namespace: "kyma-integration",
      values: `${overrides}`,
    },
    {
      release: "monitoring",
      namespace: "kyma-system",
      values: `${overrides}`,
    },
    {
      release: "kiali",
      namespace: "kyma-system",
      values: `${kialiOverrides}`,
    },
    {
      release: "tracing",
      namespace: "kyma-system",
      values: `${tracingOverrides}`,
    },
    {
      release: "logging",
      namespace: "kyma-system",
      values: `${overrides}`,
    },
    {
      release: "compass-runtime-agent",
      namespace: "compass-system",
      values: `${overrides}`,
      filter: isCompassEnabled
    },
    {
      release: "ingress-dns-cert",
      namespace: "istio-system",
      values: `global.domainName=${domain},global.environment.gardener=${isGardener}`,
      customPath: () => join(__dirname, "charts", "ingress-dns-cert"),
    },
  ];

  return kymaCharts.filter(c => c.filter == undefined || c.filter);
}

async function getGardenerDomain() {
  try {
    const { body } = await k8sCoreV1Api.readNamespacedConfigMap(
      "shoot-info",
      "kube-system"
    );
    return body.data.domain;
  } catch (err) {
    if (err.statusCode == 404) {
      return null;
    }
    throw err;
  }
}
async function uninstallIstio() {
  const crds = await getAllCRDs();
  const istioCRDs = crds.filter(crd => crd.spec.group.endsWith('istio.io'));
  await k8sDelete(istioCRDs);
  await deleteNamespaces(["istio-system"], true);
}
/**
 *
 * Uninstalls Kyma
 * @param {Object} options Uninstallation options
 * @param {boolean} options.skipCrd Do not delete CRDs
 * @param {Array<string>} options.skipComponents List of components that should not be uninstalled
 * @param {Array<string>} options.components List of components to uninstall
 * @param {boolean} options.deleteNamespaces
 */
async function uninstallKyma(options) {
  let releases = await helmList();
  debug("Releases in the cluster:", releases);
  const components = options.components;
  const skipComponents = options.skipComponents;
  if (components) {
    releases = releases.filter(r => components.includes(r.name))
  }
  if (skipComponents) {
    releases = releases.filter(r => !skipComponents.includes(r.name))
  }
  debug("Releases to uninstall:", releases);

  await Promise.allSettled(releases.map((r) => helmUninstall(r.name, r.namespace).catch(() => {
    // ignore errors during uninstall ()
  })));

  await kubectlDelete(join(__dirname, "installer-local.yaml")); // needed for the console to start
  if (!options.skipCrd) {
    const crds = await getAllCRDs();
    await k8sDelete(crds.filter(crd => kymaCrds.includes(crd.metadata.name)));
  }
  if (options.deleteNamespaces) {
    debug("Deleting kyma namespaces");
    await kubectlDelete(join(__dirname, "system-namespaces.yaml"));
  }

  const usualLeftovers = [
    '/api/v1/namespaces/kyma-system/secrets',
    '/apis/oathkeeper.ory.sh/v1alpha1/namespaces/kyma-system/rules',
    '/apis/rafter.kyma-project.io/v1beta1/clusterassets',
    '/apis/rafter.kyma-project.io/v1beta1/clusterbuckets',
  ]
  await Promise.allSettled(usualLeftovers.map(path => deleteAllK8sResources(path)));
  const skipIstio = (skipComponents && skipComponents.includes("istio")) || (components && !components.includes("istio"));

  if (!skipIstio) {
    debug("Uninstalling istio");
    await uninstallIstio();
  }
}

/**
 * Install Kyma on kubernetes cluster with current kubeconfig
 * @param {Object} options List of installation options
 * @param {string} options.resourcesPath Path to the resources folder with Kyma charts
 * @param {string} options.istioVersion Istio version, eg. 1.8.2
 * @param {boolean} options.isUpgrade Upgrade existing installation
 * @param {boolean} options.withCompass
 * @param {boolean} options.withCentralAppConnectivity Install Application Connectivity as single cluster-wide instances
 * @param {Array<string>} options.skipComponents List of components to not install
 * @param {Array<string>} options.components List of components to install
 * @param {boolean} options.useHelmTemplate Use "helm template | kubectl apply" instead of helm install/upgrade
 */
async function installKyma(options) {
  options = options || {};
  const installLocation = options.resourcesPath || join(__dirname, "..", "..", "..", "resources");
  const crdLocation = options.resourcesPath || join(__dirname, "..", "..", "..", "installation", "resources", "crds");
  const istioVersion = options.istioVersion || defaultIstioVersion;
  const isUpgrade = options.isUpgrade || false;
  const skipComponents = options.skipComponents || [];
  const components = options.components;
  console.time('Installation');
  console.log('Installing Kyma from folder', installLocation);
  await waitForNodesToBeReady();
  const crdsBefore = await getAllCRDs();
  const skipIstio = skipComponents.includes("istio") || (components && !components.includes("istio"));

  if (!skipIstio) {
    if (options.isUpgrade) {
      await upgradeIstio(istioVersion);
    } else {
      await updateCoreDNSConfigMap();
      await installIstio(istioVersion);
    }
  }
  console.timeLog('Installation', 'Istio installed');
  await removeKymaGatewayCertsYaml(installLocation);
  await kubectlApply(join(__dirname, "system-namespaces.yaml"));
  if (options.withCompass) {
    await kubectlApply(join(__dirname, "compass-namespace.yaml"));
  }
  await kubectlApplyDir(crdLocation, "kyma-system");
  let kymaCharts = await chartList(options);
  if (components) {
    kymaCharts = kymaCharts.filter(c => components.includes(c.release));
  }
  if (skipComponents) {
    kymaCharts = kymaCharts.filter(c => !skipComponents.includes(c.release))
  }
  const installFn = options.useHelmTemplate ? applyRelease : installRelease;
  await Promise.all(
    kymaCharts.map(({ release, namespace, values, customPath, profile }) => {
      const chartLocation = !!customPath
        ? customPath(installLocation)
        : join(installLocation, release);
      return pRetry(
        async () =>
          installFn(release, namespace, chartLocation, values, profile || "evaluation", isUpgrade),
        {
          retries: 10,
          onFailedAttempt: async (err) => {
            console.log(`retrying install of ${release}`);
            console.log(err);
          },
        }
      );
    })
  );
  const crdsAfter = await getAllCRDs();
  const installedCrds = crdsAfter.filter(crd => !crdsBefore.some(c => c.metadata.name == crd.metadata.name));
  debug("Installed crds:")
  debug(installedCrds.map(crd => crd.metadata.name));
  console.timeEnd('Installation');
}

module.exports = {
  installKyma,
  uninstallKyma,
  ...downloader
};
<commit_msg>Use kyma TPI kaniko-executer image (#12293)<commit_after>const downloader = require("./chart-downloader");
const {
  helmInstallUpgrade,
  helmStatus,
  helmUninstall,
  helmList,
  helmTemplate
} = require("./helm");
const execa = require("execa");
const { join } = require("path");
const { installIstio, upgradeIstio } = require("./istioctl");
const pRetry = require("p-retry");
const k8s = require("@kubernetes/client-node");
const {
  debug,
  k8sCoreV1Api,
  kubectlDelete,
  kubectlApplyDir,
  kubectlApply,
  k8sDelete,
  deleteAllK8sResources,
  deleteNamespaces,
  getAllCRDs,
  k8sApply,
  waitForDeployment,
  waitForStatefulSet
} = require("../utils");
const notDeployed = "not-deployed";
const kymaCrds = require("./kyma-crds");
const defaultIstioVersion = "1.10.0";

async function waitForNodesToBeReady(timeout = "180s") {
  await execa("kubectl", [
    "wait",
    "--for=condition=Ready",
    "nodes",
    "--all",
    `--timeout=${timeout}`,
  ]);
  debug("All nodes are ready!");
}

async function updateCoreDNSConfigMap() {

  const cmName = "coredns";
  const cmNamespace = "kube-system";

  const { body } = await k8sCoreV1Api.readNamespacedConfigMap(
    "coredns",
    "kube-system"
  );

  const { stdout: registryIP } = await execa("docker", [
    "inspect",
    "-f",
    "{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}",
    "/registry.localhost",
  ]).catch(() => "127.0.0.1");

  // this file need to be updated when we update k3s to use k8s 1.20
  body.data["Corefile"] = `registry.localhost:53 {
    hosts {
        ${registryIP} registry.localhost
    }
}
.:53 {
    errors
    health
    rewrite name regex (.*)\.local\.kyma\.dev istio-ingressgateway.istio-system.svc.cluster.local
    ready
    kubernetes cluster.local in-addr.arpa ip6.arpa {
      pods insecure
      upstream
      fallthrough in-addr.arpa ip6.arpa
    }
    hosts /etc/coredns/NodeHosts {
      reload 1s
      fallthrough
    }
    prometheus :9153
    forward . /etc/resolv.conf
    cache 30
    loop
    reload
    loadbalance
}
`;
  await k8sCoreV1Api.replaceNamespacedConfigMap(cmName, cmNamespace, body);
}

async function removeKymaGatewayCertsYaml(pathToResources) {
  // TODO: find some other mechanism, deleting that file is destructive
  // we can copy resources directory so that we can act on it
  try {
    await execa("rm", [
      join(
        pathToResources,
        "core/charts/gateway/templates/kyma-gateway-certs.yaml"
      ),
    ]);
  } catch (err) {
    console.log("kyma-gateway-certs.yaml already deleted");
  }
}
function skipHelmTest(r) {
  return !(r.metadata.annotations && r.metadata.annotations["helm.sh/hook"] && r.metadata.annotations["helm.sh/hook"].includes("test-success"));
}
function skipNull(r) {
  return (r != null);
}

async function applyRelease(
  release,
  namespace = "kyma-system",
  chart,
  values,
  profile,
  isUpgrade
) {
  const { stdout } = await helmTemplate(release, chart, namespace, values, profile);
  const yamls = k8s.loadAllYaml(stdout).filter(skipNull).filter(skipHelmTest);
  const deployments = yamls.filter((r) => r.kind === 'Deployment');
  const statefulsets = yamls.filter((r) => r.kind === 'StatefulSet');
  await k8sApply(yamls, namespace);
  for (let deployment of deployments) {
    console.log("Waitng for deployment: ", deployment)
    await waitForDeployment(deployment.metadata.name, deployment.metadata.namespace || namespace);
  }
  for (let ss of statefulsets) {
    console.log("Waitng for StatefulSet: ", ss)
    await waitForStatefulSet(ss.metadata.name, ss.metadata.namespace || namespace);
  }
}

async function installRelease(
  release,
  namespace = "kyma-system",
  chart,
  values,
  profile,
  isUpgrade
) {

  let status = await helmStatus(release, namespace);
  switch (status) {
    case "pending-install":
      debug(
        `Deleting ${release} from namespace ${namespace} because previous installation got stuck in pending-install`
      );
      await helmUninstall(release, namespace);
      break;
    case "failed":
      debug(
        `Deleting ${release} from namespace ${namespace} because previous installation failed`
      );
      await helmUninstall(release, namespace);
      break;
    case notDeployed:
      debug(`Installing release ${release}`);
      await helmInstallUpgrade(release, chart, namespace, values, profile);
      break;
    case "deployed":
      if (isUpgrade) {
        debug(`Upgrading release ${release}`);
        await helmInstallUpgrade(release, chart, namespace, values, profile);

      } else {
        debug(`Release ${release} already installed - skipped`);
        return;
      }
    default:
      break;
  }

  status = await helmStatus(release, namespace);
  if (status !== "deployed") {
    throw new Error(
      `Release ${release} is in status ${status}, which is not "deployed"`
    );
  }
  debug(`Release ${release} deployed`);
}

async function chartList(options) {
  const gardenerDomain = await getGardenerDomain();
  const domain = process.env["KYMA_DOMAIN"] || gardenerDomain || "local.kyma.dev";

  const isGardener = (gardenerDomain ? true : false) || (process.env["GARDENER"] === "true");
  const isCompassEnabled = !!options.withCompass;
  const isCentralApplicationConnectivityEnabled = !!options.withCentralAppConnectivity;

  const overrides = [
    `authProxy.config.useDex=false`,
    `global.centralApplicationConnectivityValidatorEnabled=${isCentralApplicationConnectivityEnabled}`,
    `central_application_gateway.enabled=${isCentralApplicationConnectivityEnabled}`,
    `global.disableLegacyConnectivity=${isCompassEnabled}`,
    `global.domainName=${domain}`,
    `global.environment.gardener=${isGardener}`,
    `global.isLocalEnv=false`,
    `global.domainName=${domain}`,
    `global.tlsCrt=ZHVtbXkK`
  ].join(',');

  // https://github.com/kyma-project/test-infra/pull/2967
  let registryOverrides;
  if (isGardener == true) {
    registryOverrides = [
      `dockerRegistry.enableInternal=true`,
      `global.domainName=${domain}`
    ].join(',');
  } else {
    registryOverrides = [
      `dockerRegistry.enableInternal=false`,
      `dockerRegistry.registryAddress=registry.localhost:5000`,
      `dockerRegistry.serverAddress=registry.localhost:5000`,
      `global.domainName=${domain}`
    ].join(',');
  }
  const kialiOverrides = overrides + ',kcproxy.enabled=false,virtualservice.enabled=false';
  const tracingOverrides = overrides + ',kcproxy.enabled=false,virtualservice.enabled=false';

  const kymaCharts = [
    {
      release: "pod-preset",
      namespace: "kyma-system",
      customPath: (root) =>
        join(root, "cluster-essentials", "charts", "pod-preset"),
    },
    {
      release: `cluster-users`,
      namespace: "kyma-system",
      values: `${overrides}`,
    },
    {
      release: "core",
      namespace: "kyma-system",
      values: `${overrides}`,
    },
    {
      release: "ory",
      namespace: "kyma-system",
      values: `${overrides}`,
    },
    {
      release: "serverless",
      namespace: "kyma-system",
      values: registryOverrides,
    },
    {
      release: "api-gateway",
      namespace: "kyma-system",
      values: `${overrides},deployment.resources.requests.cpu=10m`,
    },
    {
      release: "rafter",
      namespace: "kyma-system",
      values: `${overrides}`,
    },
    {
      release: "service-catalog",
      namespace: "kyma-system",
      values: `${overrides}`,
    },
    {
      release: "service-catalog-addons",
      namespace: "kyma-system",
      values: `${overrides}`,
    },
    {
      release: "helm-broker",
      namespace: "kyma-system",
      values: `${overrides}`,
    },
    {
      release: "eventing",
      namespace: "kyma-system",
      values: `${overrides}`
    },
    {
      release: "application-connector",
      namespace: "kyma-integration",
      values: `${overrides}`,
    },
    {
      release: "monitoring",
      namespace: "kyma-system",
      values: `${overrides}`,
    },
    {
      release: "kiali",
      namespace: "kyma-system",
      values: `${kialiOverrides}`,
    },
    {
      release: "tracing",
      namespace: "kyma-system",
      values: `${tracingOverrides}`,
    },
    {
      release: "logging",
      namespace: "kyma-system",
      values: `${overrides}`,
    },
    {
      release: "compass-runtime-agent",
      namespace: "compass-system",
      values: `${overrides}`,
      filter: isCompassEnabled
    },
    {
      release: "ingress-dns-cert",
      namespace: "istio-system",
      values: `global.domainName=${domain},global.environment.gardener=${isGardener}`,
      customPath: () => join(__dirname, "charts", "ingress-dns-cert"),
    },
  ];

  return kymaCharts.filter(c => c.filter == undefined || c.filter);
}

async function getGardenerDomain() {
  try {
    const { body } = await k8sCoreV1Api.readNamespacedConfigMap(
      "shoot-info",
      "kube-system"
    );
    return body.data.domain;
  } catch (err) {
    if (err.statusCode == 404) {
      return null;
    }
    throw err;
  }
}
async function uninstallIstio() {
  const crds = await getAllCRDs();
  const istioCRDs = crds.filter(crd => crd.spec.group.endsWith('istio.io'));
  await k8sDelete(istioCRDs);
  await deleteNamespaces(["istio-system"], true);
}
/**
 *
 * Uninstalls Kyma
 * @param {Object} options Uninstallation options
 * @param {boolean} options.skipCrd Do not delete CRDs
 * @param {Array<string>} options.skipComponents List of components that should not be uninstalled
 * @param {Array<string>} options.components List of components to uninstall
 * @param {boolean} options.deleteNamespaces
 */
async function uninstallKyma(options) {
  let releases = await helmList();
  debug("Releases in the cluster:", releases);
  const components = options.components;
  const skipComponents = options.skipComponents;
  if (components) {
    releases = releases.filter(r => components.includes(r.name))
  }
  if (skipComponents) {
    releases = releases.filter(r => !skipComponents.includes(r.name))
  }
  debug("Releases to uninstall:", releases);

  await Promise.allSettled(releases.map((r) => helmUninstall(r.name, r.namespace).catch(() => {
    // ignore errors during uninstall ()
  })));

  await kubectlDelete(join(__dirname, "installer-local.yaml")); // needed for the console to start
  if (!options.skipCrd) {
    const crds = await getAllCRDs();
    await k8sDelete(crds.filter(crd => kymaCrds.includes(crd.metadata.name)));
  }
  if (options.deleteNamespaces) {
    debug("Deleting kyma namespaces");
    await kubectlDelete(join(__dirname, "system-namespaces.yaml"));
  }

  const usualLeftovers = [
    '/api/v1/namespaces/kyma-system/secrets',
    '/apis/oathkeeper.ory.sh/v1alpha1/namespaces/kyma-system/rules',
    '/apis/rafter.kyma-project.io/v1beta1/clusterassets',
    '/apis/rafter.kyma-project.io/v1beta1/clusterbuckets',
  ]
  await Promise.allSettled(usualLeftovers.map(path => deleteAllK8sResources(path)));
  const skipIstio = (skipComponents && skipComponents.includes("istio")) || (components && !components.includes("istio"));

  if (!skipIstio) {
    debug("Uninstalling istio");
    await uninstallIstio();
  }
}

/**
 * Install Kyma on kubernetes cluster with current kubeconfig
 * @param {Object} options List of installation options
 * @param {string} options.resourcesPath Path to the resources folder with Kyma charts
 * @param {string} options.istioVersion Istio version, eg. 1.8.2
 * @param {boolean} options.isUpgrade Upgrade existing installation
 * @param {boolean} options.withCompass
 * @param {boolean} options.withCentralAppConnectivity Install Application Connectivity as single cluster-wide instances
 * @param {Array<string>} options.skipComponents List of components to not install
 * @param {Array<string>} options.components List of components to install
 * @param {boolean} options.useHelmTemplate Use "helm template | kubectl apply" instead of helm install/upgrade
 */
async function installKyma(options) {
  options = options || {};
  const installLocation = options.resourcesPath || join(__dirname, "..", "..", "..", "resources");
  const crdLocation = options.resourcesPath || join(__dirname, "..", "..", "..", "installation", "resources", "crds");
  const istioVersion = options.istioVersion || defaultIstioVersion;
  const isUpgrade = options.isUpgrade || false;
  const skipComponents = options.skipComponents || [];
  const components = options.components;
  console.time('Installation');
  console.log('Installing Kyma from folder', installLocation);
  await waitForNodesToBeReady();
  const crdsBefore = await getAllCRDs();
  const skipIstio = skipComponents.includes("istio") || (components && !components.includes("istio"));

  if (!skipIstio) {
    if (options.isUpgrade) {
      await upgradeIstio(istioVersion);
    } else {
      await updateCoreDNSConfigMap();
      await installIstio(istioVersion);
    }
  }
  console.timeLog('Installation', 'Istio installed');
  await removeKymaGatewayCertsYaml(installLocation);
  await kubectlApply(join(__dirname, "system-namespaces.yaml"));
  if (options.withCompass) {
    await kubectlApply(join(__dirname, "compass-namespace.yaml"));
  }
  await kubectlApplyDir(crdLocation, "kyma-system");
  let kymaCharts = await chartList(options);
  if (components) {
    kymaCharts = kymaCharts.filter(c => components.includes(c.release));
  }
  if (skipComponents) {
    kymaCharts = kymaCharts.filter(c => !skipComponents.includes(c.release))
  }
  const installFn = options.useHelmTemplate ? applyRelease : installRelease;
  await Promise.all(
    kymaCharts.map(({ release, namespace, values, customPath, profile }) => {
      const chartLocation = !!customPath
        ? customPath(installLocation)
        : join(installLocation, release);
      return pRetry(
        async () =>
          installFn(release, namespace, chartLocation, values, profile || "evaluation", isUpgrade),
        {
          retries: 10,
          onFailedAttempt: async (err) => {
            console.log(`retrying install of ${release}`);
            console.log(err);
          },
        }
      );
    })
  );
  const crdsAfter = await getAllCRDs();
  const installedCrds = crdsAfter.filter(crd => !crdsBefore.some(c => c.metadata.name == crd.metadata.name));
  debug("Installed crds:")
  debug(installedCrds.map(crd => crd.metadata.name));
  console.timeEnd('Installation');
}

module.exports = {
  installKyma,
  uninstallKyma,
  ...downloader
};
<|endoftext|>


<commit_before>
var fs = require('fs')

module.exports = function writeDatabaseSync(fileName, obj) {
  var fd = fs.openSync(fileName, 'w')
  var keys = Object.keys(obj).sort()

  fs.writeSync(fd, '{\n');

  keys.forEach(function (key, i, arr) {
    fs.writeSync(fd, '  ' + JSON.stringify(key) + ': {\n');

    var end = endLine.apply(this, arguments)
    var data = obj[key]
    var keys = Object.keys(data).sort(sortDataKeys)

    keys.forEach(function (key, i, arr) {
      var end = endLine.apply(this, arguments)
      fs.writeSync(fd, '    ' + JSON.stringify(key) + ': ' + JSON.stringify(data[key]) + end);
    })

    fs.writeSync(fd, '  }' + end);
  })

  fs.writeSync(fd, '}\n');

  fs.closeSync(fd);
};

function endLine(key, i, arr) {
  var comma = i + 1 === arr.length
    ? ''
    : ','
  return comma + '\n'
}

function sortDataKeys(a, b) {
  var cmp = a.localeCompare(b)

  return a !== 'source' || !cmp
    ? cmp
    : -1
}
<commit_msg>build: do not write undefined to db<commit_after>
var fs = require('fs')

module.exports = function writeDatabaseSync(fileName, obj) {
  var fd = fs.openSync(fileName, 'w')
  var keys = Object.keys(obj).sort()

  fs.writeSync(fd, '{\n');

  keys.forEach(function (key, i, arr) {
    fs.writeSync(fd, '  ' + JSON.stringify(key) + ': {\n');

    var end = endLine.apply(this, arguments)
    var data = obj[key]
    var keys = Object.keys(data).sort(sortDataKeys)

    keys.forEach(function (key, i, arr) {
      var end = endLine.apply(this, arguments)
      var val = data[key]

      if (val !== undefined) {
        fs.writeSync(fd, '    ' + JSON.stringify(key) + ': ' + JSON.stringify(val) + end);
      }
    })

    fs.writeSync(fd, '  }' + end);
  })

  fs.writeSync(fd, '}\n');

  fs.closeSync(fd);
};

function endLine(key, i, arr) {
  var comma = i + 1 === arr.length
    ? ''
    : ','
  return comma + '\n'
}

function sortDataKeys(a, b) {
  var cmp = a.localeCompare(b)

  return a !== 'source' || !cmp
    ? cmp
    : -1
}
<|endoftext|>


<commit_before>// ==========================================================================
// Project:   SproutCore - JavaScript Application Framework
// Copyright: ©2006-2009 Sprout Systems, Inc. and contributors.
//            Portions ©2008-2009 Apple Inc. All rights reserved.
// License:   Licened under MIT license (see license.js)
// ==========================================================================

/** @namespace

  This mixin implements many of the basic state-handling attributes for
  button-like views, including an auto-updated title, and mapping the current
  value to an isSelected state.

  Usually you will not work with this mixin directly.  Instead, you should use
  a class that incorporates the mixin such as SC.ButtonView, SC.CheckboxView
  or SC.RadioView.

  This mixin assumes you have already applied the SC.Control and
  SC.DelegateSupport mixins as well.

  @since SproutCore 1.0
*/
SC.Button = {

  // ..........................................................
  // VALUE PROPERTIES
  //

  /**
    Used to automatically update the state of the button view for toggle style
    buttons.

    for toggle style buttons, you can set the value and it will be used to
    update the isSelected state of the button view.  The value will also
    change as the user selects or deselects.  You can control which values
    the button will treat as isSelected by setting the toggleOnValue and
    toggleOffValue.  Alternatively, if you leave these properties set to
    YES or NO, the button will do its best to convert a value to an
    appropriate state:

    - null, false, 0  -> isSelected = false
    - any other single value -> isSelected = true
    - array -> if all values are the same state: that state.  otherwise MIXED.

    @property {Object}
  */
  value: null,

  /**
    Value of a selected toggle button.

    for a toggle button, set this to any object value you want.  The button
    will be selected if the value property equals the targetValue.  If the
    value is an array of multiple items that contains the targetValue, then
    the button will be set to a mixed state.

    default is YES

    @property {Object}
  */
  toggleOnValue: YES,

  /**
    Value of an unselected toggle button.

    For a toggle button, set this to any object value you want.  When the
    user toggle's the button off, the value of the button will be set to this
    value.

    default is NO

    @property {Object}
  */
  toggleOffValue: NO,

  // ..........................................................
  // TITLE
  //

  /**
    If YES, then the title will be localized.

    @property {Boolean}
  */
  localize: NO,

  /** @private */
  localizeBindingDefault: SC.Binding.bool(),

  /**
    The button title.  If localize is YES, then this should be the localization key to display.  Otherwise, this will be the actual string displayed in the title.  This property is observable and bindable.

    @property {String}
  */
  title: '',

  /**
    If you set this property, the title property will be updated automatically
    from the content using the key you specify.

    @property {String}
  */
  contentTitleKey: null,

  /**
    The button icon.  Set this to either a URL or a CSS class name (for
    spriting).  Note that if you pass a URL, it must contain at
    least one slash to be detected as such.

    @property {String}
  */
  icon: null,

  /**
    If you set this property, the icon will be updated automatically from the
    content using the key you specify.

    @property {String}
  */
  contentIconKey: null,

  /**
    If YES, button will attempt to display an ellipsis if the title cannot
    fit inside of the visible area.  This feature is not available on all
    browsers.

    @property {Boolean}
  */
  needsEllipsis: YES,

  /**
    The computed display title.  This is generated by localizing the title
    property if necessary.

    @property {String}
  */
  displayTitle: function() {
    var ret = this.get('title');
    return (ret && this.get('localize')) ? ret.loc() : (ret || '');
  }.property('title','localize').cacheable(),

  /**
    The key equivalent that should trigger this button on the page.

    @property {String}
  */
  keyEquivalent: null,

  // ..........................................................
  // METHODS
  //

  /**
    Classes that include this mixin can invoke this method from their
    render method to render the proper title HTML.  This will include an
    icon if necessary along with any other standard markup.

    @param {SC.RenderContext} context the context to render
    @param {Boolean} firstTime YES if first time rendering
    @returns {SC.RenderContext} the context
  */
  renderTitle: function(context, firstTime) {
    var icon = this.get('icon'),
        image = '' ,
        title = this.get('displayTitle') ,
        needsTitle = (!SC.none(title) && title.length>0),
        elem, htmlNode, imgTitle;
    // get the icon.  If there is an icon, then get the image and update it.
    // if there is no image element yet, create it and insert it just before
    // title.
    if (icon) {
      var blank = SC.BLANK_IMAGE_URL;

      if (icon.indexOf('/') >= 0) {
        image = '';
      } else {
        image = '';
      }
      needsTitle = YES ;
    }
    imgTitle = image + title;
    if(firstTime){
      if(this.get('needsEllipsis')){
        context.push(''+imgTitle+'');
      }else{
          context.push(''+imgTitle+'');
      }
      this._ImageTitleCached = imgTitle;
    }else{
      elem = this.$('label');
      if ( (htmlNode = elem[0])){
        if(needsTitle) {
          if(this.get('needsEllipsis')){
            elem.addClass('ellipsis');
            if(this._ImageTitleCached !== imgTitle) htmlNode.innerHTML = imgTitle;
          }else{
            elem.removeClass('ellipsis');
            if(this._ImageTitleCached !== imgTitle) htmlNode.innerHTML = imgTitle;
          }
        }
        else { htmlNode.innerHTML = ''; }
      }
    }
    return context ;
  },

  /**
    Updates the value, title, and icon keys based on the content object, if
    set.

    @property {Object} target the target of the object that changed
    @property {String} key name of property that changed
    @returns {SC.Button} receiver
  */
  contentPropertyDidChange: function(target, key) {
    var del = this.get('displayDelegate'),
        content = this.get('content'), value ;

    var valueKey = this.getDelegateProperty('contentValueKey', del) ;
    if (valueKey && (key === valueKey || key === '*')) {
      this.set('value', content ? content.get(valueKey) : null) ;
    }

    var titleKey = this.getDelegateProperty('contentTitleKey', del) ;
    if (titleKey && (key === titleKey || key === '*')) {
      this.set('title', content ? content.get(titleKey) : null) ;
    }

    var iconKey = this.getDelegateProperty('contentIconKey', del);
    if (iconKey && (key === iconKey || key === '*')) {
      this.set('icon', content ? content.get(iconKey) : null) ;
    }

    return this ;
  },

  /** @private - when title changes, dirty display. */
  _button_displayObserver: function() {
    this.displayDidChange();
  }.observes('title', 'icon', 'value'),

  /**
    Handle a key equivalent if set.  Trigger the default action for the
    button.  Depending on the implementation this may vary.

    @param {String} keystring
    @param {SC.Event} evt
    @returns {Boolean}  YES if handled, NO otherwise
  */
  performKeyEquivalent: function(keystring, evt) {

    if (!this.get('isEnabled')) return NO;
    var equiv = this.get('keyEquivalent');

    // button has defined a keyEquivalent and it matches!
    // if triggering succeeded, true will be returned and the operation will
    // be handeled (i.e performKeyEquivalent will cease crawling the view
    // tree)
    if (equiv) {
      if (equiv === keystring) return this.triggerAction(evt);

    // should fire if isDefault OR isCancel.  This way if isDefault AND
    // isCancel, responds to both return and escape
    } else if ((this.get('isDefault') && (keystring === 'return')) ||
        (this.get('isCancel') && (keystring === 'escape'))) {
          return this.triggerAction(evt);
    }

    return NO; // did not handle it; keep searching
  },

  /**
    Your class should implement this method to perform the default action on
    the button.  This is used to implement keyboard control.  Your button
    may make this change in its own way also.

    @property {SC.Event} evt the event
    @returns {void}
  */
  triggerAction: function(evt) {
    throw "SC.Button.triggerAction() is not defined in %@".fmt(this);
  },

  // ..........................................................
  // VALUE <-> isSelected STATE MANAGEMNT
  //

  /**
    This is the standard logic to compute a proposed isSelected state for a
    new value.  This takes into account the toggleOnValue/toggleOffValue
    properties, among other things.  It may return YES, NO, or SC.MIXED_STATE.

    @param {Object} value
    @returns {Boolean} return state
  */
  computeIsSelectedForValue: function(value) {
    var targetValue = this.get('toggleOnValue'), state, next ;

    if (SC.typeOf(value) === SC.T_ARRAY) {

      // treat a single item array like a single value
      if (value.length === 1) {
        state = (value[0] == targetValue) ;

      // for a multiple item array, check the states of all items.
      } else {
        state = null;
        value.find(function(x) {
          next = (x == targetValue) ;
          if (state === null) {
            state = next ;
          } else if (next !== state) state = SC.MIXED_STATE ;
          return state === SC.MIXED_STATE ; // stop when we hit a mixed state.
        });
      }

    // for single values, just compare to the toggleOnValue...use truthiness
    } else {
      if(value === SC.MIXED_STATE) state = SC.MIXED_STATE;
      else state = (value == targetValue) ;
    }
    return state ;
  },

  /** @ignore */
  initMixin: function() {
    // if value is not null, update isSelected to match value.  If value is
    // null, we assume you may be using isSelected only.
    if (!SC.none(this.get('value'))) this._button_valueDidChange();
  },

  /** @private
    Whenever the button value changes, update the selected state to match.
  */
  _button_valueDidChange: function() {
    var value = this.get('value'),
        state = this.computeIsSelectedForValue(value);
    this.set('isSelected', state) ; // set new state...
  }.observes('value'),

  /** @private
    Whenever the selected state is changed, make sure the button value is also updated.  Note that this may be called because the value has just changed.  In that case this should do nothing.
  */
  _button_isSelectedDidChange: function() {
    var newState = this.get('isSelected'),
        curState = this.computeIsSelectedForValue(this.get('value'));

    // fix up the value, but only if computed state does not match.
    // never fix up value if isSelected is set to MIXED_STATE since this can
    // only come from the value.
    if ((newState !== SC.MIXED_STATE) && (curState !== newState)) {
      var valueKey = (newState) ? 'toggleOnValue' : 'toggleOffValue' ;
      this.set('value', this.get(valueKey));
    }
  }.observes('isSelected')

} ;
<commit_msg>Updating the title cache when it is out of sync<commit_after>// ==========================================================================
// Project:   SproutCore - JavaScript Application Framework
// Copyright: ©2006-2009 Sprout Systems, Inc. and contributors.
//            Portions ©2008-2009 Apple Inc. All rights reserved.
// License:   Licened under MIT license (see license.js)
// ==========================================================================

/** @namespace

  This mixin implements many of the basic state-handling attributes for
  button-like views, including an auto-updated title, and mapping the current
  value to an isSelected state.

  Usually you will not work with this mixin directly.  Instead, you should use
  a class that incorporates the mixin such as SC.ButtonView, SC.CheckboxView
  or SC.RadioView.

  This mixin assumes you have already applied the SC.Control and
  SC.DelegateSupport mixins as well.

  @since SproutCore 1.0
*/
SC.Button = {

  // ..........................................................
  // VALUE PROPERTIES
  //

  /**
    Used to automatically update the state of the button view for toggle style
    buttons.

    for toggle style buttons, you can set the value and it will be used to
    update the isSelected state of the button view.  The value will also
    change as the user selects or deselects.  You can control which values
    the button will treat as isSelected by setting the toggleOnValue and
    toggleOffValue.  Alternatively, if you leave these properties set to
    YES or NO, the button will do its best to convert a value to an
    appropriate state:

    - null, false, 0  -> isSelected = false
    - any other single value -> isSelected = true
    - array -> if all values are the same state: that state.  otherwise MIXED.

    @property {Object}
  */
  value: null,

  /**
    Value of a selected toggle button.

    for a toggle button, set this to any object value you want.  The button
    will be selected if the value property equals the targetValue.  If the
    value is an array of multiple items that contains the targetValue, then
    the button will be set to a mixed state.

    default is YES

    @property {Object}
  */
  toggleOnValue: YES,

  /**
    Value of an unselected toggle button.

    For a toggle button, set this to any object value you want.  When the
    user toggle's the button off, the value of the button will be set to this
    value.

    default is NO

    @property {Object}
  */
  toggleOffValue: NO,

  // ..........................................................
  // TITLE
  //

  /**
    If YES, then the title will be localized.

    @property {Boolean}
  */
  localize: NO,

  /** @private */
  localizeBindingDefault: SC.Binding.bool(),

  /**
    The button title.  If localize is YES, then this should be the localization key to display.  Otherwise, this will be the actual string displayed in the title.  This property is observable and bindable.

    @property {String}
  */
  title: '',

  /**
    If you set this property, the title property will be updated automatically
    from the content using the key you specify.

    @property {String}
  */
  contentTitleKey: null,

  /**
    The button icon.  Set this to either a URL or a CSS class name (for
    spriting).  Note that if you pass a URL, it must contain at
    least one slash to be detected as such.

    @property {String}
  */
  icon: null,

  /**
    If you set this property, the icon will be updated automatically from the
    content using the key you specify.

    @property {String}
  */
  contentIconKey: null,

  /**
    If YES, button will attempt to display an ellipsis if the title cannot
    fit inside of the visible area.  This feature is not available on all
    browsers.

    @property {Boolean}
  */
  needsEllipsis: YES,

  /**
    The computed display title.  This is generated by localizing the title
    property if necessary.

    @property {String}
  */
  displayTitle: function() {
    var ret = this.get('title');
    return (ret && this.get('localize')) ? ret.loc() : (ret || '');
  }.property('title','localize').cacheable(),

  /**
    The key equivalent that should trigger this button on the page.

    @property {String}
  */
  keyEquivalent: null,

  // ..........................................................
  // METHODS
  //

  /**
    Classes that include this mixin can invoke this method from their
    render method to render the proper title HTML.  This will include an
    icon if necessary along with any other standard markup.

    @param {SC.RenderContext} context the context to render
    @param {Boolean} firstTime YES if first time rendering
    @returns {SC.RenderContext} the context
  */
  renderTitle: function(context, firstTime) {
    var icon = this.get('icon'),
        image = '' ,
        title = this.get('displayTitle') ,
        needsTitle = (!SC.none(title) && title.length>0),
        elem, htmlNode, imgTitle;
    // get the icon.  If there is an icon, then get the image and update it.
    // if there is no image element yet, create it and insert it just before
    // title.
    if (icon) {
      var blank = SC.BLANK_IMAGE_URL;

      if (icon.indexOf('/') >= 0) {
        image = '';
      } else {
        image = '';
      }
      needsTitle = YES ;
    }
    imgTitle = image + title;
    if(firstTime){
      if(this.get('needsEllipsis')){
        context.push(''+imgTitle+'');
      }else{
          context.push(''+imgTitle+'');
      }
      this._ImageTitleCached = imgTitle;
    }else{
      elem = this.$('label');
      if ( (htmlNode = elem[0])){
        if(needsTitle) {
          if(this.get('needsEllipsis')){
            elem.addClass('ellipsis');
            if(this._ImageTitleCached !== imgTitle) {
              this._ImageTitleCached = imgTitle; // Update the cache
              htmlNode.innerHTML = imgTitle;
            }
          }else{
            elem.removeClass('ellipsis');
            if(this._ImageTitleCached !== imgTitle) {
              this._ImageTitleCached = imgTitle; // Update the cache
              htmlNode.innerHTML = imgTitle;
            }
          }
        }
        else { htmlNode.innerHTML = ''; }
      }
    }
    return context ;
  },

  /**
    Updates the value, title, and icon keys based on the content object, if
    set.

    @property {Object} target the target of the object that changed
    @property {String} key name of property that changed
    @returns {SC.Button} receiver
  */
  contentPropertyDidChange: function(target, key) {
    var del = this.get('displayDelegate'),
        content = this.get('content'), value ;

    var valueKey = this.getDelegateProperty('contentValueKey', del) ;
    if (valueKey && (key === valueKey || key === '*')) {
      this.set('value', content ? content.get(valueKey) : null) ;
    }

    var titleKey = this.getDelegateProperty('contentTitleKey', del) ;
    if (titleKey && (key === titleKey || key === '*')) {
      this.set('title', content ? content.get(titleKey) : null) ;
    }

    var iconKey = this.getDelegateProperty('contentIconKey', del);
    if (iconKey && (key === iconKey || key === '*')) {
      this.set('icon', content ? content.get(iconKey) : null) ;
    }

    return this ;
  },

  /** @private - when title changes, dirty display. */
  _button_displayObserver: function() {
    this.displayDidChange();
  }.observes('title', 'icon', 'value'),

  /**
    Handle a key equivalent if set.  Trigger the default action for the
    button.  Depending on the implementation this may vary.

    @param {String} keystring
    @param {SC.Event} evt
    @returns {Boolean}  YES if handled, NO otherwise
  */
  performKeyEquivalent: function(keystring, evt) {

    if (!this.get('isEnabled')) return NO;
    var equiv = this.get('keyEquivalent');

    // button has defined a keyEquivalent and it matches!
    // if triggering succeeded, true will be returned and the operation will
    // be handeled (i.e performKeyEquivalent will cease crawling the view
    // tree)
    if (equiv) {
      if (equiv === keystring) return this.triggerAction(evt);

    // should fire if isDefault OR isCancel.  This way if isDefault AND
    // isCancel, responds to both return and escape
    } else if ((this.get('isDefault') && (keystring === 'return')) ||
        (this.get('isCancel') && (keystring === 'escape'))) {
          return this.triggerAction(evt);
    }

    return NO; // did not handle it; keep searching
  },

  /**
    Your class should implement this method to perform the default action on
    the button.  This is used to implement keyboard control.  Your button
    may make this change in its own way also.

    @property {SC.Event} evt the event
    @returns {void}
  */
  triggerAction: function(evt) {
    throw "SC.Button.triggerAction() is not defined in %@".fmt(this);
  },

  // ..........................................................
  // VALUE <-> isSelected STATE MANAGEMNT
  //

  /**
    This is the standard logic to compute a proposed isSelected state for a
    new value.  This takes into account the toggleOnValue/toggleOffValue
    properties, among other things.  It may return YES, NO, or SC.MIXED_STATE.

    @param {Object} value
    @returns {Boolean} return state
  */
  computeIsSelectedForValue: function(value) {
    var targetValue = this.get('toggleOnValue'), state, next ;

    if (SC.typeOf(value) === SC.T_ARRAY) {

      // treat a single item array like a single value
      if (value.length === 1) {
        state = (value[0] == targetValue) ;

      // for a multiple item array, check the states of all items.
      } else {
        state = null;
        value.find(function(x) {
          next = (x == targetValue) ;
          if (state === null) {
            state = next ;
          } else if (next !== state) state = SC.MIXED_STATE ;
          return state === SC.MIXED_STATE ; // stop when we hit a mixed state.
        });
      }

    // for single values, just compare to the toggleOnValue...use truthiness
    } else {
      if(value === SC.MIXED_STATE) state = SC.MIXED_STATE;
      else state = (value == targetValue) ;
    }
    return state ;
  },

  /** @ignore */
  initMixin: function() {
    // if value is not null, update isSelected to match value.  If value is
    // null, we assume you may be using isSelected only.
    if (!SC.none(this.get('value'))) this._button_valueDidChange();
  },

  /** @private
    Whenever the button value changes, update the selected state to match.
  */
  _button_valueDidChange: function() {
    var value = this.get('value'),
        state = this.computeIsSelectedForValue(value);
    this.set('isSelected', state) ; // set new state...
  }.observes('value'),

  /** @private
    Whenever the selected state is changed, make sure the button value is also updated.  Note that this may be called because the value has just changed.  In that case this should do nothing.
  */
  _button_isSelectedDidChange: function() {
    var newState = this.get('isSelected'),
        curState = this.computeIsSelectedForValue(this.get('value'));

    // fix up the value, but only if computed state does not match.
    // never fix up value if isSelected is set to MIXED_STATE since this can
    // only come from the value.
    if ((newState !== SC.MIXED_STATE) && (curState !== newState)) {
      var valueKey = (newState) ? 'toggleOnValue' : 'toggleOffValue' ;
      this.set('value', this.get(valueKey));
    }
  }.observes('isSelected')

} ;
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import './App.css';

class App extends Component {
  render() {
    return (

    );
  }
}

export default App;
<commit_msg>Add BoardContainer to App<commit_after>import React, { Component } from 'react';
import BoardContainer from './BoardContainer';
import './App.css';

class App extends Component {
  render() {
    return (

        <BoardContainer />

    );
  }
}

export default App;
<|endoftext|>


<commit_before>import React from 'react';
import PropTypes from 'prop-types';
import InputMask from 'react-input-mask';
import classnames from 'classnames';
import omit from 'lodash/omit';

import Icon from 'atoms/Icon';
import Text from 'atoms/Text';
import ErrorMessage from 'atoms/ErrorMessage';

import { renderTooltip } from 'utils/fieldUtils';

import styles from './text_field.module.scss';

function TextField( props ) {
  const {
    className,
    htmlFor,
    input,
    label,
    mask,
    maskChar,
    meta,
    noBaseStyle,
    placeholder,
    secure,
    id,
    tooltip,
    inputTooltip,
    type,
    maskVal,
    prefix,
    postfix,
    activeValidation,
    subLabel,
  } = props;

  const classes = [
    !noBaseStyle && styles['text-field'],
    meta && meta.active && !noBaseStyle && styles['focused'],
    meta && meta.touched && meta.error && !meta.active && styles['hasError'],
    className,
  ];

  const inputClasses = [
    styles['input-wrapper'],
    prefix && styles['prefix'],
    postfix && styles['postfix'],
  ];

  let message;

  if (activeValidation) {
    message = meta && (meta.dirty || meta.touched) && (meta.error || meta.warning);
  } else {
    message = meta && meta.touched && !meta.active && (meta.error || meta.warning);
  }

  return (

        { label &&

              { label }
              { secure && <Icon className={styles['icon-lock']} icon='lock' /> }
              { tooltip && renderTooltip(tooltip, styles['tooltip'], styles['tooltip-icon']) }

            <Text size={10} font='b'>
              { subLabel }
            </Text>

        }

          { mask ?
            <InputMask
              className={styles['input']}
              type='text'
              placeholder={placeholder}
              mask={mask}
              maskChar={maskChar}
              id={id}
              {...input}
            />
            :
            <input
              className={styles['input']}
              type={type}
              placeholder={placeholder}
              id={id}
              value={maskVal(input.value)}
              {...omit(input, [ 'value' ])}
            />
          }
          {inputTooltip && renderTooltip(inputTooltip, styles['input-tooltip'], styles['tooltip-icon'])}

      <ErrorMessage
        condition={!!message}
        message={message}
        type={meta && meta.warning ? 'warning' : 'error'}
      />

  );
}

TextField.propTypes = {
  /**
   * set to true to allow validation errors/warnings to occur `onChange`
   */
  activeValidation: PropTypes.bool,

  /**
   * Label is optional. If not provided, component will reorganize accordingly.
   */
  label: PropTypes.oneOfType([
    PropTypes.string,
    PropTypes.object
  ]),
  /**
   * `for` prop on label
   */
  htmlFor: PropTypes.string,

  /**
   * Will append new classname to classSet
   */
  className: PropTypes.string,

  /**
   * placeholder text for text field
   */
  placeholder: PropTypes.string,

  /**
   * object with all necessary props for input
   */
  input: PropTypes.object,
  /**
   * The props under the meta key are metadata about the state of this field that `redux-form` tracks.
   */
  meta: PropTypes.object,
  /**
   * Passing `noBaseStyle=true` will omit the base class style
   * Fields may at times be wrapped up as a collection. In this case, the wrapper would control the styling for those fields as a group.
   */
  noBaseStyle: PropTypes.bool,
  /**
   * Mask the input
   */
  mask: PropTypes.string,

  /**
   * Specific the maskChar (must have `mask` set for this to be applied)
   */
  maskChar: PropTypes.string,

  /**
   * Function to mask the actual input value
   *
   * Provides for more specific customization over the `mask` prop
   */
  maskVal: PropTypes.func,

  /**
   * Adds a lock to the label.
   */
  secure: PropTypes.bool,

  /**
   * id added to the `input` node
   */
  id: PropTypes.string,
  /**
   * either a handler for clicking the tooltip, or text to go in the tooltip for the label
   */
  tooltip: PropTypes.oneOfType([
    PropTypes.func,
    PropTypes.string,
  ]),
  /**
   * either a handler for clicking the tooltip, or text to go in the tooltip for the input
   */
  inputTooltip: PropTypes.oneOfType([
    PropTypes.func,
    PropTypes.string,
  ]),

  /**
   * HTML type for input node; when used with `mask` it always defaults to 'text'
   */
  type: PropTypes.oneOf([
    'text',
    'password',
  ]),

  /**
   * provides a prefix for the input
   */
  prefix: PropTypes.oneOf([
    '$',
  ]),

  /**
   * provides a postfix for the inpit
   */
  postfix: PropTypes.string,

  /**
   * Options text displayed directly underneath label
   */
  subLabel: PropTypes.string,
};

TextField.defaultProps = {
  placeholder: 'Placeholder',
  errorMessage: false,
  type: 'text',
  maskVal: val => val,
};

export default TextField;
<commit_msg>Add swiftype capability to TextFields<commit_after>import React from 'react';
import PropTypes from 'prop-types';
import InputMask from 'react-input-mask';
import classnames from 'classnames';
import omit from 'lodash/omit';

import Icon from 'atoms/Icon';
import Text from 'atoms/Text';
import ErrorMessage from 'atoms/ErrorMessage';

import { renderTooltip } from 'utils/fieldUtils';

import styles from './text_field.module.scss';

function TextField( props ) {
  const {
    className,
    htmlFor,
    input,
    label,
    mask,
    maskChar,
    meta,
    noBaseStyle,
    placeholder,
    secure,
    id,
    tooltip,
    inputTooltip,
    type,
    maskVal,
    prefix,
    postfix,
    activeValidation,
    subLabel,
    swiftype,
  } = props;

  const classes = [
    !noBaseStyle && styles['text-field'],
    meta && meta.active && !noBaseStyle && styles['focused'],
    meta && meta.touched && meta.error && !meta.active && styles['hasError'],
    className,
  ];

  const inputClasses = [
    styles['input-wrapper'],
    prefix && styles['prefix'],
    postfix && styles['postfix'],
  ];

  let message;

  if (activeValidation) {
    message = meta && (meta.dirty || meta.touched) && (meta.error || meta.warning);
  } else {
    message = meta && meta.touched && !meta.active && (meta.error || meta.warning);
  }

  return (

        { label &&

              { label }
              { secure && <Icon className={styles['icon-lock']} icon='lock' /> }
              { tooltip && renderTooltip(tooltip, styles['tooltip'], styles['tooltip-icon']) }

            <Text size={10} font='b'>
              { subLabel }
            </Text>

        }

          { mask ?
            <InputMask
              className={styles['input']}
              type='text'
              placeholder={placeholder}
              mask={mask}
              maskChar={maskChar}
              id={id}
              {...input}
            />
            :
            <input
              className={classnames(
                styles['input'],
                swiftype && 'swiftype-search'
              )}
              type={type}
              placeholder={placeholder}
              id={id}
              value={maskVal(input.value)}
              {...omit(input, [ 'value' ])}
            />
          }
          {inputTooltip && renderTooltip(inputTooltip, styles['input-tooltip'], styles['tooltip-icon'])}

      <ErrorMessage
        condition={!!message}
        message={message}
        type={meta && meta.warning ? 'warning' : 'error'}
      />

  );
}

TextField.propTypes = {
  /**
   * set to true to allow validation errors/warnings to occur `onChange`
   */
  activeValidation: PropTypes.bool,

  /**
   * Label is optional. If not provided, component will reorganize accordingly.
   */
  label: PropTypes.oneOfType([
    PropTypes.string,
    PropTypes.object
  ]),
  /**
   * `for` prop on label
   */
  htmlFor: PropTypes.string,

  /**
   * Will append new classname to classSet
   */
  className: PropTypes.string,

  /**
   * placeholder text for text field
   */
  placeholder: PropTypes.string,

  /**
   * object with all necessary props for input
   */
  input: PropTypes.object,
  /**
   * The props under the meta key are metadata about the state of this field that `redux-form` tracks.
   */
  meta: PropTypes.object,
  /**
   * Passing `noBaseStyle=true` will omit the base class style
   * Fields may at times be wrapped up as a collection. In this case, the wrapper would control the styling for those fields as a group.
   */
  noBaseStyle: PropTypes.bool,
  /**
   * Mask the input
   */
  mask: PropTypes.string,

  /**
   * Specific the maskChar (must have `mask` set for this to be applied)
   */
  maskChar: PropTypes.string,

  /**
   * Function to mask the actual input value
   *
   * Provides for more specific customization over the `mask` prop
   */
  maskVal: PropTypes.func,

  /**
   * Adds a lock to the label.
   */
  secure: PropTypes.bool,

  /**
   * id added to the `input` node
   */
  id: PropTypes.string,
  /**
   * either a handler for clicking the tooltip, or text to go in the tooltip for the label
   */
  tooltip: PropTypes.oneOfType([
    PropTypes.func,
    PropTypes.string,
  ]),
  /**
   * either a handler for clicking the tooltip, or text to go in the tooltip for the input
   */
  inputTooltip: PropTypes.oneOfType([
    PropTypes.func,
    PropTypes.string,
  ]),

  /**
   * HTML type for input node; when used with `mask` it always defaults to 'text'
   */
  type: PropTypes.oneOf([
    'text',
    'password',
  ]),

  /**
   * provides a prefix for the input
   */
  prefix: PropTypes.oneOf([
    '$',
  ]),

  /**
   * provides a postfix for the inpit
   */
  postfix: PropTypes.string,

  /**
   * Options text displayed directly underneath label
   */
  subLabel: PropTypes.string,
  /**
   * Enables swiftype search
   */
  swiftype: PropTypes.bool,
};

TextField.defaultProps = {
  placeholder: 'Placeholder',
  errorMessage: false,
  type: 'text',
  maskVal: val => val,
};

export default TextField;
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { StyleSheet, View, TouchableOpacity } from 'react-native';
import LinearGradient from 'react-native-linear-gradient';
import Button from './common/Button';

const propTypes = {
  ...Button.propTypes,
  width: PropTypes.number,
  height: PropTypes.number,
  type: PropTypes.string,
  shape: PropTypes.string,
  gradientStart: PropTypes.object,
  gradientEnd: PropTypes.object,
  disabledGradientStart: PropTypes.object,
  disabledGradientEnd: PropTypes.object,
  disabledOpacity: PropTypes.number,
  disabled: PropTypes.bool,
  backgroundColors: PropTypes.array,
  buttonStyle: PropTypes.any,
  style: PropTypes.any,
  progressSize: PropTypes.number,
  onPress: PropTypes.func,

};

const defaultProps = {
  type: 'primary',
  shape: 'round',
  backgroundColors: ['#4DC7A4', '#66D37A'],
  gradientStart: { x: 0.5, y: 1 },
  gradientEnd: { x: 1, y: 1 },
  disabledGradientStart: { x: 0, y: 0 },
  disabledGradientEnd: { x: 0, y: 0 },
  disabledOpacity: 0.5,
  disabled: false,
  width: null,
  height: 50,
};

class ButtonComponent extends Component {
  static propTypes = propTypes;
  static defaultProps = defaultProps;

  constructor(props) {
    super(props);

    this.state = {
      currentButton: this.getCurrentButton(props),
    };
  }

  componentWillReceiveProps(nextProps) {
    if (this.props.buttonState !== nextProps.buttonState) {
      this.setState({
        currentButton: this.getCurrentButton(nextProps),
      });
    }
  }

  shouldComponentUpdate(nextProps) {
    if (this.props.image !== nextProps.image) return true;
    if (this.props.text !== nextProps.text) return true;
    if (this.props.disabled !== nextProps.disabled) return true;
    if (this.props.states && this.props.buttonState !== nextProps.buttonState) return true;
    if (this.props.states && this.props.states[this.props.buttonState].progressFill) return true;
    if (
      this.props.states &&
      this.props.states[this.props.buttonState].disabled !== nextProps.states[nextProps.buttonState].disabled
    ) return true;
    return false;
  }

  getCurrentButton(props) {
    return (props.buttonState && props.states)
      ? props.states[props.buttonState]
      : props;
  }

  getProp(propKey) {
    const { currentButton } = this.state;
    return currentButton[propKey] || this.props[propKey];
  }

  renderButton({ textStyle = styles.text, imageStyle = styles.image }) {
    let button;

    if (this.props.states) {
      const addedAnimtionConfigButtonStates = configButtonStatesAnimation(
        this.props.states,
        this.props.height,
      );
      button = (
        <Button
          states={addedAnimtionConfigButtonStates}
          buttonState={this.props.buttonState}
          textStyle={this.props.textStyle || textStyle}
          imageStyle={this.props.imageStyle || imageStyle}
          imageAnimConfig={this.props.imageAnimConfig}
          textAnimConfig={this.props.textAnimConfig}
          spinnerAnimConfig={this.props.spinnerAnimConfig}
          spinner={this.props.spinner}
          spinnerSize={this.props.spinnerSize}
          spinnerType={this.props.spinnerType}
          spinnerStyle={this.props.spinnerStyle}
          spinnerColor={this.props.spinnerColor}
          progressAnimConfig={this.props.progressAnimConfig}
          progressSize={this.props.progressSize}
          progressWidth={this.props.progressWidth}
          progressTintColor={this.props.progressTintColor}
          progressBackgroundColor={this.props.progressBackgroundColor}
          progressStyle={this.props.progressStyle}
          textInsideProgress={this.props.textInsideProgress}
        />
      );
    } else {
      button = (
        <Button
          textStyle={this.props.textStyle || textStyle}
          imageStyle={this.props.imageStyle || imageStyle}
          text={this.props.text}
          image={this.props.image}
        />
      );
    }

    return button;
  }

  render() {
    const style = this.getProp('style');
    const gradientStart = this.getProp('gradientStart');
    const gradientEnd = this.getProp('gradientEnd');
    const disabledGradientStart = this.getProp('disabledGradientStart');
    const disabledGradientEnd = this.getProp('disabledGradientEnd');
    const disabledOpacity = this.getProp('disabledOpacity');
    const disabled = this.getProp('disabled');
    const backgroundColors = this.getProp('backgroundColors');
    const type = this.getProp('type');
    const buttonHeight = this.getProp('height');
    const buttonWidth = this.getProp('width');
    const buttonStyle = this.getProp('buttonStyle');
    const shape = this.getProp('shape');
    const onPress = this.getProp('onPress');
    const disabledStyle = disabled
      ? { opacity: disabledOpacity }
      : null;

    let shapeStyle;
    if (['round', 'circle'].includes(shape)) {
      shapeStyle = {
        borderRadius: buttonHeight / 2,
      };
    }

    let content;
    if (type === 'primary') {
      content = (
        <LinearGradient
          start={disabled ? disabledGradientStart : gradientStart}
          end={disabled ? disabledGradientEnd : gradientEnd}
          colors={backgroundColors}
          collapsable={false}
          style={[styles.button, shapeStyle, buttonStyle, disabledStyle]}
        >
          {this.renderButton({ textStyle: styles.text })}
        </LinearGradient>
      );
    } else {
      const border = type === 'border' && styles.border;
      content = (
        <View style={[styles.button, border, shapeStyle, buttonStyle, disabledStyle]}>
          {this.renderButton({ textStyle: styles.secondaryText })}
        </View>
      );
    }

    return (
      <TouchableOpacity
        accessibilityTraits="button"
        disabled={disabled}
        onPress={onPress}
        activeOpacity={0.9}
        style={[
          styles.container,
          {
            width: buttonWidth,
            height: buttonHeight,
          },
          style,
        ]}
      >
        {content}
      </TouchableOpacity>
    );
  }
}

function configButtonStatesAnimation(buttonsStates, height) {
  const addedAnimtionConfigButtonStates = {};
  const stateNames = Object.keys(buttonsStates);
  for (let i = 0; i < stateNames.length; i++) {
    const stateName = stateNames[i];
    const buttonState = buttonsStates[stateName];
    const textAnimConfig = {};
    const imageAnimConfig = {};
    const progressAnimConfig = {};

    // config the text animations
    {
      const opacity = { inputRange: [], outputRange: [] };
      const translateY = { inputRange: [], outputRange: [] };
      if (i === 0) {
        opacity.inputRange.push(0, 1);
        opacity.outputRange.push(1, 0);
        translateY.inputRange.push(0, 1);
        translateY.outputRange.push(0, height - 10);
      } else {
        opacity.inputRange.push(0, 1);
        opacity.outputRange.push(0, 1);
        translateY.inputRange.push(0, 1);
        translateY.outputRange.push((0 - height * i), (0 - height * (i - 1)));
      }
      textAnimConfig.opacity = opacity;
      textAnimConfig.transform = [{ translateY }];
    }

    // config the image animations
    {
      const opacity = { inputRange: [], outputRange: [] };
      const translateY = { inputRange: [], outputRange: [] };
      if (i === 0) {
        opacity.inputRange.push(0, 1);
        opacity.outputRange.push(1, 0);
        translateY.inputRange.push(0, 1);
        translateY.outputRange.push(0, (height - 10) * 2);
      } else {
        opacity.inputRange.push(0, 1);
        opacity.outputRange.push(0, 1);
        translateY.inputRange.push(0, 1);
        translateY.outputRange.push((0 - (height * 2) * i), (0 - (height * 2) * (i - 1)));
      }
      imageAnimConfig.opacity = opacity;
      imageAnimConfig.transform = [{ translateY }];
    }

    // config the progress animations
    {
      const opacity = { inputRange: [], outputRange: [] };
      const translateY = { inputRange: [], outputRange: [] };
      opacity.inputRange.push(0, 1);
      opacity.outputRange.push(0, 1);
      translateY.inputRange.push(0, 0);
      translateY.outputRange.push((0 - (height * 2) * i), (0 - (height * 2) * (i - 1)));
      progressAnimConfig.opacity = opacity;
      progressAnimConfig.transform = [{ translateY }];
    }

    addedAnimtionConfigButtonStates[stateName] = {
      textAnimConfig,
      imageAnimConfig,
      progressAnimConfig,
      ...buttonState,
    };
  }

  return addedAnimtionConfigButtonStates;
}

const styles = StyleSheet.create({
  container: {
    flexDirection: 'row',
    overflow: 'hidden',
  },
  button: {
    flex: 1,
    backgroundColor: 'transparent',
    // paddingHorizontal: 40,
  },
  border: {
    borderWidth: 1,
    borderColor: '#7F91A7',
  },
  content: {
    position: 'absolute',
    left: 0,
    top: 0,
    right: 0,
    bottom: 0,
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'center',
  },
  image: {
    marginRight: 12,
  },
  text: {
    letterSpacing: 1,
    fontSize: 12,
    color: 'white',
  },
  secondaryText: {
    letterSpacing: 1,
    fontSize: 12,
    color: '#7F91A7',
  },
});

export default ButtonComponent;
<commit_msg>View.propTypes fix<commit_after>import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { StyleSheet, View, TouchableOpacity, ViewPropTypes } from 'react-native';
import LinearGradient from 'react-native-linear-gradient';
import Button from './common/Button';

const RNViewPropTypes = ViewPropTypes || View.propTypes;

const propTypes = {
  ...Button.propTypes,
  width: PropTypes.number,
  height: PropTypes.number,
  type: PropTypes.string,
  shape: PropTypes.string,
  gradientStart: PropTypes.object,
  gradientEnd: PropTypes.object,
  disabledGradientStart: PropTypes.object,
  disabledGradientEnd: PropTypes.object,
  disabledOpacity: PropTypes.number,
  disabled: PropTypes.bool,
  backgroundColors: PropTypes.array,
  buttonStyle: PropTypes.any,
  style: RNViewPropTypes.style,
  progressSize: PropTypes.number,
  onPress: PropTypes.func,

};

const defaultProps = {
  type: 'primary',
  shape: 'round',
  backgroundColors: ['#4DC7A4', '#66D37A'],
  gradientStart: { x: 0.5, y: 1 },
  gradientEnd: { x: 1, y: 1 },
  disabledGradientStart: { x: 0, y: 0 },
  disabledGradientEnd: { x: 0, y: 0 },
  disabledOpacity: 0.5,
  disabled: false,
  width: null,
  height: 50,
};

class ButtonComponent extends Component {
  static propTypes = propTypes;
  static defaultProps = defaultProps;

  constructor(props) {
    super(props);

    this.state = {
      currentButton: this.getCurrentButton(props),
    };
  }

  componentWillReceiveProps(nextProps) {
    if (this.props.buttonState !== nextProps.buttonState) {
      this.setState({
        currentButton: this.getCurrentButton(nextProps),
      });
    }
  }

  shouldComponentUpdate(nextProps) {
    if (this.props.image !== nextProps.image) return true;
    if (this.props.text !== nextProps.text) return true;
    if (this.props.disabled !== nextProps.disabled) return true;
    if (this.props.states && this.props.buttonState !== nextProps.buttonState) return true;
    if (this.props.states && this.props.states[this.props.buttonState].progressFill) return true;
    if (
      this.props.states &&
      this.props.states[this.props.buttonState].disabled !== nextProps.states[nextProps.buttonState].disabled
    ) return true;
    return false;
  }

  getCurrentButton(props) {
    return (props.buttonState && props.states)
      ? props.states[props.buttonState]
      : props;
  }

  getProp(propKey) {
    const { currentButton } = this.state;
    return currentButton[propKey] || this.props[propKey];
  }

  renderButton({ textStyle = styles.text, imageStyle = styles.image }) {
    let button;

    if (this.props.states) {
      const addedAnimtionConfigButtonStates = configButtonStatesAnimation(
        this.props.states,
        this.props.height,
      );
      button = (
        <Button
          states={addedAnimtionConfigButtonStates}
          buttonState={this.props.buttonState}
          textStyle={this.props.textStyle || textStyle}
          imageStyle={this.props.imageStyle || imageStyle}
          imageAnimConfig={this.props.imageAnimConfig}
          textAnimConfig={this.props.textAnimConfig}
          spinnerAnimConfig={this.props.spinnerAnimConfig}
          spinner={this.props.spinner}
          spinnerSize={this.props.spinnerSize}
          spinnerType={this.props.spinnerType}
          spinnerStyle={this.props.spinnerStyle}
          spinnerColor={this.props.spinnerColor}
          progressAnimConfig={this.props.progressAnimConfig}
          progressSize={this.props.progressSize}
          progressWidth={this.props.progressWidth}
          progressTintColor={this.props.progressTintColor}
          progressBackgroundColor={this.props.progressBackgroundColor}
          progressStyle={this.props.progressStyle}
          textInsideProgress={this.props.textInsideProgress}
        />
      );
    } else {
      button = (
        <Button
          textStyle={this.props.textStyle || textStyle}
          imageStyle={this.props.imageStyle || imageStyle}
          text={this.props.text}
          image={this.props.image}
        />
      );
    }

    return button;
  }

  render() {
    const style = this.getProp('style');
    const gradientStart = this.getProp('gradientStart');
    const gradientEnd = this.getProp('gradientEnd');
    const disabledGradientStart = this.getProp('disabledGradientStart');
    const disabledGradientEnd = this.getProp('disabledGradientEnd');
    const disabledOpacity = this.getProp('disabledOpacity');
    const disabled = this.getProp('disabled');
    const backgroundColors = this.getProp('backgroundColors');
    const type = this.getProp('type');
    const buttonHeight = this.getProp('height');
    const buttonWidth = this.getProp('width');
    const buttonStyle = this.getProp('buttonStyle');
    const shape = this.getProp('shape');
    const onPress = this.getProp('onPress');
    const disabledStyle = disabled
      ? { opacity: disabledOpacity }
      : null;

    let shapeStyle;
    if (['round', 'circle'].includes(shape)) {
      shapeStyle = {
        borderRadius: buttonHeight / 2,
      };
    }

    let content;
    if (type === 'primary') {
      content = (
        <LinearGradient
          start={disabled ? disabledGradientStart : gradientStart}
          end={disabled ? disabledGradientEnd : gradientEnd}
          colors={backgroundColors}
          collapsable={false}
          style={[styles.button, shapeStyle, buttonStyle, disabledStyle]}
        >
          {this.renderButton({ textStyle: styles.text })}
        </LinearGradient>
      );
    } else {
      const border = type === 'border' && styles.border;
      content = (
        <View style={[styles.button, border, shapeStyle, buttonStyle, disabledStyle]}>
          {this.renderButton({ textStyle: styles.secondaryText })}
        </View>
      );
    }

    return (
      <TouchableOpacity
        accessibilityTraits="button"
        disabled={disabled}
        onPress={onPress}
        activeOpacity={0.9}
        style={[
          styles.container,
          {
            width: buttonWidth,
            height: buttonHeight,
          },
          style,
        ]}
      >
        {content}
      </TouchableOpacity>
    );
  }
}

function configButtonStatesAnimation(buttonsStates, height) {
  const addedAnimtionConfigButtonStates = {};
  const stateNames = Object.keys(buttonsStates);
  for (let i = 0; i < stateNames.length; i++) {
    const stateName = stateNames[i];
    const buttonState = buttonsStates[stateName];
    const textAnimConfig = {};
    const imageAnimConfig = {};
    const progressAnimConfig = {};

    // config the text animations
    {
      const opacity = { inputRange: [], outputRange: [] };
      const translateY = { inputRange: [], outputRange: [] };
      if (i === 0) {
        opacity.inputRange.push(0, 1);
        opacity.outputRange.push(1, 0);
        translateY.inputRange.push(0, 1);
        translateY.outputRange.push(0, height - 10);
      } else {
        opacity.inputRange.push(0, 1);
        opacity.outputRange.push(0, 1);
        translateY.inputRange.push(0, 1);
        translateY.outputRange.push((0 - height * i), (0 - height * (i - 1)));
      }
      textAnimConfig.opacity = opacity;
      textAnimConfig.transform = [{ translateY }];
    }

    // config the image animations
    {
      const opacity = { inputRange: [], outputRange: [] };
      const translateY = { inputRange: [], outputRange: [] };
      if (i === 0) {
        opacity.inputRange.push(0, 1);
        opacity.outputRange.push(1, 0);
        translateY.inputRange.push(0, 1);
        translateY.outputRange.push(0, (height - 10) * 2);
      } else {
        opacity.inputRange.push(0, 1);
        opacity.outputRange.push(0, 1);
        translateY.inputRange.push(0, 1);
        translateY.outputRange.push((0 - (height * 2) * i), (0 - (height * 2) * (i - 1)));
      }
      imageAnimConfig.opacity = opacity;
      imageAnimConfig.transform = [{ translateY }];
    }

    // config the progress animations
    {
      const opacity = { inputRange: [], outputRange: [] };
      const translateY = { inputRange: [], outputRange: [] };
      opacity.inputRange.push(0, 1);
      opacity.outputRange.push(0, 1);
      translateY.inputRange.push(0, 0);
      translateY.outputRange.push((0 - (height * 2) * i), (0 - (height * 2) * (i - 1)));
      progressAnimConfig.opacity = opacity;
      progressAnimConfig.transform = [{ translateY }];
    }

    addedAnimtionConfigButtonStates[stateName] = {
      textAnimConfig,
      imageAnimConfig,
      progressAnimConfig,
      ...buttonState,
    };
  }

  return addedAnimtionConfigButtonStates;
}

const styles = StyleSheet.create({
  container: {
    flexDirection: 'row',
    overflow: 'hidden',
  },
  button: {
    flex: 1,
    backgroundColor: 'transparent',
    // paddingHorizontal: 40,
  },
  border: {
    borderWidth: 1,
    borderColor: '#7F91A7',
  },
  content: {
    position: 'absolute',
    left: 0,
    top: 0,
    right: 0,
    bottom: 0,
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'center',
  },
  image: {
    marginRight: 12,
  },
  text: {
    letterSpacing: 1,
    fontSize: 12,
    color: 'white',
  },
  secondaryText: {
    letterSpacing: 1,
    fontSize: 12,
    color: '#7F91A7',
  },
});

export default ButtonComponent;
<|endoftext|>